diff options
Diffstat (limited to 'drivers/gpu/drm')
84 files changed, 8342 insertions, 18705 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index aa76700..e8acc19 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -381,6 +381,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, if (ret) goto out; + crtc->base.properties = &crtc->properties; + list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++; @@ -480,6 +482,7 @@ int drm_connector_init(struct drm_device *dev, if (ret) goto out; + connector->base.properties = &connector->properties; connector->dev = dev; connector->funcs = funcs; connector->connector_type = connector_type; @@ -602,6 +605,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, if (ret) goto out; + plane->base.properties = &plane->properties; plane->dev = dev; plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, @@ -1421,11 +1425,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } connector = obj_to_connector(obj); - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] != 0) { - props_count++; - } - } + props_count = connector->properties.count; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] != 0) { @@ -1478,21 +1478,19 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, copied = 0; prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] != 0) { - if (put_user(connector->property_ids[i], - prop_ptr + copied)) { - ret = -EFAULT; - goto out; - } + for (i = 0; i < connector->properties.count; i++) { + if (put_user(connector->properties.ids[i], + prop_ptr + copied)) { + ret = -EFAULT; + goto out; + } - if (put_user(connector->property_values[i], - prop_values + copied)) { - ret = -EFAULT; - goto out; - } - copied++; + if (put_user(connector->properties.values[i], + prop_values + copied)) { + ret = -EFAULT; + goto out; } + copied++; } } out_resp->count_props = props_count; @@ -2712,6 +2710,34 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, } EXPORT_SYMBOL(drm_property_create_enum); +struct drm_property *drm_property_create_bitmask(struct drm_device *dev, + int flags, const char *name, + const struct drm_prop_enum_list *props, + int num_values) +{ + struct drm_property *property; + int i, ret; + + flags |= DRM_MODE_PROP_BITMASK; + + property = drm_property_create(dev, flags, name, num_values); + if (!property) + return NULL; + + for (i = 0; i < num_values; i++) { + ret = drm_property_add_enum(property, i, + props[i].type, + props[i].name); + if (ret) { + drm_property_destroy(dev, property); + return NULL; + } + } + + return property; +} +EXPORT_SYMBOL(drm_property_create_bitmask); + struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max) @@ -2736,7 +2762,14 @@ int drm_property_add_enum(struct drm_property *property, int index, { struct drm_property_enum *prop_enum; - if (!(property->flags & DRM_MODE_PROP_ENUM)) + if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) + return -EINVAL; + + /* + * Bitmask enum properties have the additional constraint of values + * from 0 to 63 + */ + if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) return -EINVAL; if (!list_empty(&property->enum_blob_list)) { @@ -2780,60 +2813,78 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); -int drm_connector_attach_property(struct drm_connector *connector, +void drm_connector_attach_property(struct drm_connector *connector, struct drm_property *property, uint64_t init_val) { - int i; - - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == 0) { - connector->property_ids[i] = property->base.id; - connector->property_values[i] = init_val; - break; - } - } - - if (i == DRM_CONNECTOR_MAX_PROPERTY) - return -EINVAL; - return 0; + drm_object_attach_property(&connector->base, property, init_val); } EXPORT_SYMBOL(drm_connector_attach_property); int drm_connector_property_set_value(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + return drm_object_property_set_value(&connector->base, property, value); +} +EXPORT_SYMBOL(drm_connector_property_set_value); + +int drm_connector_property_get_value(struct drm_connector *connector, + struct drm_property *property, uint64_t *val) +{ + return drm_object_property_get_value(&connector->base, property, val); +} +EXPORT_SYMBOL(drm_connector_property_get_value); + +void drm_object_attach_property(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t init_val) +{ + int count = obj->properties->count; + + if (count == DRM_OBJECT_MAX_PROPERTY) { + WARN(1, "Failed to attach object property (type: 0x%x). Please " + "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " + "you see this message on the same object type.\n", + obj->type); + return; + } + + obj->properties->ids[count] = property->base.id; + obj->properties->values[count] = init_val; + obj->properties->count++; +} +EXPORT_SYMBOL(drm_object_attach_property); + +int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_property *property, uint64_t val) +{ int i; - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == property->base.id) { - connector->property_values[i] = value; - break; + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->ids[i] == property->base.id) { + obj->properties->values[i] = val; + return 0; } } - if (i == DRM_CONNECTOR_MAX_PROPERTY) - return -EINVAL; - return 0; + return -EINVAL; } -EXPORT_SYMBOL(drm_connector_property_set_value); +EXPORT_SYMBOL(drm_object_property_set_value); -int drm_connector_property_get_value(struct drm_connector *connector, +int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val) { int i; - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == property->base.id) { - *val = connector->property_values[i]; - break; + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->ids[i] == property->base.id) { + *val = obj->properties->values[i]; + return 0; } } - if (i == DRM_CONNECTOR_MAX_PROPERTY) - return -EINVAL; - return 0; + return -EINVAL; } -EXPORT_SYMBOL(drm_connector_property_get_value); +EXPORT_SYMBOL(drm_object_property_get_value); int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -2864,7 +2915,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } property = obj_to_property(obj); - if (property->flags & DRM_MODE_PROP_ENUM) { + if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++; } else if (property->flags & DRM_MODE_PROP_BLOB) { @@ -2889,7 +2940,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } out_resp->count_values = value_count; - if (property->flags & DRM_MODE_PROP_ENUM) { + if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; @@ -3035,75 +3086,202 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_update_edid_property); +static bool drm_property_change_is_valid(struct drm_property *property, + __u64 value) +{ + if (property->flags & DRM_MODE_PROP_IMMUTABLE) + return false; + if (property->flags & DRM_MODE_PROP_RANGE) { + if (value < property->values[0] || value > property->values[1]) + return false; + return true; + } else if (property->flags & DRM_MODE_PROP_BITMASK) { + int i; + __u64 valid_mask = 0; + for (i = 0; i < property->num_values; i++) + valid_mask |= (1ULL << property->values[i]); + return !(value & ~valid_mask); + } else { + int i; + for (i = 0; i < property->num_values; i++) + if (property->values[i] == value) + return true; + return false; + } +} + int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_connector_set_property *out_resp = data; - struct drm_mode_object *obj; - struct drm_property *property; - struct drm_connector *connector; + struct drm_mode_connector_set_property *conn_set_prop = data; + struct drm_mode_obj_set_property obj_set_prop = { + .value = conn_set_prop->value, + .prop_id = conn_set_prop->prop_id, + .obj_id = conn_set_prop->connector_id, + .obj_type = DRM_MODE_OBJECT_CONNECTOR + }; + + /* It does all the locking and checking we need */ + return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); +} + +static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ + int ret = -EINVAL; + struct drm_connector *connector = obj_to_connector(obj); + + /* Do DPMS ourselves */ + if (property == connector->dev->mode_config.dpms_property) { + if (connector->funcs->dpms) + (*connector->funcs->dpms)(connector, (int)value); + ret = 0; + } else if (connector->funcs->set_property) + ret = connector->funcs->set_property(connector, property, value); + + /* store the property value if successful */ + if (!ret) + drm_connector_property_set_value(connector, property, value); + return ret; +} + +static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ + int ret = -EINVAL; + struct drm_crtc *crtc = obj_to_crtc(obj); + + if (crtc->funcs->set_property) + ret = crtc->funcs->set_property(crtc, property, value); + if (!ret) + drm_object_property_set_value(obj, property, value); + + return ret; +} + +static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ int ret = -EINVAL; + struct drm_plane *plane = obj_to_plane(obj); + + if (plane->funcs->set_property) + ret = plane->funcs->set_property(plane, property, value); + if (!ret) + drm_object_property_set_value(obj, property, value); + + return ret; +} + +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_get_properties *arg = data; + struct drm_mode_object *obj; + int ret = 0; int i; + int copied = 0; + int props_count = 0; + uint32_t __user *props_ptr; + uint64_t __user *prop_values_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; mutex_lock(&dev->mode_config.mutex); - obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); + obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { + ret = -EINVAL; + goto out; + } + if (!obj->properties) { + ret = -EINVAL; goto out; } - connector = obj_to_connector(obj); - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == out_resp->prop_id) - break; + props_count = obj->properties->count; + + /* This ioctl is called twice, once to determine how much space is + * needed, and the 2nd time to fill it. */ + if ((arg->count_props >= props_count) && props_count) { + copied = 0; + props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); + prop_values_ptr = (uint64_t __user *)(unsigned long) + (arg->prop_values_ptr); + for (i = 0; i < props_count; i++) { + if (put_user(obj->properties->ids[i], + props_ptr + copied)) { + ret = -EFAULT; + goto out; + } + if (put_user(obj->properties->values[i], + prop_values_ptr + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } } + arg->count_props = props_count; +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_set_property *arg = data; + struct drm_mode_object *arg_obj; + struct drm_mode_object *prop_obj; + struct drm_property *property; + int ret = -EINVAL; + int i; - if (i == DRM_CONNECTOR_MAX_PROPERTY) { + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + mutex_lock(&dev->mode_config.mutex); + + arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + if (!arg_obj) + goto out; + if (!arg_obj->properties) goto out; - } - obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); - if (!obj) { + for (i = 0; i < arg_obj->properties->count; i++) + if (arg_obj->properties->ids[i] == arg->prop_id) + break; + + if (i == arg_obj->properties->count) goto out; - } - property = obj_to_property(obj); - if (property->flags & DRM_MODE_PROP_IMMUTABLE) + prop_obj = drm_mode_object_find(dev, arg->prop_id, + DRM_MODE_OBJECT_PROPERTY); + if (!prop_obj) goto out; + property = obj_to_property(prop_obj); - if (property->flags & DRM_MODE_PROP_RANGE) { - if (out_resp->value < property->values[0]) - goto out; + if (!drm_property_change_is_valid(property, arg->value)) + goto out; - if (out_resp->value > property->values[1]) - goto out; - } else { - int found = 0; - for (i = 0; i < property->num_values; i++) { - if (property->values[i] == out_resp->value) { - found = 1; - break; - } - } - if (!found) { - goto out; - } + switch (arg_obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: + ret = drm_mode_connector_set_obj_prop(arg_obj, property, + arg->value); + break; + case DRM_MODE_OBJECT_CRTC: + ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); + break; + case DRM_MODE_OBJECT_PLANE: + ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); + break; } - /* Do DPMS ourselves */ - if (property == connector->dev->mode_config.dpms_property) { - if (connector->funcs->dpms) - (*connector->funcs->dpms)(connector, (int) out_resp->value); - ret = 0; - } else if (connector->funcs->set_property) - ret = connector->funcs->set_property(connector, property, out_resp->value); - - /* store the property value if successful */ - if (!ret) - drm_connector_property_set_value(connector, property, out_resp->value); out: mutex_unlock(&dev->mode_config.mutex); return ret; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 2083180..4ac7199 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -162,7 +162,9 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED) + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 3d002cc..b4ae4c5 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -15,6 +15,12 @@ config DRM_EXYNOS_MEMSIZE depends on DRM_EXYNOS default "32768" +config DRM_EXYNOS_IOMMU + bool "GEM IOMMU Support" + depends on DRM_EXYNOS + help + Choose this option if you want to use GEM IOMMU feature for DRM. + config DRM_EXYNOS_DMABUF bool "GEM DMABUF Support" depends on DRM_EXYNOS @@ -54,8 +60,29 @@ config DRM_EXYNOS_G2D help Choose this option if you want to use Samsung G2D for DRM. +config DRM_EXYNOS_IPP + bool "Samsung DRM IPP" + depends on DRM_EXYNOS + help + This option enables IPP drivers for FIMC, GSC, Rotator. + It can support user space ioctl for each devices. + ipp operations support rotating and scaling, writeback, flip, output + when enable this options. + config DRM_EXYNOS_ROTATOR bool "Samsung DRM Rotator" - depends on DRM_EXYNOS + depends on DRM_EXYNOS_IPP help Choose this option if you want to use Samsung Rotator for DRM. + +config DRM_EXYNOS_FIMC + bool "Samsung DRM FIMC" + depends on DRM_EXYNOS_IPP + help + Choose this option if you want to use Samsung FIMC for DRM. + +config DRM_EXYNOS_GSC + bool "Samsung DRM GSC" + depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 + help + Choose this option if you want to use Samsung GSC for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index f8a88f3..369b3a5 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -7,14 +7,18 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos \ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \ exynos_drm_buf.o exynos_drm_ump.o exynos_drm_gem.o \ - exynos_drm_core.o exynos_drm_plane.o exynos_drm_iommu.o + exynos_drm_core.o exynos_drm_plane.o +exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \ exynos_hdmiphy.o exynos_drm_hdmi.o exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o +exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o +exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index f7bb7be..a0b27d9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -47,7 +47,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, return -EINVAL; } - if (buf->dma_addr) { + if (buf->paddr) { DRM_DEBUG_KMS("already allocated.\n"); return 0; } @@ -78,21 +78,21 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, } #ifdef CONFIG_CMA - buf->dma_addr = cma_alloc(dev->dev, "drm", buf->size, + buf->paddr = cma_alloc(dev->dev, "drm", buf->size, buf->page_size); - if (IS_ERR((void *)buf->dma_addr)) { + if (IS_ERR((void *)buf->paddr)) { DRM_DEBUG_KMS("cma_alloc of size %ld failed\n", buf->size); ret = -ENOMEM; goto err1; } - buf->kvaddr = phys_to_virt(buf->dma_addr); + buf->kvaddr = phys_to_virt(buf->paddr); #else /* align it as page size(page or section) TODO */ buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size, - &buf->dma_addr, GFP_KERNEL); + &buf->paddr, GFP_KERNEL); if (!buf->kvaddr) { DRM_ERROR("failed to allocate buffer.\n"); ret = -ENOMEM; @@ -107,7 +107,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, } sgl = buf->sgt->sgl; - start_addr = buf->dma_addr; + start_addr = buf->paddr; while (i < npages) { buf->pages[i] = phys_to_page(start_addr); @@ -118,20 +118,20 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, i++; } - DRM_INFO("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", + DRM_INFO("vaddr(0x%lx), paddr(0x%lx), size(0x%lx)\n", (unsigned long)buf->kvaddr, - (unsigned long)buf->dma_addr, + (unsigned long)buf->paddr, buf->size); return ret; err2: #ifdef CONFIG_CMA - cma_free(buf->dma_addr); + cma_free(buf->paddr); #else dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, - (dma_addr_t)buf->dma_addr); + (dma_addr_t)buf->paddr); #endif - buf->dma_addr = (dma_addr_t)NULL; + buf->paddr = (dma_addr_t)NULL; err1: sg_free_table(buf->sgt); kfree(buf->sgt); @@ -146,13 +146,6 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, DRM_DEBUG_KMS("%s.\n", __FILE__); /* - * now buffer is being shared and it would be released - * by original owner so ignor free action. - */ - if (buf->shared || atomic_read(&buf->shared_refcount)) - return; - - /* * release only physically continuous memory and * non-continuous memory would be released by exynos * gem framework. @@ -162,25 +155,36 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, return; } - if (!buf->dma_addr) { - DRM_DEBUG_KMS("dma_addr is invalid.\n"); + if (!buf->paddr) { + DRM_DEBUG_KMS("paddr is invalid.\n"); return; } - sg_free_table(buf->sgt); - - kfree(buf->sgt); - buf->sgt = NULL; + if (buf->sgt) { + sg_free_table(buf->sgt); + kfree(buf->sgt); + buf->sgt = NULL; + } kfree(buf->pages); buf->pages = NULL; + + /* + * now buffer is being shared and it would be released + * by original owner so ignor free action. + * this buffer was imported from physical memory to gem directly + * and this feature is used temporarily so removed later. + */ + if (buf->shared) + return; + #ifdef CONFIG_CMA - cma_free(buf->dma_addr); + cma_free(buf->paddr); #else dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, - (dma_addr_t)buf->dma_addr); + (dma_addr_t)buf->paddr); #endif - buf->dma_addr = (dma_addr_t)NULL; + buf->paddr = (dma_addr_t)NULL; } struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, @@ -221,7 +225,7 @@ int exynos_drm_alloc_buf(struct drm_device *dev, /* * allocate memory region and set the memory information - * to vaddr and dma_addr of a buffer object. + * to vaddr and paddr of a buffer object. */ if (lowlevel_buffer_allocate(dev, flags, buf) < 0) return -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index eaf630d..80cba2f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -33,7 +33,6 @@ #include "exynos_drm_fbdev.h" static LIST_HEAD(exynos_drm_subdrv_list); -static struct drm_device *drm_dev; static int exynos_drm_subdrv_probe(struct drm_device *dev, struct exynos_drm_subdrv *subdrv) @@ -95,7 +94,7 @@ static void exynos_drm_subdrv_remove(struct drm_device *dev, DRM_DEBUG_DRIVER("%s\n", __FILE__); if (subdrv->remove) - subdrv->remove(dev); + subdrv->remove(dev, subdrv->dev); if (subdrv->encoder) { struct drm_encoder *encoder = subdrv->encoder; @@ -120,8 +119,6 @@ int exynos_drm_device_register(struct drm_device *dev) if (!dev) return -EINVAL; - drm_dev = dev; - list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { subdrv->drm_dev = dev; err = exynos_drm_subdrv_probe(dev, subdrv); @@ -149,8 +146,6 @@ int exynos_drm_device_unregister(struct drm_device *dev) list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) exynos_drm_subdrv_remove(dev, subdrv); - drm_dev = NULL; - return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 4afb625..5904c58 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -29,21 +29,23 @@ #include "drmP.h" #include "drm_crtc_helper.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" -#include "exynos_drm_fb.h" #include "exynos_drm_encoder.h" -#include "exynos_drm_gem.h" +#include "exynos_drm_plane.h" #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ drm_crtc) +enum exynos_crtc_mode { + CRTC_MODE_NORMAL, /* normal mode */ + CRTC_MODE_BLANK, /* The private plane of crtc is blank */ +}; + /* * Exynos specific crtc structure. * * @drm_crtc: crtc object. - * @overlay: contain information common to display controller and hdmi and - * contents of this overlay object would be copied to sub driver size. + * @drm_plane: pointer of private plane object for this crtc * @pipe: a crtc index created at load() with a new crtc object creation * and the crtc object would be set to private->crtc array * to get a crtc object corresponding to this pipe from private->crtc @@ -52,115 +54,16 @@ * we can refer to the crtc to current hardware interrupt occured through * this pipe value. * @dpms: store the crtc dpms value + * @mode: store the crtc mode value */ struct exynos_drm_crtc { struct drm_crtc drm_crtc; - struct exynos_drm_overlay overlay; + struct drm_plane *plane; unsigned int pipe; unsigned int dpms; + enum exynos_crtc_mode mode; }; -static void exynos_drm_crtc_apply(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; - - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); -} - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos) -{ - struct exynos_drm_gem_buf *buffer; - unsigned int actual_w; - unsigned int actual_h; - int nr = exynos_drm_format_num_buffers(fb->pixel_format); - int i; - - for (i = 0; i < nr; i++) { - buffer = exynos_drm_fb_buffer(fb, i); - if (!buffer) { - DRM_LOG_KMS("buffer is null\n"); - return -EFAULT; - } - - overlay->dma_addr[i] = buffer->dma_addr; - overlay->vaddr[i] = buffer->kvaddr; - - DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", - i, (unsigned long)overlay->vaddr[i], - (unsigned long)overlay->dma_addr[i]); - } - - actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); - actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); - - /* set drm framebuffer data. */ - overlay->fb_x = pos->fb_x; - overlay->fb_y = pos->fb_y; - overlay->fb_width = fb->width; - overlay->fb_height = fb->height; - overlay->src_width = pos->src_w; - overlay->src_height = pos->src_h; - overlay->bpp = fb->bits_per_pixel; - overlay->pitch = fb->pitches[0]; - overlay->pixel_format = fb->pixel_format; - - /* set overlay range to be displayed. */ - overlay->crtc_x = pos->crtc_x; - overlay->crtc_y = pos->crtc_y; - overlay->crtc_width = actual_w; - overlay->crtc_height = actual_h; - - /* set drm mode data. */ - overlay->mode_width = mode->hdisplay; - overlay->mode_height = mode->vdisplay; - overlay->refresh = mode->vrefresh; - overlay->scan_flag = mode->flags; - - DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", - overlay->crtc_x, overlay->crtc_y, - overlay->crtc_width, overlay->crtc_height); - - return 0; -} - -static int exynos_drm_crtc_update(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_overlay *overlay; - struct exynos_drm_crtc_pos pos; - struct drm_display_mode *mode = &crtc->mode; - struct drm_framebuffer *fb = crtc->fb; - - if (!mode || !fb) - return -EINVAL; - - exynos_crtc = to_exynos_crtc(crtc); - overlay = &exynos_crtc->overlay; - - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - - /* it means the offset of framebuffer to be displayed. */ - pos.fb_x = crtc->x; - pos.fb_y = crtc->y; - - /* OSD position to be displayed. */ - pos.crtc_x = 0; - pos.crtc_y = 0; - pos.crtc_w = fb->width - crtc->x; - pos.crtc_h = fb->height - crtc->y; - pos.src_w = pos.crtc_w; - pos.src_h = pos.crtc_h; - - return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); -} - static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; @@ -175,23 +78,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) mutex_lock(&dev->struct_mutex); - switch (mode) { - case DRM_MODE_DPMS_ON: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - default: - DRM_ERROR("unspecified mode %d\n", mode); - break; - } + exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); + exynos_crtc->dpms = mode; mutex_unlock(&dev->struct_mutex); } @@ -209,30 +97,8 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) DRM_DEBUG_KMS("%s\n", __FILE__); - /* - * when set_crtc is requested from user or at booting time, - * crtc->commit would be called without dpms call so if dpms is - * no power on then crtc->dpms should be called - * with DRM_MODE_DPMS_ON for the hardware power to be on. - */ - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) { - int mode = DRM_MODE_DPMS_ON; - - /* - * enable hardware(power on) to all encoders hdmi connected - * to current crtc. - */ - exynos_drm_crtc_dpms(crtc, mode); - /* - * enable dma to all encoders connected to current crtc and - * lcd panel. - */ - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_dpms_from_crtc); - } - - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); + exynos_plane_commit(exynos_crtc->plane); + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); } static bool @@ -251,31 +117,61 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane = exynos_crtc->plane; + unsigned int crtc_w; + unsigned int crtc_h; + int pipe = exynos_crtc->pipe; + int ret; + DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + /* * copy the mode data adjusted by mode_fixup() into crtc->mode * so that hardware can be seet to proper mode. */ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); - return exynos_drm_crtc_update(crtc); + crtc_w = crtc->fb->width - x; + crtc_h = crtc->fb->height - y; + + ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, + x, y, crtc_w, crtc_h); + if (ret) + return ret; + + plane->crtc = crtc; + plane->fb = crtc->fb; + + exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); + + return 0; } static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane = exynos_crtc->plane; + unsigned int crtc_w; + unsigned int crtc_h; int ret; DRM_DEBUG_KMS("%s\n", __FILE__); - ret = exynos_drm_crtc_update(crtc); + crtc_w = crtc->fb->width - x; + crtc_h = crtc->fb->height - y; + + ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, + x, y, crtc_w, crtc_h); if (ret) return ret; - exynos_drm_crtc_apply(crtc); + exynos_drm_crtc_commit(crtc); - return ret; + return 0; } static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) @@ -284,6 +180,16 @@ static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) /* drm framework doesn't check NULL */ } +static void exynos_drm_crtc_disable(struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF); + exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .dpms = exynos_drm_crtc_dpms, .prepare = exynos_drm_crtc_prepare, @@ -292,6 +198,7 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .mode_set = exynos_drm_crtc_mode_set, .mode_set_base = exynos_drm_crtc_mode_set_base, .load_lut = exynos_drm_crtc_load_lut, + .disable = exynos_drm_crtc_disable, }; static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, @@ -327,7 +234,8 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, &dev_priv->pageflip_event_list); crtc->fb = fb; - ret = exynos_drm_crtc_update(crtc); + ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, + NULL); if (ret) { crtc->fb = old_fb; drm_vblank_put(dev, exynos_crtc->pipe); @@ -335,14 +243,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, goto out; } - - /* - * the values related to a buffer of the drm framebuffer - * to be applied should be set at here. because these values - * first, are set to shadow registers and then to - * real registers at vsync front porch period. - */ - exynos_drm_crtc_apply(crtc); } out: mutex_unlock(&dev->struct_mutex); @@ -362,18 +262,73 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) kfree(exynos_crtc); } +static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + DRM_DEBUG_KMS("%s\n", __func__); + + if (property == dev_priv->crtc_mode_property) { + enum exynos_crtc_mode mode = val; + + if (mode == exynos_crtc->mode) + return 0; + + exynos_crtc->mode = mode; + + switch (mode) { + case CRTC_MODE_NORMAL: + exynos_drm_crtc_commit(crtc); + break; + case CRTC_MODE_BLANK: + exynos_plane_dpms(exynos_crtc->plane, + DRM_MODE_DPMS_OFF); + break; + default: + break; + } + + return 0; + } + + return -EINVAL; +} + static struct drm_crtc_funcs exynos_crtc_funcs = { .set_config = drm_crtc_helper_set_config, .page_flip = exynos_drm_crtc_page_flip, .destroy = exynos_drm_crtc_destroy, + .set_property = exynos_drm_crtc_set_property, +}; + +static const struct drm_prop_enum_list mode_names[] = { + { CRTC_MODE_NORMAL, "normal" }, + { CRTC_MODE_BLANK, "blank" }, }; -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc) +static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) { - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; - return &exynos_crtc->overlay; + DRM_DEBUG_KMS("%s\n", __func__); + + prop = dev_priv->crtc_mode_property; + if (!prop) { + prop = drm_property_create_enum(dev, 0, "mode", mode_names, + ARRAY_SIZE(mode_names)); + if (!prop) + return; + + dev_priv->crtc_mode_property = prop; + } + + drm_object_attach_property(&crtc->base, prop, 0); } int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) @@ -391,8 +346,13 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) } exynos_crtc->pipe = nr; - exynos_crtc->dpms = DRM_MODE_DPMS_OFF; - exynos_crtc->overlay.zpos = DEFAULT_ZPOS; + exynos_crtc->dpms = DRM_MODE_DPMS_ON; + exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); + if (!exynos_crtc->plane) { + kfree(exynos_crtc); + return -ENOMEM; + } + crtc = &exynos_crtc->drm_crtc; private->crtc[nr] = crtc; @@ -400,6 +360,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) drm_crtc_init(dev, crtc, &exynos_crtc_funcs); drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); + exynos_drm_crtc_attach_mode_property(crtc); + return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 16b8e21..6bae8d8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -29,39 +29,8 @@ #ifndef _EXYNOS_DRM_CRTC_H_ #define _EXYNOS_DRM_CRTC_H_ -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc); int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); -/* - * Exynos specific crtc postion structure. - * - * @fb_x: offset x on a framebuffer to be displyed - * - the unit is screen coordinates. - * @fb_y: offset y on a framebuffer to be displayed - * - the unit is screen coordinates. - * @src_w: width of source area to be displayed from a framebuffer. - * @src_h: height of source area to be displayed from a framebuffer. - * @crtc_x: offset x on hardware screen. - * @crtc_y: offset y on hardware screen. - * @crtc_w: width of hardware screen. - * @crtc_h: height of hardware screen. - */ -struct exynos_drm_crtc_pos { - unsigned int fb_x; - unsigned int fb_y; - unsigned int src_w; - unsigned int src_h; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_w; - unsigned int crtc_h; -}; - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index ca40c68..128627c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -25,6 +25,7 @@ #include "drmP.h" #include "drm.h" +#include "exynos_drm.h" #include "exynos_drm_drv.h" #include "exynos_drm_gem.h" @@ -235,6 +236,9 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, if (sgt->nents == 1) { buffer->dma_addr = sg_dma_address(sgt->sgl); buffer->size = sg_dma_len(sgt->sgl); + + /* always physically continuous memory if sgt->nents is 1. */ + exynos_gem_obj->flags |= EXYNOS_BO_CONTIG; } else { unsigned int i = 0; @@ -245,28 +249,24 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, sgl = sg_next(sgl); i++; } + + /* + * this case could be CONTIG or NONCONTIG type but now CONTIG. + * we have to find a way that exporter can notify the type of + * its own buffer to importer. TODO + */ + exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG; } exynos_gem_obj->buffer = buffer; buffer->sgt = sgt; exynos_gem_obj->base.import_attach = attach; - /* register buffer information to private buffer manager. */ - ret = register_buf_to_priv_mgr(exynos_gem_obj, - &exynos_gem_obj->priv_handle, - &exynos_gem_obj->priv_id); - if (ret < 0) - goto err_release_gem; - - DRM_DEBUG_PRIME("ump id = %d, dma_addr = 0x%x, size = 0x%lx\n", - exynos_gem_obj->priv_id, buffer->dma_addr, buffer->size); + DRM_DEBUG_PRIME("dma_addr = 0x%x, size = 0x%lx\n", buffer->dma_addr, + buffer->size); return &exynos_gem_obj->base; -err_release_gem: - drm_gem_object_release(&exynos_gem_obj->base); - kfree(exynos_gem_obj); - exynos_gem_obj = NULL; err_free_pages: kfree(buffer->pages); buffer->pages = NULL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 340a8d6..90cff3f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -38,12 +38,13 @@ #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" #include "exynos_drm_g2d.h" -#include "exynos_drm_rotator.h" +#include "exynos_drm_ipp.h" #include "exynos_drm_plane.h" #include "exynos_drm_vidi.h" #include "exynos_drm_dmabuf.h" +#include "exynos_drm_iommu.h" -#define DRIVER_NAME "exynos-drm" +#define DRIVER_NAME "exynos" #define DRIVER_DESC "Samsung SoC DRM" #define DRIVER_DATE "20110530" #define DRIVER_MAJOR 1 @@ -51,53 +52,65 @@ #define VBLANK_OFF_DELAY 50000 -static int exynos_drm_list_gem_info(int id, void *ptr, void *data) +struct exynos_drm_gem_info_data { + struct drm_file *filp; + struct seq_file *m; +}; + +static int exynos_drm_gem_one_info(int id, void *ptr, void *data) { struct drm_gem_object *obj = ptr; - struct drm_file *filp = data; - struct exynos_drm_gem_obj *gem = to_exynos_gem_obj(obj); - struct exynos_drm_gem_buf *buf = gem->buffer; - - DRM_INFO("%3d \t%3d \t%2d \t\t%2d \t0x%lx \t0x%x \t0x%lx "\ - "\t%2d \t\t%2d \t\t%2d\n", - filp->pid, - id, - atomic_read(&obj->refcount.refcount), - atomic_read(&obj->handle_count), - gem->size, - gem->flags, - buf->page_size, - buf->pfnmap, - obj->export_dma_buf ? 1 : 0, - obj->import_attach ? 1 : 0); + struct exynos_drm_gem_info_data *gem_info_data = data; + struct drm_exynos_file_private *file_priv = + gem_info_data->filp->driver_priv; + struct exynos_drm_gem_obj *exynos_gem = to_exynos_gem_obj(obj); + struct exynos_drm_gem_buf *buf = exynos_gem->buffer; + + seq_printf(gem_info_data->m, "%3d \t%3d \t%3d \t%2d \t\t%2d \t0x%08lx"\ + " \t0x%x \t0x%08lx \t%2d \t\t%2d \t\t%2d\n", + gem_info_data->filp->pid, + file_priv->tgid, + id, + atomic_read(&obj->refcount.refcount), + atomic_read(&obj->handle_count), + exynos_gem->size, + exynos_gem->flags, + buf->page_size, + buf->pfnmap, + obj->export_dma_buf ? 1 : 0, + obj->import_attach ? 1 : 0); return 0; } -static ssize_t exynos_drm_show_gem_info(struct device *dev, - struct device_attribute *attr, - char *buf) +static int exynos_drm_gem_info(struct seq_file *m, void *data) { - struct drm_device *drm_dev = dev_get_drvdata(dev); - struct drm_file *filp; + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *drm_dev = node->minor->dev; + struct exynos_drm_gem_info_data gem_info_data; + + gem_info_data.m = m; - DRM_INFO("pid \thandle \trefcount \thcount \tsize \t\tflags "\ - "\tpage_size \tpfnmap \texport_to_fd \timport_from_fd\n"); + seq_printf(gem_info_data.m, "pid \ttgid \thandle \trefcount \thcount "\ + "\tsize \t\tflags \tpage_size \tpfnmap \t"\ + "exyport_to_fd \timport_from_fd\n"); - list_for_each_entry(filp, &drm_dev->filelist, lhead) - idr_for_each(&filp->object_idr, &exynos_drm_list_gem_info, - filp); + list_for_each_entry(gem_info_data.filp, &drm_dev->filelist, lhead) + idr_for_each(&gem_info_data.filp->object_idr, + exynos_drm_gem_one_info, &gem_info_data); - return strlen(buf); + return 0; } -static const struct device_attribute exynos_device_attrs[] = { - __ATTR(gem_info, S_IRUGO, exynos_drm_show_gem_info, NULL) +static struct drm_info_list exynos_drm_debugfs_list[] = { + {"gem_info", exynos_drm_gem_info, DRIVER_GEM}, }; +#define EXYNOS_DRM_DEBUGFS_ENTRIES ARRAY_SIZE(exynos_drm_debugfs_list) static int exynos_drm_load(struct drm_device *dev, unsigned long flags) { struct exynos_drm_private *private; + struct drm_minor *minor; int ret; int nr; @@ -109,6 +122,17 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) return -ENOMEM; } + /* maximum size of userptr is limited to 16MB as default. */ + private->userptr_limit = SZ_16M; + + /* setup device address space for iommu. */ + private->vmm = exynos_drm_iommu_setup(0x80000000, 0x40000000); + if (IS_ERR(private->vmm)) { + DRM_ERROR("failed to setup iommu.\n"); + kfree(private); + return PTR_ERR(private->vmm); + } + INIT_LIST_HEAD(&private->pageflip_event_list); dev->dev_private = (void *)private; @@ -130,8 +154,11 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) } for (nr = 0; nr < MAX_PLANE; nr++) { - ret = exynos_plane_init(dev, nr); - if (ret) + struct drm_plane *plane; + unsigned int possible_crtcs = (1 << MAX_CRTC) - 1; + + plane = exynos_plane_init(dev, possible_crtcs, false); + if (!plane) goto err_crtc; } @@ -163,9 +190,12 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) drm_vblank_offdelay = VBLANK_OFF_DELAY; - ret = device_create_file(dev->dev, &exynos_device_attrs[0]); - if (ret < 0) - DRM_DEBUG_DRIVER("failed to create sysfs.\n"); + minor = dev->primary; + ret = drm_debugfs_create_files(exynos_drm_debugfs_list, + EXYNOS_DRM_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); + if (ret) + DRM_DEBUG_DRIVER("failed to create exynos-drm debugfs.\n"); return 0; @@ -182,8 +212,15 @@ err_crtc: static int exynos_drm_unload(struct drm_device *dev) { + struct exynos_drm_private *private; + + private = dev->dev_private; + DRM_DEBUG_DRIVER("%s\n", __FILE__); + /* release vmm object and device address space for iommu. */ + exynos_drm_iommu_cleanup(private->vmm); + exynos_drm_fbdev_fini(dev); exynos_drm_device_unregister(dev); drm_vblank_cleanup(dev); @@ -193,6 +230,9 @@ static int exynos_drm_unload(struct drm_device *dev) dev->dev_private = NULL; + drm_debugfs_remove_files(exynos_drm_debugfs_list, + EXYNOS_DRM_DEBUGFS_ENTRIES, dev->primary); + return 0; } @@ -206,6 +246,8 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) if (!file_priv) return -ENOMEM; + file_priv->tgid = task_tgid_nr(current); + drm_prime_init_file_private(&file->prime); file->driver_priv = file_priv; @@ -271,6 +313,9 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_userptr_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(EXYNOS_USER_LIMIT, + exynos_drm_gem_user_limit_ioctl, DRM_MASTER | + DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_EXPORT_UMP, exynos_drm_gem_export_ump_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CACHE_OP, @@ -280,8 +325,6 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_get_phy_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_PHY_IMP, exynos_drm_gem_phy_imp_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, - exynos_plane_set_zpos_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), @@ -292,8 +335,14 @@ static struct drm_ioctl_desc exynos_ioctls[] = { DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), - DRM_IOCTL_DEF_DRV(EXYNOS_ROTATOR_EXEC, - exynos_drm_rotator_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, + exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, + exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_BUF, + exynos_drm_ipp_buf, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CTRL, + exynos_drm_ipp_ctrl, DRM_UNLOCKED | DRM_AUTH), }; static const struct file_operations exynos_drm_driver_fops = { @@ -307,8 +356,8 @@ static const struct file_operations exynos_drm_driver_fops = { }; static struct drm_driver exynos_drm_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM | - DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | + DRIVER_GEM | DRIVER_PRIME, .load = exynos_drm_load, .unload = exynos_drm_unload, .open = exynos_drm_open, @@ -407,6 +456,24 @@ static int __init exynos_drm_init(void) goto out_rotator; #endif +#ifdef CONFIG_DRM_EXYNOS_FIMC + ret = platform_driver_register(&fimc_driver); + if (ret < 0) + goto out_fimc; +#endif + +#ifdef CONFIG_DRM_EXYNOS_GSC + ret = platform_driver_register(&gsc_driver); + if (ret < 0) + goto out_gsc; +#endif + +#ifdef CONFIG_DRM_EXYNOS_IPP + ret = platform_driver_register(&ipp_driver); + if (ret < 0) + goto out_ipp; +#endif + ret = platform_driver_register(&exynos_drm_platform_driver); if (ret < 0) goto out; @@ -414,6 +481,21 @@ static int __init exynos_drm_init(void) return 0; out: +#ifdef CONFIG_DRM_EXYNOS_IPP + platform_driver_unregister(&ipp_driver); +out_ipp: +#endif + +#ifdef CONFIG_DRM_EXYNOS_GSC + platform_driver_unregister(&gsc_driver); +out_gsc: +#endif + +#ifdef CONFIG_DRM_EXYNOS_FIMC + platform_driver_unregister(&fimc_driver); +out_fimc: +#endif + #ifdef CONFIG_DRM_EXYNOS_ROTATOR platform_driver_unregister(&rotator_driver); out_rotator: @@ -451,6 +533,18 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&exynos_drm_platform_driver); +#ifdef CONFIG_DRM_EXYNOS_IPP + platform_driver_unregister(&ipp_driver); +#endif + +#ifdef CONFIG_DRM_EXYNOS_GSC + platform_driver_unregister(&gsc_driver); +#endif + +#ifdef CONFIG_DRM_EXYNOS_FIMC + platform_driver_unregister(&fimc_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_ROTATOR platform_driver_unregister(&rotator_driver); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 463b086..8cdf93b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -59,12 +59,14 @@ enum exynos_drm_output_type { * * @mode_set: copy drm overlay info to hw specific overlay info. * @commit: apply hardware specific overlay data to registers. + * @enable: enable hardware specific overlay. * @disable: disable hardware specific overlay. */ struct exynos_drm_overlay_ops { void (*mode_set)(struct device *subdrv_dev, struct exynos_drm_overlay *overlay); void (*commit)(struct device *subdrv_dev, int zpos); + void (*enable)(struct device *subdrv_dev, int zpos); void (*disable)(struct device *subdrv_dev, int zpos); }; @@ -220,32 +222,30 @@ struct iommu_info_node { * Exynos drm g2d private structure * * @dev: device object to device driver for using iommu. - * @iommu_map_list: list head to iommu map information. - * each device driver using iommu should have its own iommu_map_list - * because device drivers have their own device address space and - * the device address spaces could be duplicated echo other. */ struct exynos_drm_g2d_private { struct device *dev; struct list_head inuse_cmdlist; struct list_head event_list; - struct list_head iommu_map_list; }; /* - * Exynos drm rotator private structure + * Exynos drm ipp private structure * * @dev: device object to device driver for using driver data. - * @iommu_list: list head to iommu map information. + * @ippdrv: link used ippdrv. + * @event_list: list head to event. */ -struct exynos_drm_rot_private { - struct device *dev; - struct list_head iommu_list; +struct exynos_drm_ipp_private { + struct device *dev; + void *ippdrv; + struct list_head event_list; }; struct drm_exynos_file_private { struct exynos_drm_g2d_private *g2d_priv; - struct exynos_drm_rot_private *rot_priv; + struct exynos_drm_ipp_private *ipp_priv; + pid_t tgid; }; /* @@ -262,6 +262,18 @@ struct exynos_drm_private { * this array is used to be aware of which crtc did it request vblank. */ struct drm_crtc *crtc[MAX_CRTC]; + + /* + * maximum size of allocation by userptr feature. + * - as default, this has 16MB and only root user can change it. + */ + unsigned long userptr_limit; + + /* a iovmm object for iommu support. */ + void *vmm; + + struct drm_property *plane_zpos_property; + struct drm_property *crtc_mode_property; }; /* @@ -289,7 +301,7 @@ struct exynos_drm_subdrv { struct exynos_drm_manager *manager; int (*probe)(struct drm_device *drm_dev, struct device *dev); - void (*remove)(struct drm_device *dev); + void (*remove)(struct drm_device *drm_dev, struct device *dev); int (*open)(struct drm_device *drm_dev, struct device *dev, struct drm_file *file); void (*close)(struct drm_device *drm_dev, struct device *dev, @@ -332,4 +344,7 @@ extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; extern struct platform_driver rotator_driver; +extern struct platform_driver fimc_driver; +extern struct platform_driver gsc_driver; +extern struct platform_driver ipp_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 6e9ac7b..ebc4456 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -30,7 +30,6 @@ #include "drm_crtc_helper.h" #include "exynos_drm_drv.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_encoder.h" #define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ @@ -136,21 +135,16 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, struct drm_connector *connector; struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev, - encoder->crtc); DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { + if (connector->encoder == encoder) if (manager_ops && manager_ops->mode_set) manager_ops->mode_set(manager->dev, adjusted_mode); - - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); - } } } @@ -261,7 +255,7 @@ exynos_drm_encoder_create(struct drm_device *dev, return NULL; } - exynos_encoder->dpms = DRM_MODE_DPMS_OFF; + exynos_encoder->dpms = DRM_MODE_DPMS_ON; exynos_encoder->manager = manager; encoder = &exynos_encoder->drm_encoder; encoder->possible_crtcs = possible_crtcs; @@ -317,8 +311,8 @@ void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int crtc = *(int *)data; - if (manager->pipe == -1) - manager->pipe = crtc; + if (manager->pipe != crtc) + return; if (manager_ops->enable_vblank) manager_ops->enable_vblank(manager->dev); @@ -331,34 +325,41 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int crtc = *(int *)data; - if (manager->pipe == -1) - manager->pipe = crtc; + if (manager->pipe != crtc) + return; if (manager_ops->disable_vblank) manager_ops->disable_vblank(manager->dev); } -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data) +void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) { - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - int zpos = DEFAULT_ZPOS; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + struct exynos_drm_manager *manager = exynos_encoder->manager; + struct exynos_drm_manager_ops *manager_ops = manager->ops; + int mode = *(int *)data; - if (data) - zpos = *(int *)data; + DRM_DEBUG_KMS("%s\n", __FILE__); - if (overlay_ops && overlay_ops->commit) - overlay_ops->commit(manager->dev, zpos); + if (manager_ops && manager_ops->dpms) + manager_ops->dpms(manager->dev, mode); + + /* + * if this condition is ok then it means that the crtc is already + * detached from encoder and last function for detaching is properly + * done, so clear pipe from manager to prevent repeated call. + */ + if (mode > DRM_MODE_DPMS_ON) { + if (!encoder->crtc) + manager->pipe = -1; + } } -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; - int crtc = *(int *)data; - int zpos = DEFAULT_ZPOS; + int pipe = *(int *)data; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -366,76 +367,62 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) * when crtc is detached from encoder, this pipe is used * to select manager operation */ - manager->pipe = crtc; - - exynos_drm_encoder_crtc_plane_commit(encoder, &zpos); + manager->pipe = pipe; } -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) { - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - int mode = *(int *)data; + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + struct exynos_drm_overlay *overlay = data; DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_encoder_dpms(encoder, mode); - - exynos_encoder->dpms = mode; + if (overlay_ops && overlay_ops->mode_set) + overlay_ops->mode_set(manager->dev, overlay); } -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) { - struct drm_device *dev = encoder->dev; - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - struct exynos_drm_manager *manager = exynos_encoder->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct drm_connector *connector; - int mode = *(int *)data; + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + int zpos = DEFAULT_ZPOS; DRM_DEBUG_KMS("%s\n", __FILE__); - if (manager_ops && manager_ops->dpms) - manager_ops->dpms(manager->dev, mode); - - /* - * set current dpms mode to the connector connected to - * current encoder. connector->dpms would be checked - * at drm_helper_connector_dpms() - */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - if (connector->encoder == encoder) - connector->dpms = mode; + if (data) + zpos = *(int *)data; - /* - * if this condition is ok then it means that the crtc is already - * detached from encoder and last function for detaching is properly - * done, so clear pipe from manager to prevent repeated call. - */ - if (mode > DRM_MODE_DPMS_ON) { - if (!encoder->crtc) - manager->pipe = -1; - } + if (overlay_ops && overlay_ops->commit) + overlay_ops->commit(manager->dev, zpos); } -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = data; + int zpos = DEFAULT_ZPOS; - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (data) + zpos = *(int *)data; + + if (overlay_ops && overlay_ops->enable) + overlay_ops->enable(manager->dev, zpos); } -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; int zpos = DEFAULT_ZPOS; - DRM_DEBUG_KMS("\n"); + DRM_DEBUG_KMS("%s\n", __FILE__); if (data) zpos = *(int *)data; diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h index eb7d231..6470d9d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -40,13 +40,11 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, void (*fn)(struct drm_encoder *, void *)); void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data); -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, - void *data); void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 7b47330..41009e4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -49,10 +49,19 @@ struct exynos_drm_fb { struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; }; -static int check_fb_gem_memory_type(struct exynos_drm_gem_obj *exynos_gem_obj) +static int check_fb_gem_memory_type(struct drm_device *drm_dev, + struct exynos_drm_gem_obj *exynos_gem_obj) { + struct exynos_drm_private *private = drm_dev->dev_private; unsigned int flags; + /* + * if exynos drm driver supports iommu then framebuffer can use + * all the buffer types. + */ + if (private->vmm) + return 0; + flags = exynos_gem_obj->flags; /* not support physically non-continuous memory for fb yet. TODO */ @@ -64,14 +73,63 @@ static int check_fb_gem_memory_type(struct exynos_drm_gem_obj *exynos_gem_obj) return 0; } +static int check_fb_gem_size(struct drm_device *drm_dev, + struct drm_framebuffer *fb, + unsigned int nr) +{ + unsigned long fb_size; + struct drm_gem_object *obj; + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + + /* in case of RGB format, only one plane is used. */ + if (nr < 2) { + exynos_gem_obj = exynos_fb->exynos_gem_obj[0]; + obj = &exynos_gem_obj->base; + fb_size = fb->pitches[0] * fb->height; + + if (fb_size != exynos_gem_obj->packed_size) { + DRM_ERROR("invalid fb or gem size.\n"); + return -EINVAL; + } + /* in case of NV12MT, YUV420M and so on, two and three planes. */ + } else { + unsigned int i; + + for (i = 0; i < nr; i++) { + exynos_gem_obj = exynos_fb->exynos_gem_obj[i]; + obj = &exynos_gem_obj->base; + fb_size = fb->pitches[i] * fb->height; + + if (fb_size != exynos_gem_obj->packed_size) { + DRM_ERROR("invalid fb or gem size.\n"); + return -EINVAL; + } + } + } + + return 0; +} + static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) { struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + unsigned int i; DRM_DEBUG_KMS("%s\n", __FILE__); drm_framebuffer_cleanup(fb); + for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem_obj); i++) { + struct drm_gem_object *obj; + + if (exynos_fb->exynos_gem_obj[i] == NULL) + continue; + + obj = &exynos_fb->exynos_gem_obj[i]->base; + drm_gem_object_unreference_unlocked(obj); + } + kfree(exynos_fb); exynos_fb = NULL; } @@ -117,7 +175,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev, exynos_gem_obj = to_exynos_gem_obj(obj); - ret = check_fb_gem_memory_type(exynos_gem_obj); + ret = check_fb_gem_memory_type(dev, exynos_gem_obj); if (ret < 0) { DRM_ERROR("cannot use this gem memory type for fb.\n"); return ERR_PTR(-EINVAL); @@ -149,8 +207,7 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_gem_object *obj; struct drm_framebuffer *fb; struct exynos_drm_fb *exynos_fb; - int nr; - int i; + int nr, i, ret; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -160,11 +217,11 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-ENOENT); } - drm_gem_object_unreference_unlocked(obj); - fb = exynos_drm_framebuffer_init(dev, mode_cmd, obj); - if (IS_ERR(fb)) + if (IS_ERR(fb)) { + drm_gem_object_unreference_unlocked(obj); return fb; + } exynos_fb = to_exynos_fb(fb); nr = exynos_drm_format_num_buffers(fb->pixel_format); @@ -181,11 +238,9 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-ENOENT); } - drm_gem_object_unreference_unlocked(obj); - exynos_gem_obj = to_exynos_gem_obj(obj); - ret = check_fb_gem_memory_type(exynos_gem_obj); + ret = check_fb_gem_memory_type(dev, exynos_gem_obj); if (ret < 0) { DRM_ERROR("cannot use this gem memory type for fb.\n"); exynos_drm_fb_destroy(fb); @@ -195,6 +250,12 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj); } + ret = check_fb_gem_size(dev, fb, nr); + if (ret < 0) { + exynos_drm_fb_destroy(fb); + return ERR_PTR(ret); + } + return fb; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index d5586cc..df34adf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -82,9 +82,9 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); offset += fbi->var.yoffset * fb->pitches[0]; - dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr; + dev->mode_config.fb_base = (resource_size_t)buffer->paddr; fbi->screen_base = buffer->kvaddr + offset; - fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset); + fbi->fix.smem_start = (unsigned long)(buffer->paddr + offset); fbi->screen_size = size; fbi->fix.smem_len = size; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c new file mode 100644 index 0000000..fdf5be8 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -0,0 +1,1925 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include "drmP.h" +#include "drm_backlight.h" +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/cma.h> +#include <plat/map-base.h> + +#include "regs-fimc.h" +#include <drm/exynos_drm.h> +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" +#include "exynos_drm_ipp.h" +#include "exynos_drm_fimc.h" + +/* + * FIMC is stand for Fully Interactive Mobile Camera and + * supports image scaler/rotator and input/output DMA operations. + * input DMA reads image data from the memory. + * output DMA writes image data to memory. + * FIMC supports image rotation and image effect functions. + */ + +#define FIMC_MAX_DEVS 4 +#define FIMC_MAX_SRC 2 +#define FIMC_MAX_DST 32 +#ifdef CONFIG_SLP_DISP_DEBUG +#define FIMC_MAX_REG 128 +#define FIMC_BASE_REG(id) (0x11800000 + (0x10000 * id)) +#endif +#define FIMC_CLK_RATE 166750000 +#define FIMC_BUF_STOP 1 +#define FIMC_BUF_START 2 +#define FIMC_REG_SZ 32 +#define FIMC_WIDTH_ITU_709 1280 + +#define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ + struct fimc_context, ippdrv); +#define fimc_read(offset) readl(ctx->regs + (offset)); +#define fimc_write(cfg, offset) writel(cfg, ctx->regs + (offset)); + +enum fimc_wb { + FIMC_WB_NONE, + FIMC_WB_A, + FIMC_WB_B, +}; + +/* + * A structure of scaler. + * + * @range: narrow, wide. + * @bypass: unused scaler path. + * @up_h: horizontal scale up. + * @up_v: vertical scale up. + * @hratio: horizontal ratio. + * @vratio: vertical ratio. + */ +struct fimc_scaler { + bool range; + bool bypass; + bool up_h; + bool up_v; + u32 hratio; + u32 vratio; +}; + +/* + * A structure of scaler capability. + * + * find user manual table 43-1. + * @in_hori: scaler input horizontal size. + * @bypass: scaler bypass mode. + * @dst_h_wo_rot: target horizontal size without output rotation. + * @dst_h_rot: target horizontal size with output rotation. + * @rl_w_wo_rot: real width without input rotation. + * @rl_h_rot: real height without output rotation. + */ +struct fimc_capability { + /* scaler */ + u32 in_hori; + u32 bypass; + /* output rotator */ + u32 dst_h_wo_rot; + u32 dst_h_rot; + /* input rotator */ + u32 rl_w_wo_rot; + u32 rl_h_rot; +}; + +/* + * A structure of fimc context. + * + * @ippdrv: prepare initialization using ippdrv. + * @regs_res: register resources. + * @regs: memory mapped io registers. + * @lock: locking of operations. + * @sclk_fimc_clk: fimc source clock. + * @fimc_clk: fimc clock. + * @wb_clk: writeback a clock. + * @wb_b_clk: writeback b clock. + * @sc: scaler infomations. + * @capa: scaler capability. + * @odr: ordering of YUV. + * @ver: fimc version. + * @pol: porarity of writeback. + * @id: fimc id. + * @irq: irq number. + * @suspended: qos operations. + */ +struct fimc_context { + struct exynos_drm_ippdrv ippdrv; + struct resource *regs_res; + void __iomem *regs; + struct mutex lock; + struct clk *sclk_fimc_clk; + struct clk *fimc_clk; + struct clk *wb_clk; + struct clk *wb_b_clk; + struct fimc_scaler sc; + struct fimc_capability *capa; + enum exynos_drm_fimc_ver ver; + struct exynos_drm_fimc_pol pol; + int id; + int irq; + bool suspended; +}; + +struct fimc_capability fimc51_capa[FIMC_MAX_DEVS] = { + { + .in_hori = 4224, + .bypass = 8192, + .dst_h_wo_rot = 4224, + .dst_h_rot = 1920, + .rl_w_wo_rot = 8192, + .rl_h_rot = 1920, + }, { + .in_hori = 4224, + .bypass = 8192, + .dst_h_wo_rot = 4224, + .dst_h_rot = 1920, + .rl_w_wo_rot = 8192, + .rl_h_rot = 1920, + }, { + .in_hori = 4224, + .bypass = 8192, + .dst_h_wo_rot = 4224, + .dst_h_rot = 1920, + .rl_w_wo_rot = 8192, + .rl_h_rot = 1920, + }, { + .in_hori = 1920, + .bypass = 8192, + .dst_h_wo_rot = 1920, + .dst_h_rot = 1366, + .rl_w_wo_rot = 8192, + .rl_h_rot = 1366, + }, +}; + +static void fimc_sw_reset(struct fimc_context *ctx, bool pattern) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:pattern[%d]\n", __func__, pattern); + + cfg = fimc_read(EXYNOS_CISRCFMT); + cfg |= EXYNOS_CISRCFMT_ITU601_8BIT; + if (pattern) + cfg |= EXYNOS_CIGCTRL_TESTPATTERN_COLOR_BAR; + + fimc_write(cfg, EXYNOS_CISRCFMT); + + /* s/w reset */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg |= (EXYNOS_CIGCTRL_SWRST); + fimc_write(cfg, EXYNOS_CIGCTRL); + + /* s/w reset complete */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~EXYNOS_CIGCTRL_SWRST; + fimc_write(cfg, EXYNOS_CIGCTRL); + + /* reset sequence */ + fimc_write(0x0, EXYNOS_CIFCNTSEQ); +} + +static void fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) +{ + u32 camblk_cfg; + + DRM_DEBUG_KMS("%s\n", __func__); + + camblk_cfg = readl(SYSREG_CAMERA_BLK); + camblk_cfg &= ~(SYSREG_FIMD0WB_DEST_MASK); + camblk_cfg |= ctx->id << (SYSREG_FIMD0WB_DEST_SHIFT); + + writel(camblk_cfg, SYSREG_CAMERA_BLK); +} + +static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:wb[%d]\n", __func__, wb); + + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | + EXYNOS_CIGCTRL_SELCAM_ITU_MASK | + EXYNOS_CIGCTRL_SELCAM_MIPI_MASK | + EXYNOS_CIGCTRL_SELCAM_FIMC_MASK | + EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | + EXYNOS_CIGCTRL_SELWRITEBACK_MASK); + + switch (wb) { + case FIMC_WB_A: + cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A | + EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); + break; + case FIMC_WB_B: + cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B | + EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); + break; + case FIMC_WB_NONE: + default: + cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | + EXYNOS_CIGCTRL_SELWRITEBACK_A | + EXYNOS_CIGCTRL_SELCAM_MIPI_A | + EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); + break; + } + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_set_polarity(struct fimc_context *ctx, + struct exynos_drm_fimc_pol *pol) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:inv_pclk[%d]inv_vsync[%d]\n", + __func__, pol->inv_pclk, pol->inv_vsync); + DRM_DEBUG_KMS("%s:inv_href[%d]inv_hsync[%d]\n", + __func__, pol->inv_href, pol->inv_hsync); + + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | + EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); + + if (pol->inv_pclk) + cfg |= EXYNOS_CIGCTRL_INVPOLPCLK; + if (pol->inv_vsync) + cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC; + if (pol->inv_href) + cfg |= EXYNOS_CIGCTRL_INVPOLHREF; + if (pol->inv_hsync) + cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + cfg = fimc_read(EXYNOS_CIGCTRL); + if (enable) + cfg |= EXYNOS_CIGCTRL_CAM_JPEG; + else + cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG; + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_handle_irq(struct fimc_context *ctx, bool enable, + bool overflow, bool level) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, + enable, overflow, level); + + cfg = fimc_read(EXYNOS_CIGCTRL); + if (enable) { + cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL); + cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE; + if (overflow) + cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN; + if (level) + cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL; + } else + cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE); + + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static void fimc_clear_irq(struct fimc_context *ctx) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s\n", __func__); + + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg |= EXYNOS_CIGCTRL_IRQ_CLR; + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static bool fimc_check_ovf(struct fimc_context *ctx) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg, status, flag; + + status = fimc_read(EXYNOS_CISTATUS); + flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | + EXYNOS_CISTATUS_OVFICR; + + DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag); + + if (status & flag) { + cfg = fimc_read(EXYNOS_CIWDOFST); + cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | + EXYNOS_CIWDOFST_CLROVFICR); + + fimc_write(cfg, EXYNOS_CIWDOFST); + + cfg = fimc_read(EXYNOS_CIWDOFST); + cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | + EXYNOS_CIWDOFST_CLROVFICR); + + fimc_write(cfg, EXYNOS_CIWDOFST); + + dev_err(ippdrv->dev, "occured overflow at %d, status 0x%x.\n", + ctx->id, status); + return true; + } + + return false; +} + +static bool fimc_check_frame_end(struct fimc_context *ctx) +{ + u32 cfg; + + cfg = fimc_read(EXYNOS_CISTATUS); + + DRM_DEBUG_KMS("%s:cfg[0x%x]\n", __func__, cfg); + + if (!(cfg & EXYNOS_CISTATUS_FRAMEEND)) + return false; + + cfg &= ~(EXYNOS_CISTATUS_FRAMEEND); + fimc_write(cfg, EXYNOS_CISTATUS); + + return true; +} + +static int fimc_get_buf_id(struct fimc_context *ctx) +{ + u32 cfg; + int frame_cnt, buf_id; + + DRM_DEBUG_KMS("%s\n", __func__); + + cfg = fimc_read(EXYNOS_CISTATUS2); + frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); + + if (frame_cnt == 0) + frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg); + + DRM_DEBUG_KMS("%s:present[%d]before[%d]\n", __func__, + EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg), + EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg)); + + if (frame_cnt == 0) { + DRM_ERROR("failed to get frame count.\n"); + return -EIO; + } + + buf_id = frame_cnt - 1; + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + + return buf_id; +} + +static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + cfg = fimc_read(EXYNOS_CIOCTRL); + if (enable) + cfg |= EXYNOS_CIOCTRL_LASTENDEN; + else + cfg &= ~EXYNOS_CIOCTRL_LASTENDEN; + + fimc_write(cfg, EXYNOS_CIOCTRL); +} + +static int fimc_set_planar_addr(struct drm_exynos_ipp_buf_info *buf_info, + u32 fmt, struct drm_exynos_sz *sz) +{ + dma_addr_t *y_addr = &buf_info->base[EXYNOS_DRM_PLANAR_Y]; + dma_addr_t *cb_addr = &buf_info->base[EXYNOS_DRM_PLANAR_CB]; + dma_addr_t *cr_addr = &buf_info->base[EXYNOS_DRM_PLANAR_CR]; + uint64_t y_ofs, cb_ofs, cr_ofs; + + /* + * ToDo: check the buffer size between gem allocated buffers + * and each planar size. + */ + switch (fmt) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + y_ofs = sz->hsize * sz->vsize; + cb_ofs = y_ofs >> 1; + cr_ofs = 0; + break; + case DRM_FORMAT_NV12M: + y_ofs = ALIGN(ALIGN(sz->hsize, 16) * + ALIGN(sz->vsize, 16), SZ_2K); + cb_ofs = ALIGN(ALIGN(sz->hsize, 16) * + ALIGN(sz->vsize >> 1, 16), SZ_2K); + cr_ofs = 0; + break; + case DRM_FORMAT_NV12MT: + y_ofs = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize, 32), SZ_8K); + cb_ofs = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize >> 1, 32), SZ_8K); + cr_ofs = 0; + break; + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU444: + case DRM_FORMAT_YUV420M: + y_ofs = sz->hsize * sz->vsize; + cb_ofs = cr_ofs = y_ofs >> 2; + break; + default: + y_ofs = cb_ofs = cr_ofs = 0; + break; + } + + if (y_ofs && *y_addr) { + *cb_addr = *y_addr + y_ofs; + + if (cb_ofs && *cb_addr) + *cr_addr = *cb_addr + cb_ofs; + } + + return 0; +} + +static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + /* RGB */ + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK; + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + default: + /* bypass */ + break; + } + + /* YUV */ + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK | + EXYNOS_MSCTRL_C_INT_IN_2PLANE | + EXYNOS_MSCTRL_ORDER422_YCBYCR); + + switch (fmt) { + case DRM_FORMAT_YUYV: + cfg |= EXYNOS_MSCTRL_ORDER422_YCBYCR; + break; + case DRM_FORMAT_YVYU: + cfg |= EXYNOS_MSCTRL_ORDER422_YCRYCB; + break; + case DRM_FORMAT_UYVY: + cfg |= EXYNOS_MSCTRL_ORDER422_CBYCRY; + break; + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUV444: + cfg |= EXYNOS_MSCTRL_ORDER422_CRYCBY; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CRCB | + EXYNOS_MSCTRL_C_INT_IN_2PLANE); + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= EXYNOS_MSCTRL_C_INT_IN_3PLANE; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + case DRM_FORMAT_NV12MT: + case DRM_FORMAT_NV16: + cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | + EXYNOS_MSCTRL_C_INT_IN_2PLANE); + break; + default: + dev_err(ippdrv->dev, "inavlid source yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_MSCTRL); + + return 0; +} + +static int fimc_src_set_fmt(struct device *dev, u32 fmt) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_MSCTRL_INFORMAT_RGB; + break; + case DRM_FORMAT_YUV444: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE; + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_YUV422: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV12MT: + cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; + break; + default: + dev_err(ippdrv->dev, "inavlid source format 0x%x.\n", fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_MSCTRL); + + cfg = fimc_read(EXYNOS_CIDMAPARAM); + cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; + + if (fmt == DRM_FORMAT_NV12MT) + cfg |= EXYNOS_CIDMAPARAM_R_MODE_64X32; + else + cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; + + fimc_write(cfg, EXYNOS_CIDMAPARAM); + + return fimc_src_set_fmt_order(ctx, fmt); +} + +static int fimc_src_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | + EXYNOS_MSCTRL_FLIP_Y_MIRROR); + cfg &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_90: + cfg |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | + EXYNOS_MSCTRL_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= (EXYNOS_CITRGFMT_INROT90_CLOCKWISE | + EXYNOS_MSCTRL_FLIP_X_MIRROR | + EXYNOS_MSCTRL_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_MSCTRL); + + return (cfg & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; +} + +static int fimc_set_window(struct fimc_context *ctx, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + u32 cfg, h1, h2, v1, v2; + + /* cropped image */ + h1 = pos->x; + h2 = sz->hsize - pos->w - pos->x; + v1 = pos->y; + v2 = sz->vsize - pos->h - pos->y; + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("%s:h1[%d]h2[%d]v1[%d]v2[%d]\n", __func__, + h1, h2, v1, v2); + + /* + * set window offset 1, 2 size + * check figure 43-21 in user manual + */ + cfg = fimc_read(EXYNOS_CIWDOFST); + cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK | + EXYNOS_CIWDOFST_WINVEROFST_MASK); + cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) | + EXYNOS_CIWDOFST_WINVEROFST(v1)); + cfg |= EXYNOS_CIWDOFST_WINOFSEN; + fimc_write(cfg, EXYNOS_CIWDOFST); + + cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | + EXYNOS_CIWDOFST2_WINVEROFST2(v2)); + fimc_write(cfg, EXYNOS_CIWDOFST2); + + return 0; +} + +static int fimc_src_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_SRC]; + struct drm_exynos_pos img_pos = *pos; + struct drm_exynos_sz img_sz = *sz; + u32 cfg; + + /* ToDo: check width and height */ + + DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* original size */ + cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | + EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); + + fimc_write(cfg, EXYNOS_ORGISIZE); + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", __func__, + pos->x, pos->y, pos->w, pos->h); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + img_sz.hsize = sz->vsize; + img_sz.vsize = sz->hsize; + } + + /* set input DMA image size */ + cfg = fimc_read(EXYNOS_CIREAL_ISIZE); + cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | + EXYNOS_CIREAL_ISIZE_WIDTH_MASK); + cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_sz.hsize) | + EXYNOS_CIREAL_ISIZE_HEIGHT(img_sz.vsize)); + fimc_write(cfg, EXYNOS_CIREAL_ISIZE); + + /* + * set input FIFO image size + * for now, we support only ITU601 8 bit mode + */ + cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | + EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | + EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); + fimc_write(cfg, EXYNOS_CISRCFMT); + + config->sz = img_sz; + config->pos = img_pos; + + return fimc_set_window(ctx, &img_pos, &img_sz); +} + +static int fimc_src_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_SRC]; + int ret; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + if (buf_id > FIMC_MAX_SRC) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + case IPP_BUF_CTRL_DEQUEUE: + ret = fimc_set_planar_addr(buf_info, config->fmt, &config->sz); + + if (ret) { + dev_err(dev, "failed to set plane addr.\n"); + return ret; + } + + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + EXYNOS_CIIYSA(buf_id)); + + if (config->fmt == DRM_FORMAT_YVU420) { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIICBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIICRSA(buf_id)); + } else { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIICBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIICRSA(buf_id)); + } + break; + default: + /* bypass */ + break; + } + + return 0; +} + +static struct exynos_drm_ipp_ops fimc_src_ops = { + .set_fmt = fimc_src_set_fmt, + .set_transf = fimc_src_set_transf, + .set_size = fimc_src_set_size, + .set_addr = fimc_src_set_addr, +}; + +static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + /* RGB */ + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK; + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + case DRM_FORMAT_RGB888: + cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; + fimc_write(cfg, EXYNOS_CISCCTRL); + return 0; + case DRM_FORMAT_XRGB8888: + cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | + EXYNOS_CISCCTRL_EXTRGB_EXTENSION); + fimc_write(cfg, EXYNOS_CISCCTRL); + break; + default: + /* bypass */ + break; + } + + /* YUV */ + cfg = fimc_read(EXYNOS_CIOCTRL); + cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK | + EXYNOS_CIOCTRL_ORDER422_MASK | + EXYNOS_CIOCTRL_YCBCR_PLANE_MASK); + + switch (fmt) { + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_CIOCTRL_ALPHA_OUT; + break; + case DRM_FORMAT_YUYV: + cfg |= EXYNOS_CIOCTRL_ORDER422_YCBYCR; + break; + case DRM_FORMAT_YVYU: + cfg |= EXYNOS_CIOCTRL_ORDER422_YCRYCB; + break; + case DRM_FORMAT_UYVY: + cfg |= EXYNOS_CIOCTRL_ORDER422_CBYCRY; + break; + case DRM_FORMAT_VYUY: + cfg |= EXYNOS_CIOCTRL_ORDER422_CRYCBY; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB; + cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= EXYNOS_CIOCTRL_YCBCR_3PLANE; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + case DRM_FORMAT_NV12MT: + case DRM_FORMAT_NV16: + cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; + cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_CIOCTRL); + + return 0; +} + +static int fimc_dst_set_fmt(struct device *dev, u32 fmt) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = fimc_read(EXYNOS_CIEXTEN); + + if (fmt == DRM_FORMAT_AYUV) { + cfg |= EXYNOS_CIEXTEN_YUV444_OUT; + fimc_write(cfg, EXYNOS_CIEXTEN); + } else { + cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT; + fimc_write(cfg, EXYNOS_CIEXTEN); + + cfg = fimc_read(EXYNOS_CITRGFMT); + cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK; + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_RGB; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE; + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_YUV422: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + case DRM_FORMAT_NV12MT: + case DRM_FORMAT_NV21: + cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; + break; + default: + dev_err(ippdrv->dev, "inavlid target format 0x%x.\n", + fmt); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_CITRGFMT); + } + + cfg = fimc_read(EXYNOS_CIDMAPARAM); + cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; + + if (fmt == DRM_FORMAT_NV12MT) + cfg |= EXYNOS_CIDMAPARAM_W_MODE_64X32; + else + cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; + + fimc_write(cfg, EXYNOS_CIDMAPARAM); + + return fimc_dst_set_fmt_order(ctx, fmt); +} + +static int fimc_dst_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = fimc_read(EXYNOS_CITRGFMT); + cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; + cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_90: + cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | + EXYNOS_CITRGFMT_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | + EXYNOS_CITRGFMT_FLIP_X_MIRROR | + EXYNOS_CITRGFMT_FLIP_Y_MIRROR); + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + fimc_write(cfg, EXYNOS_CITRGFMT); + + return (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; +} + +static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift) +{ + DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); + + if (src >= dst * 64) { + DRM_ERROR("failed to make ratio and shift.\n"); + return -EINVAL; + } else if (src >= dst * 32) { + *ratio = 32; + *shift = 5; + } else if (src >= dst * 16) { + *ratio = 16; + *shift = 4; + } else if (src >= dst * 8) { + *ratio = 8; + *shift = 3; + } else if (src >= dst * 4) { + *ratio = 4; + *shift = 2; + } else if (src >= dst * 2) { + *ratio = 2; + *shift = 1; + } else { + *ratio = 1; + *shift = 0; + } + + return 0; +} + +static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, + struct drm_exynos_pos *src, struct drm_exynos_pos *dst) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg, shfactor; + u32 pre_dst_width, pre_dst_height; + u32 pre_hratio, hfactor, pre_vratio, vfactor; + int ret = 0; + + ret = fimc_get_ratio_shift(src->w, dst->w, &pre_hratio, &hfactor); + if (ret) { + dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); + return ret; + } + + ret = fimc_get_ratio_shift(src->h, dst->h, &pre_vratio, &vfactor); + if (ret) { + dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); + return ret; + } + + pre_dst_width = src->w / pre_hratio; + pre_dst_height = src->h / pre_vratio; + DRM_DEBUG_KMS("%s:pre_dst_width[%d]pre_dst_height[%d]\n", __func__, + pre_dst_width, pre_dst_height); + DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", + __func__, pre_hratio, hfactor, pre_vratio, vfactor); + + sc->hratio = (src->w << 14) / (dst->w << hfactor); + sc->vratio = (src->h << 14) / (dst->h << vfactor); + sc->up_h = (dst->w >= src->w) ? true : false; + sc->up_v = (dst->h >= src->h) ? true : false; + DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", + __func__, sc->hratio, sc->vratio, sc->up_h, sc->up_v); + + shfactor = 10 - (hfactor + vfactor); + DRM_DEBUG_KMS("%s:shfactor[%d]\n", __func__, shfactor); + + cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | + EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) | + EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio)); + fimc_write(cfg, EXYNOS_CISCPRERATIO); + + cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) | + EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height)); + fimc_write(cfg, EXYNOS_CISCPREDST); + + return ret; +} + +static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) +{ + u32 cfg, cfg_ext; + + DRM_DEBUG_KMS("%s:range[%d]bypass[%d]up_h[%d]up_v[%d]\n", + __func__, sc->range, sc->bypass, sc->up_h, sc->up_v); + DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]\n", + __func__, sc->hratio, sc->vratio); + + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | + EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V | + EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK | + EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK | + EXYNOS_CISCCTRL_CSCR2Y_WIDE | + EXYNOS_CISCCTRL_CSCY2R_WIDE); + + if (sc->range) + cfg |= (EXYNOS_CISCCTRL_CSCR2Y_WIDE | + EXYNOS_CISCCTRL_CSCY2R_WIDE); + if (sc->bypass) + cfg |= EXYNOS_CISCCTRL_SCALERBYPASS; + if (sc->up_h) + cfg |= EXYNOS_CISCCTRL_SCALEUP_H; + if (sc->up_v) + cfg |= EXYNOS_CISCCTRL_SCALEUP_V; + + cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) | + EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6))); + fimc_write(cfg, EXYNOS_CISCCTRL); + + cfg_ext = fimc_read(EXYNOS_CIEXTEN); + cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK; + cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK; + cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) | + EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio)); + fimc_write(cfg_ext, EXYNOS_CIEXTEN); +} + +static int fimc_dst_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_DST]; + struct drm_exynos_pos img_pos = *pos; + struct drm_exynos_sz img_sz = *sz; + u32 cfg; + + DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* original size */ + cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | + EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); + + fimc_write(cfg, EXYNOS_ORGOSIZE); + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h); + + /* CSC ITU */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; + + if (sz->hsize >= FIMC_WIDTH_ITU_709) + cfg |= EXYNOS_CIGCTRL_CSC_ITU709; + else + cfg |= EXYNOS_CIGCTRL_CSC_ITU601; + + fimc_write(cfg, EXYNOS_CIGCTRL); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + img_sz.hsize = sz->vsize; + img_sz.vsize = sz->hsize; + } + + /* target image size */ + cfg = fimc_read(EXYNOS_CITRGFMT); + cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | + EXYNOS_CITRGFMT_TARGETV_MASK); + cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | + EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); + fimc_write(cfg, EXYNOS_CITRGFMT); + + /* target area */ + cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); + fimc_write(cfg, EXYNOS_CITAREA); + + /* ToDo: Move Scaler in this line and YUV */ + config->sz = img_sz; + config->pos = img_pos; + + /* offset Y(RGB), Cb, Cr */ + cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIOYOFF); + cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIOCBOFF); + cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | + EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); + fimc_write(cfg, EXYNOS_CIOCROFF); + + return 0; +} + +static int fimc_dst_get_buf_seq(struct fimc_context *ctx) +{ + u32 cfg, i, buf_num = 0; + u32 mask = 0x00000001; + + cfg = fimc_read(EXYNOS_CIFCNTSEQ); + + for (i = 0; i < FIMC_REG_SZ; i++) + if (cfg & (mask << i)) + buf_num++; + + DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); + + return buf_num; +} + +static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + bool enable; + u32 cfg; + u32 mask = 0x00000001 << buf_id; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + /* mask register set */ + cfg = fimc_read(EXYNOS_CIFCNTSEQ); + + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + enable = true; + break; + case IPP_BUF_CTRL_DEQUEUE: + enable = false; + break; + default: + dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); + return -EINVAL; + } + + /* sequence id */ + cfg &= (~mask); + cfg |= (enable << buf_id); + fimc_write(cfg, EXYNOS_CIFCNTSEQ); + + /* interrupt enable */ + if (buf_ctrl == IPP_BUF_CTRL_QUEUE && + fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START) + fimc_handle_irq(ctx, true, false, true); + + /* interrupt disable */ + if (buf_ctrl == IPP_BUF_CTRL_DEQUEUE && + fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP) + fimc_handle_irq(ctx, false, false, true); + + return 0; +} + +static int fimc_dst_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_DST]; + int ret; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + if (buf_id > FIMC_MAX_DST) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + case IPP_BUF_CTRL_DEQUEUE: + ret = fimc_set_planar_addr(buf_info, config->fmt, &config->sz); + + if (ret) { + dev_err(dev, "failed to set plane addr.\n"); + return ret; + } + + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + EXYNOS_CIOYSA(buf_id)); + + if (config->fmt == DRM_FORMAT_YVU420) { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIOCBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIOCRSA(buf_id)); + } else { + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + EXYNOS_CIOCBSA(buf_id)); + fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + EXYNOS_CIOCRSA(buf_id)); + } + break; + default: + /* bypass */ + break; + } + + return fimc_dst_set_buf_seq(ctx, buf_id, buf_ctrl); +} + +static struct exynos_drm_ipp_ops fimc_dst_ops = { + .set_fmt = fimc_dst_set_fmt, + .set_transf = fimc_dst_set_transf, + .set_size = fimc_dst_set_size, + .set_addr = fimc_dst_set_addr, +}; + +static int fimc_power_on(struct fimc_context *ctx, bool enable) +{ + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + if (enable) { + clk_enable(ctx->sclk_fimc_clk); + clk_enable(ctx->fimc_clk); + clk_enable(ctx->wb_clk); + /* ToDo : wb_b_clk */ + ctx->suspended = false; + } else { + clk_disable(ctx->sclk_fimc_clk); + clk_disable(ctx->fimc_clk); + clk_disable(ctx->wb_clk); + /* ToDo : wb_b_clk */ + ctx->suspended = true; + } + + return 0; +} + +static irqreturn_t fimc_irq_handler(int irq, void *dev_id) +{ + struct fimc_context *ctx = dev_id; + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + int buf_id; + u32 status1, status2; + + DRM_DEBUG_KMS("%s:fimc id[%d]\n", __func__, ctx->id); + + /* ToDo: must be remove debug routine */ + status1 = fimc_read(EXYNOS_CISTATUS); + status2 = fimc_read(EXYNOS_CISTATUS2); + DRM_DEBUG_KMS("%s:status1[0x%x]status2[0x%x]\n", + __func__, status1, status2); + + fimc_clear_irq(ctx); + if (fimc_check_ovf(ctx)) + return IRQ_NONE; + + if (!fimc_check_frame_end(ctx)) + return IRQ_NONE; + + if (list_empty(&ippdrv->event_list)) { + DRM_DEBUG_KMS("%s:event list empty. so ignore.\n", __func__); + + return IRQ_NONE; + } + + buf_id = fimc_get_buf_id(ctx); + if (buf_id < 0) + return IRQ_HANDLED; + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + + if (fimc_dst_set_buf_seq(ctx, buf_id, + IPP_BUF_CTRL_DEQUEUE) < 0) { + DRM_ERROR("failed to dequeue.\n"); + + return IRQ_HANDLED; + } + + ipp_send_event_handler(ippdrv, buf_id); + + if (fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP) + fimc_handle_irq(ctx, false, false, true); + + return IRQ_HANDLED; +} + +static int fimc_ippdrv_check_property(struct device *dev, + struct drm_exynos_ipp_property *property) +{ + /* ToDo: check valid using property information */ + DRM_DEBUG_KMS("%s\n", __func__); + + return 0; +} + +static int fimc_ippdrv_reset(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s\n", __func__); + + /* reset h/w block */ + fimc_sw_reset(ctx, false); + + /* reset scaler capability */ + memset(&ctx->sc, 0x0, sizeof(ctx->sc)); + + return 0; +} + +static int fimc_check_prepare(struct fimc_context *ctx) +{ + /* ToDo: check prepare using read register */ + DRM_DEBUG_KMS("%s\n", __func__); + + return 0; +} + +static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config; + struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; + int ret, i; + int enable = 1; + u32 cfg0, cfg1; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + ret = fimc_check_prepare(ctx); + if (ret) { + dev_err(dev, "failed to check prepare.\n"); + return ret; + } + + ippdrv->cmd = cmd; + fimc_handle_irq(ctx, true, false, true); + + /* ToDo: window size, prescaler config */ + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + config = &property->config[i]; + img_pos[i] = config->pos; + } + + ret = fimc_set_prescaler(ctx, &ctx->sc, + &img_pos[EXYNOS_DRM_OPS_SRC], + &img_pos[EXYNOS_DRM_OPS_DST]); + if (ret) { + dev_err(dev, "failed to set precalser.\n"); + return ret; + } + + /* If set ture, we can save jpeg about screen */ + fimc_handle_jpeg(ctx, false); + fimc_set_scaler(ctx, &ctx->sc); + fimc_set_polarity(ctx, &ctx->pol); + + switch (cmd) { + case IPP_CMD_M2M: + fimc_set_type_ctrl(ctx, FIMC_WB_NONE); + fimc_handle_lastend(ctx, false); + + /* setup dma */ + cfg0 = fimc_read(EXYNOS_MSCTRL); + cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; + cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; + fimc_write(cfg0, EXYNOS_MSCTRL); + break; + case IPP_CMD_WB: + fimc_set_type_ctrl(ctx, FIMC_WB_A); + fimc_handle_lastend(ctx, true); + + /* setup FIMD */ + fimc_set_camblk_fimd0_wb(ctx); + exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)enable); + break; + case IPP_CMD_OUTPUT: + default: + ret = -EINVAL; + dev_err(dev, "invalid operations.\n"); + return ret; + } + + /* Reset status */ + fimc_write(0x0, EXYNOS_CISTATUS); + + cfg0 = fimc_read(EXYNOS_CIIMGCPT); + cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC; + cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC; + + /* Scaler */ + cfg1 = fimc_read(EXYNOS_CISCCTRL); + cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK; + cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE | + EXYNOS_CISCCTRL_SCALERSTART); + + fimc_write(cfg1, EXYNOS_CISCCTRL); + + /* Enable image capture*/ + cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN; + fimc_write(cfg0, EXYNOS_CIIMGCPT); + + /* Disable frame end irq */ + cfg0 = fimc_read(EXYNOS_CIGCTRL); + cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE; + fimc_write(cfg0, EXYNOS_CIGCTRL); + + cfg0 = fimc_read(EXYNOS_CIOCTRL); + cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK; + fimc_write(cfg0, EXYNOS_CIOCTRL); + + /* ToDo: m2m start errata - refer fimd */ + if (cmd == IPP_CMD_M2M) { + cfg0 = fimc_read(EXYNOS_MSCTRL); + cfg0 |= EXYNOS_MSCTRL_ENVID; + fimc_write(cfg0, EXYNOS_MSCTRL); + + cfg0 = fimc_read(EXYNOS_MSCTRL); + cfg0 |= EXYNOS_MSCTRL_ENVID; + fimc_write(cfg0, EXYNOS_MSCTRL); + } + + return 0; +} + +static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + int enable = 0; + u32 cfg; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + switch (cmd) { + case IPP_CMD_M2M: + /* Source clear */ + cfg = fimc_read(EXYNOS_MSCTRL); + cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; + cfg &= ~EXYNOS_MSCTRL_ENVID; + fimc_write(cfg, EXYNOS_MSCTRL); + break; + case IPP_CMD_WB: + exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)enable); + break; + case IPP_CMD_OUTPUT: + default: + dev_err(dev, "invalid operations.\n"); + break; + } + + ippdrv->cmd = IPP_CMD_NONE; + fimc_handle_irq(ctx, false, false, true); + + /* reset sequence */ + fimc_write(0x0, EXYNOS_CIFCNTSEQ); + + /* Scaler disable */ + cfg = fimc_read(EXYNOS_CISCCTRL); + cfg &= ~EXYNOS_CISCCTRL_SCALERSTART; + fimc_write(cfg, EXYNOS_CISCCTRL); + + /* Disable image capture */ + cfg = fimc_read(EXYNOS_CIIMGCPT); + cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); + fimc_write(cfg, EXYNOS_CIIMGCPT); + + /* Enable frame end irq */ + cfg = fimc_read(EXYNOS_CIGCTRL); + cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE; + fimc_write(cfg, EXYNOS_CIGCTRL); +} + +static struct fimc_capability *fimc_get_capability( + enum exynos_drm_fimc_ver ver) +{ + struct fimc_capability *capa; + + DRM_DEBUG_KMS("%s:ver[0x%x]\n", __func__, ver); + + /* ToDo: version check */ + switch (ver) { + case FIMC_EXYNOS_4412: + default: + capa = fimc51_capa; + break; + } + + return capa; +} + +#ifdef CONFIG_SLP_DISP_DEBUG +static int fimc_read_reg(struct fimc_context *ctx, char *buf) +{ + u32 cfg; + int i; + int pos = 0; + + pos += sprintf(buf+pos, "0x%.8x | ", FIMC_BASE_REG(ctx->id)); + for (i = 1; i < FIMC_MAX_REG + 1; i++) { + cfg = fimc_read((i-1) * sizeof(u32)); + pos += sprintf(buf+pos, "0x%.8x ", cfg); + if (i % 4 == 0) + pos += sprintf(buf+pos, "\n0x%.8x | ", + FIMC_BASE_REG(ctx->id) + (i * sizeof(u32))); + } + + pos += sprintf(buf+pos, "\n"); + + return pos; +} + +static ssize_t show_read_reg(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + if (!ctx->regs) { + dev_err(dev, "failed to get current register.\n"); + return -EINVAL; + } + + return fimc_read_reg(ctx, buf); +} + +static struct device_attribute device_attrs[] = { + __ATTR(read_reg, S_IRUGO, show_read_reg, NULL), +}; +#endif + +static int __devinit fimc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimc_context *ctx; + struct clk *parent_clk; + struct resource *res; + struct exynos_drm_ippdrv *ippdrv; + struct exynos_drm_fimc_pdata *pdata; + int ret = -EINVAL; +#ifdef CONFIG_SLP_DISP_DEBUG + int i; +#endif + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data specified.\n"); + return -EINVAL; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + /* clock control */ + ctx->sclk_fimc_clk = clk_get(dev, "sclk_fimc"); + if (IS_ERR(ctx->sclk_fimc_clk)) { + dev_err(dev, "failed to get src fimc clock.\n"); + ret = PTR_ERR(ctx->sclk_fimc_clk); + goto err_ctx; + } + clk_enable(ctx->sclk_fimc_clk); + + ctx->fimc_clk = clk_get(dev, "fimc"); + if (IS_ERR(ctx->fimc_clk)) { + dev_err(dev, "failed to get fimc clock.\n"); + ret = PTR_ERR(ctx->fimc_clk); + clk_put(ctx->sclk_fimc_clk); + goto err_ctx; + } + + ctx->wb_clk = clk_get(dev, "pxl_async0"); + if (IS_ERR(ctx->wb_clk)) { + dev_err(dev, "failed to get writeback a clock.\n"); + ret = PTR_ERR(ctx->wb_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + goto err_ctx; + } + + ctx->wb_b_clk = clk_get(dev, "pxl_async1"); + if (IS_ERR(ctx->wb_b_clk)) { + dev_err(dev, "failed to get writeback b clock.\n"); + ret = PTR_ERR(ctx->wb_b_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + goto err_ctx; + } + + if (pdata->ver == FIMC_EXYNOS_4212 || + pdata->ver == FIMC_EXYNOS_4412) + parent_clk = clk_get(dev, "mout_mpll_user"); + else + parent_clk = clk_get(dev, "mout_mpll"); + + if (IS_ERR(parent_clk)) { + dev_err(dev, "failed to get parent clock.\n"); + ret = PTR_ERR(parent_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); + goto err_ctx; + } + + if (clk_set_parent(ctx->sclk_fimc_clk, parent_clk)) { + dev_err(dev, "failed to set parent.\n"); + clk_put(parent_clk); + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); + goto err_ctx; + } + clk_put(parent_clk); + clk_set_rate(ctx->sclk_fimc_clk, FIMC_CLK_RATE); + clk_disable(ctx->sclk_fimc_clk); + + /* resource memory */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to find registers.\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!ctx->regs_res) { + dev_err(dev, "failed to claim register region.\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs = ioremap(res->start, resource_size(res)); + if (!ctx->regs) { + dev_err(dev, "failed to map registers.\n"); + ret = -ENXIO; + goto err_req_region; + } + + /* resource irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to request irq resource.\n"); + goto err_get_regs; + } + + ctx->irq = res->start; + ret = request_threaded_irq(ctx->irq, NULL, fimc_irq_handler, + IRQF_ONESHOT, "drm_fimc", ctx); + if (ret < 0) { + dev_err(dev, "failed to request irq.\n"); + goto err_get_regs; + } + + /* context initailization */ + ctx->ver = pdata->ver; + ctx->id = pdev->id; + ctx->capa = fimc_get_capability(ctx->ver); + if (!ctx->capa) { + dev_err(dev, "failed to get capability.\n"); + goto err_get_irq; + } + ctx->pol = pdata->pol; + +#ifdef CONFIG_SLP_DISP_DEBUG + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { + ret = device_create_file(&(pdev->dev), + &device_attrs[i]); + if (ret) + break; + } + + if (ret < 0) + dev_err(&pdev->dev, "failed to add sysfs entries\n"); +#endif + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + ippdrv = &ctx->ippdrv; + ippdrv->dev = dev; + ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops; + ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops; + ippdrv->check_property = fimc_ippdrv_check_property; + ippdrv->reset = fimc_ippdrv_reset; + ippdrv->start = fimc_ippdrv_start; + ippdrv->stop = fimc_ippdrv_stop; + + mutex_init(&ctx->lock); + platform_set_drvdata(pdev, ctx); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = exynos_drm_ippdrv_register(ippdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm fimc device.\n"); + goto err_get_irq; + } + + dev_info(&pdev->dev, "drm fimc registered successfully.\n"); + + return 0; + +err_get_irq: + free_irq(ctx->irq, ctx); +err_get_regs: + iounmap(ctx->regs); +err_req_region: + release_resource(ctx->regs_res); + kfree(ctx->regs_res); +err_clk: + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); +err_ctx: + kfree(ctx); + return ret; +} + +static int __devexit fimc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimc_context *ctx = get_fimc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + + exynos_drm_ippdrv_unregister(ippdrv); + + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + free_irq(ctx->irq, ctx); + iounmap(ctx->regs); + release_resource(ctx->regs_res); + kfree(ctx->regs_res); + + clk_put(ctx->sclk_fimc_clk); + clk_put(ctx->fimc_clk); + clk_put(ctx->wb_clk); + clk_put(ctx->wb_b_clk); + + kfree(ctx); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_suspend(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + if (pm_runtime_suspended(dev)) + return 0; + /* ToDo */ + return fimc_power_on(ctx, false); +} + +static int fimc_resume(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + if (!pm_runtime_suspended(dev)) + return fimc_power_on(ctx, true); + /* ToDo */ + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int fimc_runtime_suspend(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + /* ToDo */ + return fimc_power_on(ctx, false); +} + +static int fimc_runtime_resume(struct device *dev) +{ + struct fimc_context *ctx = get_fimc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + /* ToDo */ + return fimc_power_on(ctx, true); +} +#endif + +static const struct dev_pm_ops fimc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) + SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) +}; + +/* ToDo: need to check use case platform_device_id */ +struct platform_driver fimc_driver = { + .probe = fimc_probe, + .remove = __devexit_p(fimc_remove), + .driver = { + .name = "exynos-drm-fimc", + .owner = THIS_MODULE, + .pm = &fimc_pm_ops, + }, +}; + diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_vidi.h b/drivers/gpu/drm/exynos/exynos_drm_fimc.h index a4babe4..e631774 100644 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_vidi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.h @@ -1,7 +1,8 @@ -/* exynos_drm_vidi.h - * +/* * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> + * + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,14 +24,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef _EXYNOS_DRM_VIDI_H_ -#define _EXYNOS_DRM_VIDI_H_ +#ifndef _EXYNOS_DRM_FIMC_H_ +#define _EXYNOS_DRM_FIMC_H_ + +/* ToDo */ -#ifdef CONFIG_DRM_EXYNOS_VIDI -int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv); -#else -#define vidi_connection_ioctl NULL -#endif +#endif /* _EXYNOS_DRM_FIMC_H_ */ -#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c deleted file mode 100644 index 823bfb0..0000000 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ /dev/null @@ -1,1543 +0,0 @@ -/* exynos_drm_fimd.c - * - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Joonyoung Shim <jy0922.shim@samsung.com> - * Inki Dae <inki.dae@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ -#include "drmP.h" -#include "drm_backlight.h" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/cma.h> - -#include <drm/exynos_drm.h> -#include <plat/regs-fb-v4.h> - -#include <plat/fimd_lite_ext.h> - -#include <mach/map.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_fbdev.h" -#include "exynos_drm_crtc.h" - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ -#include <linux/devfreq/exynos4_display.h> -#endif - -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB -#include <plat/fimc.h> -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ -#include <plat/pd.h> -#include <linux/pm_qos_params.h> -#endif -#define FIMD_GET_LCD_WIDTH _IOR('F', 302, int) -#define FIMD_GET_LCD_HEIGHT _IOR('F', 303, int) -#define FIMD_SET_WRITEBACK _IOW('F', 304, u32) -#endif - -/* - * FIMD is stand for Fully Interactive Mobile Display and - * as a display controller, it transfers contents drawn on memory - * to a LCD Panel through Display Interfaces such as RGB or - * CPU Interface. - */ - -/* position control register for hardware window 0, 2 ~ 4.*/ -#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) -#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) -/* size control register for hardware window 0. */ -#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08) -/* alpha control register for hardware window 1 ~ 4. */ -#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16) -/* size control register for hardware window 1 ~ 4. */ -#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) - -#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) -#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) -#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) - -/* color key control register for hardware window 1 ~ 4. */ -#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8)) -/* color key value register for hardware window 1 ~ 4. */ -#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8)) - -/* FIMD has totally five hardware windows. */ -#define WINDOWS_NR 5 - -#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) - -static struct s5p_fimd_ext_device *fimd_lite_dev, *mdnie; -static struct s5p_fimd_dynamic_refresh *fimd_refresh; - -struct fimd_notifier_block { - struct list_head list; - void *data; - int (*client_notifier)(unsigned int val, void *data); -}; - -static LIST_HEAD(fimd_notifier_list); -static DEFINE_MUTEX(fimd_notifier_lock); - -struct fimd_win_data { - unsigned int offset_x; - unsigned int offset_y; - unsigned int ovl_width; - unsigned int ovl_height; - unsigned int fb_width; - unsigned int fb_height; - unsigned int bpp; - dma_addr_t dma_addr; - void __iomem *vaddr; - unsigned int buf_offsize; - unsigned int line_size; /* bytes */ - bool enabled; -}; - -struct fimd_context { - struct exynos_drm_subdrv subdrv; - int irq; - struct drm_crtc *crtc; - struct clk *bus_clk; - struct clk *lcd_clk; - struct resource *regs_res; - void __iomem *regs; - struct fimd_win_data win_data[WINDOWS_NR]; - unsigned int clkdiv; - unsigned int default_win; - unsigned long irq_flags; - u32 vidcon0; - u32 vidcon1; - bool suspended; - struct mutex lock; - - struct exynos_drm_panel_info *panel; - unsigned int high_freq; - unsigned int dynamic_refresh; - struct notifier_block nb_exynos_display; - - struct work_struct work; - bool errata; -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB - struct notifier_block nb_ctrl; -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - struct pm_qos_request_list pm_qos; -#endif -#endif -}; - -static bool fimd_display_is_connected(struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ - - return true; -} - -static void *fimd_get_panel(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return ctx->panel; -} - -static int fimd_check_timing(struct device *dev, void *timing) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ - - return 0; -} - -static int fimd_display_power_on(struct device *dev, int mode) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO */ - - drm_bl_dpms(mode); - - return 0; -} - -static struct exynos_drm_display_ops fimd_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_LCD, - .is_connected = fimd_display_is_connected, - .get_panel = fimd_get_panel, - .check_timing = fimd_check_timing, - .power_on = fimd_display_power_on, -}; - -static void exynos_drm_mdnie_mode_stop(struct fimd_context *ctx) -{ - struct s5p_fimd_ext_driver *fimd_lite_drv; - u32 cfg; - - fimd_lite_drv = to_fimd_ext_driver(fimd_lite_dev->dev.driver); - - /* set dualrgb register to mDNIe mode. */ - cfg = readl(ctx->regs + DUALRGB); - cfg &= ~(0x3 << 0); - writel(cfg, ctx->regs + DUALRGB); - msleep(20); - - /* change display path. */ - cfg = readl(S3C_VA_SYS + 0x210); - cfg |= 1 << 1; - writel(cfg, S3C_VA_SYS + 0x210); - - if (fimd_lite_drv->stop) - fimd_lite_drv->stop(fimd_lite_dev); - - if (fimd_lite_drv->setup) - fimd_lite_drv->setup(fimd_lite_dev, 0); - - /* clock off */ - if (fimd_lite_drv->power_off) - fimd_lite_drv->power_off(fimd_lite_dev); - - fimd_lite_dev->enabled = false; -} - -static void exynos_drm_set_mdnie_mode(struct fimd_context *ctx) -{ - u32 cfg; - - /* change display path. */ - cfg = readl(S3C_VA_SYS + 0x210); - /* MIE_LBLK0 is mDNIe. */ - cfg |= 1 << 0; - /* FIMDBYPASS_LBLK0 is MIE/mDNIe. */ - cfg &= ~(1 << 1); - writel(cfg, S3C_VA_SYS + 0x210); - - /* all polarity values should be 0 for mDNIe. */ - cfg = readl(ctx->regs + VIDCON1); - cfg &= ~(VIDCON1_INV_VCLK | VIDCON1_INV_HSYNC | - VIDCON1_INV_VSYNC | VIDCON1_INV_VDEN | - VIDCON1_VCLK_MASK); - - writel(cfg, ctx->regs + VIDCON1); - - /* set dualrgb register to mDNIe mode. */ - cfg = readl(ctx->regs + DUALRGB); - cfg &= ~(0x3 << 0); - cfg |= 0x3 << 0; - writel(cfg, ctx->regs + DUALRGB); -} - -static int exynos_drm_change_to_mdnie(struct fimd_context *ctx) -{ - u32 cfg; - struct s5p_fimd_ext_driver *mdnie_drv, *fimd_lite_drv; - - mdnie_drv = to_fimd_ext_driver(mdnie->dev.driver); - fimd_lite_drv = to_fimd_ext_driver(fimd_lite_dev->dev.driver); - - /** - * path change sequence for mDNIe. - * - * 1. FIMD-LITE DMA stop. - * 2. FIMD DMA stop. - * 3. change DISPLAY_CONTROL and DUALRGB registers to mDNIe mode. - * 4. change FIMD VCLKFREE to freerun mode. - * 5. initialize mDNIe module. - * 6. initialize FIMD-LITE module. - * 7. FIMD-LITE logic start. - * 8. FIMD-LITE DMA start. - * 9. FIMD DMA start. - * - * ps. FIMD polarity values should be 0. - * lcd polarity values should be set to FIMD-LITE. - * FIMD and FIMD-LITE DMA should be started at same time. - */ - /* set fimd to mDNIe mode.(WB/mDNIe) */ - exynos_drm_set_mdnie_mode(ctx); - - /* enable FIMD-LITE. clk */ - if (fimd_lite_drv && fimd_lite_drv->power_on) - fimd_lite_drv->power_on(fimd_lite_dev); - - /* setup mDNIe. */ - if (mdnie_drv) - mdnie_drv->setup(mdnie, 1); - - /* setup FIMD-LITE. */ - if (fimd_lite_drv) - fimd_lite_drv->setup(fimd_lite_dev, 1); - - cfg = readl(ctx->regs + VIDCON0); - cfg |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(cfg, ctx->regs + VIDCON0); - - if (fimd_lite_drv->start) - fimd_lite_drv->start(fimd_lite_dev); - return 0; -} - -static void fimd_dpms(struct device *subdrv_dev, int mode) -{ - struct fimd_context *ctx = get_fimd_context(subdrv_dev); - - DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); - - mutex_lock(&ctx->lock); - - switch (mode) { - case DRM_MODE_DPMS_ON: - /* - * enable fimd hardware only if suspended status. - * - * P.S. fimd_dpms function would be called at booting time so - * clk_enable could be called double time. - */ - if (ctx->suspended) - pm_runtime_get_sync(subdrv_dev); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - if (!ctx->suspended) - pm_runtime_put_sync(subdrv_dev); - break; - default: - DRM_DEBUG_KMS("unspecified mode %d\n", mode); - break; - } - - mutex_unlock(&ctx->lock); -} - -static void fimd_apply(struct device *subdrv_dev) -{ - struct fimd_context *ctx = get_fimd_context(subdrv_dev); - struct exynos_drm_manager *mgr = ctx->subdrv.manager; - struct exynos_drm_manager_ops *mgr_ops = mgr->ops; - struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; - struct fimd_win_data *win_data; - int i; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - for (i = 0; i < WINDOWS_NR; i++) { - win_data = &ctx->win_data[i]; - if (win_data->enabled && (ovl_ops && ovl_ops->commit)) - ovl_ops->commit(subdrv_dev, i); - } - - if (mgr_ops && mgr_ops->commit) - mgr_ops->commit(subdrv_dev); -} - -static void fimd_commit(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - u32 val; - - if (ctx->suspended) - return; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* setup polarity values from machine code. */ - writel(ctx->vidcon1, ctx->regs + VIDCON1); - - /* setup vertical timing values. */ - val = VIDTCON0_VBPD(timing->upper_margin - 1) | - VIDTCON0_VFPD(timing->lower_margin - 1) | - VIDTCON0_VSPW(timing->vsync_len - 1); - writel(val, ctx->regs + VIDTCON0); - - /* setup horizontal timing values. */ - val = VIDTCON1_HBPD(timing->left_margin - 1) | - VIDTCON1_HFPD(timing->right_margin - 1) | - VIDTCON1_HSPW(timing->hsync_len - 1); - writel(val, ctx->regs + VIDTCON1); - - /* setup horizontal and vertical display size. */ - val = VIDTCON2_LINEVAL(timing->yres - 1) | - VIDTCON2_HOZVAL(timing->xres - 1); - writel(val, ctx->regs + VIDTCON2); - - /* setup clock source, clock divider, enable dma. */ - val = ctx->vidcon0; - val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (ctx->clkdiv > 1) - val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; - else - val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* - * fields of register with prefix '_F' would be updated - * at vsync(same as dma start) - */ - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(val, ctx->regs + VIDCON0); - - /* - * fix fimd errata with mDNIe. - * - * this code fixes a issue that mDNIe unfunctions properly - * when fimd power off goes to on. this issue is because dma - * is enabled two times with setcrtc call once a process is - * ternimated(at this thime, fimd goes to on from off for back - * to console fb) so this condition would avoid the situation. - */ - if (!ctx->errata) { - /* - * Workaround: After power domain is turned off then - * when it is turned on, this needs. - */ - val &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); - writel(val, ctx->regs + VIDCON0); - - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(val, ctx->regs + VIDCON0); - - ctx->errata = true; - } -} - -static int fimd_enable_vblank(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return -EPERM; - - if (!test_and_set_bit(0, &ctx->irq_flags)) { - val = readl(ctx->regs + VIDINTCON0); - - val |= VIDINTCON0_INT_ENABLE; - val |= VIDINTCON0_INT_FRAME; - - val &= ~VIDINTCON0_FRAMESEL0_MASK; - val |= VIDINTCON0_FRAMESEL0_VSYNC; - val &= ~VIDINTCON0_FRAMESEL1_MASK; - val |= VIDINTCON0_FRAMESEL1_NONE; - - writel(val, ctx->regs + VIDINTCON0); - } - - return 0; -} - -static void fimd_disable_vblank(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; - - if (test_and_clear_bit(0, &ctx->irq_flags)) { - val = readl(ctx->regs + VIDINTCON0); - - val &= ~VIDINTCON0_INT_FRAME; - val &= ~VIDINTCON0_INT_ENABLE; - - writel(val, ctx->regs + VIDINTCON0); - } -} - -static struct exynos_drm_manager_ops fimd_manager_ops = { - .dpms = fimd_dpms, - .apply = fimd_apply, - .commit = fimd_commit, - .enable_vblank = fimd_enable_vblank, - .disable_vblank = fimd_disable_vblank, -}; - -static void fimd_win_mode_set(struct device *dev, - struct exynos_drm_overlay *overlay) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data; - int win; - unsigned long offset; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!overlay) { - dev_err(dev, "overlay is NULL\n"); - return; - } - - win = overlay->zpos; - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - offset = overlay->fb_x * (overlay->bpp >> 3); - offset += overlay->fb_y * overlay->pitch; - - DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); - - win_data = &ctx->win_data[win]; - - win_data->offset_x = overlay->crtc_x; - win_data->offset_y = overlay->crtc_y; - win_data->ovl_width = overlay->crtc_width; - win_data->ovl_height = overlay->crtc_height; - win_data->fb_width = overlay->fb_width; - win_data->fb_height = overlay->fb_height; - win_data->dma_addr = overlay->dma_addr[0] + offset; - win_data->vaddr = overlay->vaddr[0] + offset; - win_data->bpp = overlay->bpp; - win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * - (overlay->bpp >> 3); - win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); - - DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", - win_data->offset_x, win_data->offset_y); - DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - win_data->ovl_width, win_data->ovl_height); - DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", - (unsigned long)win_data->dma_addr, - (unsigned long)win_data->vaddr); - DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", - overlay->fb_width, overlay->crtc_width); -} - -static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data = &ctx->win_data[win]; - unsigned long val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - val = WINCONx_ENWIN; - - switch (win_data->bpp) { - case 1: - val |= WINCON0_BPPMODE_1BPP; - val |= WINCONx_BITSWP; - val |= WINCONx_BURSTLEN_4WORD; - break; - case 2: - val |= WINCON0_BPPMODE_2BPP; - val |= WINCONx_BITSWP; - val |= WINCONx_BURSTLEN_8WORD; - break; - case 4: - val |= WINCON0_BPPMODE_4BPP; - val |= WINCONx_BITSWP; - val |= WINCONx_BURSTLEN_8WORD; - break; - case 8: - val |= WINCON0_BPPMODE_8BPP_PALETTE; - val |= WINCONx_BURSTLEN_8WORD; - val |= WINCONx_BYTSWP; - break; - case 16: - val |= WINCON0_BPPMODE_16BPP_565; - val |= WINCONx_HAWSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - case 24: - val |= WINCON0_BPPMODE_24BPP_888; - val |= WINCONx_WSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - case 32: - val |= WINCON1_BPPMODE_28BPP_A4888 - | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; - val |= WINCONx_WSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - default: - DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); - - val |= WINCON0_BPPMODE_24BPP_888; - val |= WINCONx_WSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - } - - DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); - - writel(val, ctx->regs + WINCON(win)); -} - -static void fimd_win_set_colkey(struct device *dev, unsigned int win) -{ - struct fimd_context *ctx = get_fimd_context(dev); - unsigned int keycon0 = 0, keycon1 = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | - WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); - - keycon1 = WxKEYCON1_COLVAL(0xffffffff); - - writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); - writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); -} - -static void fimd_win_commit(struct device *dev, int zpos) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data; - int win = zpos; - unsigned long val, alpha, size; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; - - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - win_data = &ctx->win_data[win]; - - /* - * SHADOWCON register is used for enabling timing. - * - * for example, once only width value of a register is set, - * if the dma is started then fimd hardware could malfunction so - * with protect window setting, the register fields with prefix '_F' - * wouldn't be updated at vsync also but updated once unprotect window - * is set. - */ - - /* protect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - /* buffer start address */ - val = (unsigned long)win_data->dma_addr; - writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); - - /* buffer end address */ - size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); - val = (unsigned long)(win_data->dma_addr + size); - writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); - - DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", - (unsigned long)win_data->dma_addr, val, size); - DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - win_data->ovl_width, win_data->ovl_height); - - /* buffer size */ - val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | - VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size); - writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); - - /* OSD position */ - val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | - VIDOSDxA_TOPLEFT_Y(win_data->offset_y); - writel(val, ctx->regs + VIDOSD_A(win)); - - val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x + - win_data->ovl_width - 1) | - VIDOSDxB_BOTRIGHT_Y(win_data->offset_y + - win_data->ovl_height - 1); - writel(val, ctx->regs + VIDOSD_B(win)); - - DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", - win_data->offset_x, win_data->offset_y, - win_data->offset_x + win_data->ovl_width - 1, - win_data->offset_y + win_data->ovl_height - 1); - - /* hardware window 0 doesn't support alpha channel. */ - if (win != 0) { - /* OSD alpha */ - alpha = VIDISD14C_ALPHA1_R(0xf) | - VIDISD14C_ALPHA1_G(0xf) | - VIDISD14C_ALPHA1_B(0xf); - - writel(alpha, ctx->regs + VIDOSD_C(win)); - } - - /* OSD size */ - if (win != 3 && win != 4) { - u32 offset = VIDOSD_D(win); - if (win == 0) - offset = VIDOSD_C_SIZE_W0; - val = win_data->ovl_width * win_data->ovl_height; - writel(val, ctx->regs + offset); - - DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); - } - - fimd_win_set_pixfmt(dev, win); - - /* hardware window 0 doesn't support color key. */ - if (win != 0) - fimd_win_set_colkey(dev, win); - - /* wincon */ - val = readl(ctx->regs + WINCON(win)); - val |= WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); - - /* Enable DMA channel and unprotect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_CHx_ENABLE(win); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - win_data->enabled = true; -} - -static void fimd_win_disable(struct device *dev, int zpos) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data; - int win = zpos; - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - win_data = &ctx->win_data[win]; - - /* protect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - /* wincon */ - val = readl(ctx->regs + WINCON(win)); - val &= ~WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); - - /* unprotect windows */ - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_CHx_ENABLE(win); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - win_data->enabled = false; -} - -static struct exynos_drm_overlay_ops fimd_overlay_ops = { - .mode_set = fimd_win_mode_set, - .commit = fimd_win_commit, - .disable = fimd_win_disable, -}; - -static struct exynos_drm_manager fimd_manager = { - .pipe = -1, - .ops = &fimd_manager_ops, - .overlay_ops = &fimd_overlay_ops, - .display_ops = &fimd_display_ops, -}; - -static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) -{ - struct exynos_drm_private *dev_priv = drm_dev->dev_private; - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned long flags; - bool is_checked = false; - - spin_lock_irqsave(&drm_dev->event_lock, flags); - - list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, - base.link) { - /* if event's pipe isn't same as crtc then ignore it. */ - if (crtc != e->pipe) - continue; - - is_checked = true; - - do_gettimeofday(&now); - e->event.sequence = 0; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; - - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - } - - if (is_checked) { - /* - * call drm_vblank_put only in case that drm_vblank_get was - * called. - */ - if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) - drm_vblank_put(drm_dev, crtc); - - /* - * don't off vblank if vblank_disable_allowed is 1, - * because vblank would be off by timer handler. - */ - if (!drm_dev->vblank_disable_allowed) - drm_vblank_off(drm_dev, crtc); - } - - spin_unlock_irqrestore(&drm_dev->event_lock, flags); -} - -static void exynos_fimd_schedule_work(struct work_struct *work) -{ - struct fimd_context *ctx = container_of(work, struct fimd_context, - work); - u32 ret; - - /* Change mdnie mode after irq handler */ - if (mdnie && fimd_lite_dev) { - if (!fimd_lite_dev->enabled) { - while (1) { - ret = (__raw_readl(ctx->regs + VIDCON1)) & - VIDCON1_VSTATUS_MASK; - if (ret == VIDCON1_VSTATUS_BACKPORCH) { - exynos_drm_change_to_mdnie(ctx); - fimd_lite_dev->enabled = true; - break; - } - } - } - } - -} - -static irqreturn_t fimd_irq_handler(int irq, void *dev_id) -{ - struct fimd_context *ctx = (struct fimd_context *)dev_id; - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct drm_device *drm_dev = subdrv->drm_dev; - struct exynos_drm_manager *manager = subdrv->manager; - u32 val; - - val = readl(ctx->regs + VIDINTCON1); - - if (val & VIDINTCON1_INT_FRAME) - /* VSYNC interrupt */ - writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); - - /* check the crtc is detached already from encoder */ - if (manager->pipe < 0) - goto out; - - drm_handle_vblank(drm_dev, manager->pipe); - fimd_finish_pageflip(drm_dev, manager->pipe); - - schedule_work(&ctx->work); - -out: - return IRQ_HANDLED; -} - -static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * enable drm irq mode. - * - with irq_enabled = 1, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - drm_dev->irq_enabled = 1; - - /* - * with vblank_disable_allowed = 1, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) - */ - drm_dev->vblank_disable_allowed = 1; - - return 0; -} - -static void fimd_subdrv_remove(struct drm_device *drm_dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ -} - -static int fimd_calc_clkdiv(struct fimd_context *ctx, - struct fb_videomode *timing) -{ - unsigned long clk = clk_get_rate(ctx->lcd_clk); - u32 retrace; - u32 clkdiv; - u32 best_framerate = 0; - u32 framerate; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - retrace = timing->left_margin + timing->hsync_len + - timing->right_margin + timing->xres; - retrace *= timing->upper_margin + timing->vsync_len + - timing->lower_margin + timing->yres; - - /* default framerate is 60Hz */ - if (!timing->refresh) - timing->refresh = 60; - - clk /= retrace; - - for (clkdiv = 1; clkdiv < 0x100; clkdiv++) { - int tmp; - - /* get best framerate */ - framerate = clk / clkdiv; - tmp = timing->refresh - framerate; - if (tmp < 0) { - best_framerate = framerate; - continue; - } else { - if (!best_framerate) - best_framerate = framerate; - else if (tmp < (best_framerate - framerate)) - best_framerate = framerate; - break; - } - } - - return clkdiv; -} - -static void fimd_clear_win(struct fimd_context *ctx, int win) -{ - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - writel(0, ctx->regs + WINCON(win)); - writel(0, ctx->regs + VIDOSD_A(win)); - writel(0, ctx->regs + VIDOSD_B(win)); - writel(0, ctx->regs + VIDOSD_C(win)); - - if (win == 1 || win == 2) - writel(0, ctx->regs + VIDOSD_D(win)); - - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); -} - -int fimd_register_client(int (*client_notifier)(unsigned int val, void *data), - void *data) -{ - struct fimd_notifier_block *fimd_block; - - fimd_block = kzalloc(sizeof(*fimd_block), GFP_KERNEL); - if (!fimd_block) { - printk(KERN_ERR "failed to allocate fimd_notifier_block\n"); - return -ENOMEM; - } - - fimd_block->client_notifier = client_notifier; - fimd_block->data = data; - - mutex_lock(&fimd_notifier_lock); - list_add_tail(&fimd_block->list, &fimd_notifier_list); - mutex_unlock(&fimd_notifier_lock); - - return 0; -} -EXPORT_SYMBOL(fimd_register_client); - -void fimd_unregister_client(int (*client_notifier)(unsigned int val, - void *data)) -{ - struct fimd_notifier_block *fimd_block; - - mutex_lock(&fimd_notifier_lock); - list_for_each_entry(fimd_block, &fimd_notifier_list, list) { - if (!fimd_block) - continue; - - if (fimd_block->client_notifier == client_notifier) { - list_del(&fimd_block->list); - kfree(fimd_block); - fimd_block = NULL; - break; - } - } - mutex_unlock(&fimd_notifier_lock); -} -EXPORT_SYMBOL(fimd_unregister_client); - -static int fimd_notifier_call_chain(void) -{ - struct fimd_notifier_block *fimd_block; - - mutex_lock(&fimd_notifier_lock); - list_for_each_entry(fimd_block, &fimd_notifier_list, list) { - if (fimd_block && fimd_block->client_notifier) - fimd_block->client_notifier(0, fimd_block->data); - } - mutex_unlock(&fimd_notifier_lock); - - return 0; -} - -static int fimd_power_on(struct fimd_context *ctx, bool enable) -{ - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->dev; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (enable != false && enable != true) - return -EINVAL; - - if (enable) { - int ret; - - /* fimd power should be off to clear mipi-dsi fifo. */ - fimd_notifier_call_chain(); - - ret = clk_enable(ctx->bus_clk); - if (ret < 0) - return ret; - - ret = clk_enable(ctx->lcd_clk); - if (ret < 0) { - clk_disable(ctx->bus_clk); - return ret; - } - - ctx->suspended = false; - - /* if vblank was enabled status, enable it again. */ - if (test_and_clear_bit(0, &ctx->irq_flags)) - fimd_enable_vblank(dev); - - fimd_apply(dev); - } else { - if (fimd_lite_dev) - exynos_drm_mdnie_mode_stop(ctx); - - clk_disable(ctx->lcd_clk); - clk_disable(ctx->bus_clk); - - ctx->suspended = true; - ctx->errata = false; - } - - return 0; -} - -static void exynos_drm_change_clock(struct fimd_context *ctx) -{ - unsigned int cfg = 0; - struct s5p_fimd_ext_driver *fimd_lite_drv; - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - - fimd_lite_drv = to_fimd_ext_driver(fimd_lite_dev->dev.driver); - - if (!ctx->dynamic_refresh) { - timing->refresh = 60; - ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); -#ifdef CONFIG_LCD_S6E8AA0 - /* workaround: To apply dynamic refresh rate */ - s6e8aa0_panel_cond(1); -#endif - if (fimd_lite_dev && fimd_lite_dev->enabled) { - fimd_refresh->clkdiv = ctx->clkdiv; - fimd_lite_drv->change_clock(fimd_refresh, - fimd_lite_dev); - } else { - cfg = readl(ctx->regs + VIDCON0); - cfg &= ~VIDCON0_CLKVAL_F(0xFF); - cfg |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1); - writel(cfg, ctx->regs + VIDCON0); - } - } else { - ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); -#ifdef CONFIG_LCD_S6E8AA0 - /* workaround: To apply dynamic refresh rate */ - s6e8aa0_panel_cond(ctx->high_freq); -#endif - if (fimd_lite_dev && fimd_lite_dev->enabled) { - fimd_refresh->clkdiv = ctx->clkdiv; - fimd_lite_drv->change_clock(fimd_refresh, - fimd_lite_dev); - } else { - cfg = readl(ctx->regs + VIDCON0); - cfg &= ~VIDCON0_CLKVAL_F(0xFF); - cfg |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1); - writel(cfg, ctx->regs + VIDCON0); - } - } -} - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ -static int exynos_display_notifier_callback(struct notifier_block *this, - unsigned long event, void *_data) -{ - struct fimd_context *ctx - = container_of(this, struct fimd_context, nb_exynos_display); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - - if (ctx->suspended) - return NOTIFY_DONE; - - switch (event) { - case EXYNOS4_DISPLAY_LV_HF: - timing->refresh = EXYNOS4_DISPLAY_LV_HF; - ctx->high_freq = 1; - break; - case EXYNOS4_DISPLAY_LV_LF: - timing->refresh = EXYNOS4_DISPLAY_LV_LF; - ctx->high_freq = 0; - break; - default: - return NOTIFY_BAD; - } - - exynos_drm_change_clock(ctx); - - return NOTIFY_DONE; -} -#endif - -static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - unsigned long refresh; - int ret; - - if (ctx->dynamic_refresh) { - ret = kstrtoul(buf, 0, &refresh); - timing->refresh = refresh; - if (refresh == 60) - ctx->high_freq = 1; - else - ctx->high_freq = 0; - - exynos_drm_change_clock(ctx); - } - - return count; -} - -static ssize_t show_refresh(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - - return snprintf(buf, PAGE_SIZE, "%d\n", timing->refresh); -} - -static struct device_attribute device_attrs[] = { - __ATTR(refresh, S_IRUGO|S_IWUSR, show_refresh, store_refresh), -}; - -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB -static void fimd_set_writeback(struct fimd_context *ctx, int enable) -{ - u32 vidcon0 = readl(ctx->regs + VIDCON0); - u32 vidcon2 = readl(ctx->regs + VIDCON2); - - vidcon0 &= ~VIDCON0_VIDOUT_MASK; - vidcon2 &= ~(VIDCON2_WB_MASK | - VIDCON2_WB_SKIP_MASK | - VIDCON2_TVFORMATSEL_HW_SW_MASK | - VIDCON2_TVFORMATSEL_MASK); - - if (enable) { - vidcon0 |= VIDCON0_VIDOUT_WB; - vidcon2 |= (VIDCON2_WB_ENABLE | - VIDCON2_TVFORMATSEL_SW | - VIDCON2_TVFORMATSEL_YUV444); - } else { - vidcon0 |= VIDCON0_VIDOUT_RGB; - vidcon2 |= VIDCON2_WB_DISABLE; - } - - writel(vidcon0, ctx->regs + VIDCON0); - writel(vidcon2, ctx->regs + VIDCON2); -} - -static int fimd_notifier_ctrl(struct notifier_block *this, - unsigned long event, void *_data) -{ - struct fimd_context *ctx = container_of(this, - struct fimd_context, nb_ctrl); - - switch (event) { - case FIMD_GET_LCD_WIDTH: { - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - int *width = (int *)_data; - - *width = timing->xres; - } - break; - case FIMD_GET_LCD_HEIGHT: { - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - int *height = (int *)_data; - - *height = timing->yres; - } - break; - case FIMD_SET_WRITEBACK: { - unsigned int refresh; - int *enable = (int *)&_data; - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - if (*enable) - refresh = EXYNOS4_DISPLAY_LV_HF; - else - refresh = EXYNOS4_DISPLAY_LV_LF; - pm_qos_update_request(&ctx->pm_qos, - refresh); -#endif - fimd_set_writeback(ctx, *enable); - } - break; - default: - /* ToDo : for checking use case */ - DRM_INFO("%s:event[0x%x]\n", __func__, (unsigned int)event); - break; - } - - return NOTIFY_DONE; -} -#endif - -static int __devinit fimd_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct fimd_context *ctx; - struct exynos_drm_subdrv *subdrv; - struct exynos_drm_fimd_pdata *pdata; - struct exynos_drm_panel_info *panel; - struct resource *res; - int win; - int i; - int ret = -EINVAL; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - - panel = &pdata->panel; - if (!panel) { - dev_err(dev, "panel is null.\n"); - return -EINVAL; - } - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->bus_clk = clk_get(dev, "lcd"); - if (IS_ERR(ctx->bus_clk)) { - dev_err(dev, "failed to get bus clock\n"); - ret = PTR_ERR(ctx->bus_clk); - goto err_clk_get; - } - - clk_enable(ctx->bus_clk); - - ctx->lcd_clk = clk_get(dev, "sclk_fimd"); - if (IS_ERR(ctx->lcd_clk)) { - dev_err(dev, "failed to get lcd clock\n"); - ret = PTR_ERR(ctx->lcd_clk); - goto err_bus_clk; - } - - clk_enable(ctx->lcd_clk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "failed to find registers\n"); - ret = -ENOENT; - goto err_clk; - } - - ctx->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!ctx->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_clk; - } - - ctx->regs = ioremap(res->start, resource_size(res)); - if (!ctx->regs) { - dev_err(dev, "failed to map registers\n"); - ret = -ENXIO; - goto err_req_region_io; - } - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "irq request failed.\n"); - goto err_get_resource; - } - - ctx->irq = res->start; - - ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); - if (ret < 0) { - dev_err(dev, "irq request failed.\n"); - goto err_get_resource; - } - - ctx->clkdiv = fimd_calc_clkdiv(ctx, &panel->timing); - ctx->vidcon0 = pdata->vidcon0; - ctx->vidcon1 = pdata->vidcon1; - ctx->default_win = pdata->default_win; - ctx->dynamic_refresh = pdata->dynamic_refresh; - ctx->panel = panel; - - INIT_WORK(&ctx->work, exynos_fimd_schedule_work); - - panel->timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; - - DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", - panel->timing.pixclock, ctx->clkdiv); - - /* mdnie support. */ - mdnie = s5p_fimd_ext_find_device("mdnie"); - fimd_lite_dev = s5p_fimd_ext_find_device("fimd_lite"); - if (mdnie && fimd_lite_dev) { - fimd_refresh = kzalloc(sizeof(*fimd_refresh), GFP_KERNEL); - if (!fimd_refresh) { - dev_err(dev, "failed to allocate fimd_refresh.\n"); - ret = -ENOMEM; - goto err_alloc_fail; - } - - fimd_refresh->dynamic_refresh = pdata->dynamic_refresh; - fimd_refresh->regs = ctx->regs; - fimd_refresh->clkdiv = ctx->clkdiv; - } - - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { - ret = device_create_file(&(pdev->dev), - &device_attrs[i]); - if (ret) - break; - } - - if (ret < 0) - dev_err(&pdev->dev, "failed to add sysfs entries\n"); - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - ctx->nb_exynos_display.notifier_call = exynos_display_notifier_callback; - ret = exynos4_display_register_client(&ctx->nb_exynos_display); - if (ret < 0) - dev_warn(dev, "failed to register exynos-display notifier\n"); -#endif - - dev_info(&pdev->dev, "registered successfully\n"); - - subdrv = &ctx->subdrv; - - subdrv->dev = dev; - subdrv->manager = &fimd_manager; - subdrv->probe = fimd_subdrv_probe; - subdrv->remove = fimd_subdrv_remove; - -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB - ctx->nb_ctrl.notifier_call = fimd_notifier_ctrl; - ret = fimc_register_client(&ctx->nb_ctrl); - if (ret) { - dev_err(dev, "could not register fimd notify callback\n"); - goto err_alloc_fail; - } -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - pm_qos_add_request(&ctx->pm_qos, - PM_QOS_DISPLAY_FREQUENCY, EXYNOS4_DISPLAY_LV_LF); -#endif -#endif - - mutex_init(&ctx->lock); - - platform_set_drvdata(pdev, ctx); - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_get_sync(dev); - - for (win = 0; win < WINDOWS_NR; win++) - if (win != ctx->default_win) - fimd_clear_win(ctx, win); - - exynos_drm_subdrv_register(subdrv); - - return 0; - -err_alloc_fail: - free_irq(ctx->irq, ctx); - -err_get_resource: - iounmap(ctx->regs); - -err_req_region_io: - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - -err_clk: - clk_disable(ctx->lcd_clk); - clk_put(ctx->lcd_clk); - -err_bus_clk: - clk_disable(ctx->bus_clk); - clk_put(ctx->bus_clk); - -err_clk_get: - kfree(ctx); - return ret; -} - -static int __devexit fimd_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct fimd_context *ctx = platform_get_drvdata(pdev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_drm_subdrv_unregister(&ctx->subdrv); -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB - fimc_unregister_client(&ctx->nb_ctrl); -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - pm_qos_remove_request(&ctx->pm_qos); -#endif -#endif - - if (ctx->suspended) - goto out; - - pm_runtime_set_suspended(dev); - pm_runtime_put_sync(dev); - -out: - pm_runtime_disable(dev); - - clk_put(ctx->lcd_clk); - clk_put(ctx->bus_clk); - - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - exynos4_display_unregister_client(&ctx->nb_exynos_display); -#endif - - iounmap(ctx->regs); - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - free_irq(ctx->irq, ctx); - - kfree(ctx); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int fimd_suspend(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - if (pm_runtime_suspended(dev)) - return 0; - - /* - * do not use pm_runtime_suspend(). if pm_runtime_suspend() is - * called here, an error would be returned by that interface - * because the usage_count of pm runtime is more than 1. - */ - return fimd_power_on(ctx, false); -} - -static int fimd_resume(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - /* - * if entered to sleep when lcd panel was on, the usage_count - * of pm runtime would still be 1 so in this case, fimd driver - * should be on directly not drawing on pm runtime interface. - */ - if (!pm_runtime_suspended(dev)) - return fimd_power_on(ctx, true); - - return 0; -} -#endif - -#ifdef CONFIG_PM_RUNTIME -static int fimd_runtime_suspend(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return fimd_power_on(ctx, false); -} - -static int fimd_runtime_resume(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return fimd_power_on(ctx, true); -} -#endif - -static const struct dev_pm_ops fimd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) - SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) -}; - -struct platform_driver fimd_driver = { - .probe = fimd_probe, - .remove = __devexit_p(fimd_remove), - .driver = { - .name = "s3cfb", - .owner = THIS_MODULE, - .pm = &fimd_pm_ops, - }, -}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index a8f201e..5633207 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -22,6 +22,7 @@ #include "drmP.h" #include "exynos_drm.h" #include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" #include "exynos_drm_iommu.h" #define G2D_HW_MAJOR_VER 4 @@ -93,7 +94,7 @@ #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) #define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) -#define MAX_BUF_ADDR_NR 6 +#define MAX_BUF_ADDR_NR 6 /* cmdlist data structure */ struct g2d_cmdlist { @@ -111,7 +112,7 @@ struct g2d_cmdlist_node { struct list_head list; struct g2d_cmdlist *cmdlist; unsigned int map_nr; - void *gem_obj[MAX_BUF_ADDR_NR]; + void *gem_objs[MAX_BUF_ADDR_NR]; dma_addr_t dma_addr; struct drm_exynos_pending_g2d_event *event; @@ -152,7 +153,8 @@ struct g2d_data { struct kmem_cache *runqueue_slab; }; -static int g2d_init_cmdlist(struct g2d_data *g2d) +static int g2d_init_cmdlist(struct g2d_data *g2d, + struct exynos_drm_private *drm_priv) { struct device *dev = g2d->dev; struct g2d_cmdlist_node *node = g2d->cmdlist_node; @@ -171,8 +173,9 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) * pages contained in sg list to iommu table. Command list pool also is * accessed by dma through device address with using iommu. */ - g2d->cmdlist_pool_map = exynos_drm_iommu_map(dev, g2d->cmdlist_pool, - G2D_CMDLIST_POOL_SIZE); + g2d->cmdlist_pool_map = exynos_drm_iommu_map(drm_priv->vmm, + g2d->cmdlist_pool, + G2D_CMDLIST_POOL_SIZE); if (!g2d->cmdlist_pool_map) { dev_err(dev, "failed map to iommu\n"); ret = -EFAULT; @@ -199,7 +202,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) return 0; err_iommu_unmap: - exynos_drm_iommu_unmap(dev, g2d->cmdlist_pool_map); + exynos_drm_iommu_unmap(drm_priv->vmm, g2d->cmdlist_pool_map); err: dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, g2d->cmdlist_pool); @@ -208,13 +211,18 @@ err: static void g2d_fini_cmdlist(struct g2d_data *g2d) { - struct device *dev = g2d->dev; + struct exynos_drm_private *drm_priv; + struct exynos_drm_subdrv *subdrv = &g2d->subdrv; + + drm_priv = subdrv->drm_dev->dev_private; - exynos_drm_iommu_unmap(dev, g2d->cmdlist_pool_map); + if (drm_priv->vmm) + exynos_drm_iommu_unmap(drm_priv->vmm, g2d->cmdlist_pool_map); kfree(g2d->cmdlist_node); - dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, - g2d->cmdlist_pool); + dma_free_coherent(g2d->dev, G2D_CMDLIST_POOL_SIZE, + g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); } static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) @@ -269,70 +277,48 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, struct drm_device *drm_dev, struct drm_file *file) { - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct g2d_cmdlist *cmdlist = node->cmdlist; - struct iommu_gem_map_params params; - unsigned int gem_handle; - dma_addr_t addr; int offset; int i; - params.dev = g2d->dev; - params.drm_dev = drm_dev; - params.file = file; - for (i = 0; i < node->map_nr; i++) { + unsigned int gem_handle, gem_obj; + dma_addr_t *addr; + offset = cmdlist->last - (i * 2 + 1); gem_handle = cmdlist->data[offset]; - addr = exynos_drm_iommu_map_gem(¶ms, - &g2d_priv->iommu_map_list, - gem_handle, - IOMMU_G2D); - if (!addr) { + addr = exynos_drm_gem_get_dma_addr(drm_dev, gem_handle, + file, + &gem_obj); + if (IS_ERR(addr)) { node->map_nr = i; return -EFAULT; } - cmdlist->data[offset] = addr; - node->gem_obj[i] = params.gem_obj; + cmdlist->data[offset] = *addr; + node->gem_objs[i] = (void *)gem_obj; } return 0; } -static void g2d_unmap_cmdlist_gem(struct drm_device *drm_dev, - struct g2d_cmdlist_node *node, - struct drm_file *file, int dec) +static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, + struct g2d_cmdlist_node *node) { - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; - struct device *dev = g2d_priv->dev; - struct g2d_cmdlist *cmdlist = node->cmdlist; - struct iommu_gem_map_params params; - struct g2d_data *g2d; - dma_addr_t addr; - int offset; + struct exynos_drm_subdrv *subdrv = &g2d->subdrv; int i; - g2d = dev_get_drvdata(dev); - if (!g2d) - return; - - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; - for (i = 0; i < node->map_nr; i++) { - offset = cmdlist->last - (i * 2 + 1) - dec; - addr = cmdlist->data[offset]; + void *gem_obj = node->gem_objs[i]; - params.gem_obj = node->gem_obj[i]; + if (gem_obj) + exynos_drm_gem_put_dma_addr(subdrv->drm_dev, gem_obj); - exynos_drm_iommu_unmap_gem(¶ms, addr, - IOMMU_G2D); + node->gem_objs[i] = NULL; } + + node->map_nr = 0; } static void g2d_dma_start(struct g2d_data *g2d, @@ -344,7 +330,7 @@ static void g2d_dma_start(struct g2d_data *g2d, pm_runtime_get_sync(g2d->dev); clk_enable(g2d->gate_clk); - pm_qos_update_request(&g2d->pm_qos, 400000); + pm_qos_update_request(&g2d->pm_qos, 400200); /* interrupt enable */ writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, @@ -370,10 +356,18 @@ static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d) static void g2d_free_runqueue_node(struct g2d_data *g2d, struct g2d_runqueue_node *runqueue_node) { + struct g2d_cmdlist_node *node; + if (!runqueue_node) return; mutex_lock(&g2d->cmdlist_mutex); + /* + * commands in run_cmdlist have been completed so unmap all gem + * objects in each command node so that they are unreferenced. + */ + list_for_each_entry(node, &runqueue_node->run_cmdlist, list) + g2d_unmap_cmdlist_gem(g2d, node); list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist); mutex_unlock(&g2d->cmdlist_mutex); @@ -398,7 +392,15 @@ static void g2d_runqueue_worker(struct work_struct *work) clk_disable(g2d->gate_clk); pm_runtime_put_sync(g2d->dev); - complete(&g2d->runqueue_node->complete); + /* if async mode, do not call complete. */ + if (!g2d->runqueue_node->async) + complete(&g2d->runqueue_node->complete); + + /* + * if async mode, run_cmdlist of runqueue_node is not freed + * at exynos_g2d_exec_ioctl once complete because wait_for_completion + * wasn't called there so free it here. + */ if (g2d->runqueue_node->async) g2d_free_runqueue_node(g2d, g2d->runqueue_node); @@ -444,12 +446,14 @@ static irqreturn_t g2d_irq_handler(int irq, void *dev_id) writel_relaxed(pending, g2d->regs + G2D_INTC_PEND); if (pending & G2D_INTP_GCMD_FIN) { - u32 cmdlist_no = readl_relaxed(g2d->regs + G2D_DMA_STATUS); + u32 value, list_done_count; - cmdlist_no = (cmdlist_no & G2D_DMA_LIST_DONE_COUNT) >> + value = readl_relaxed(g2d->regs + G2D_DMA_STATUS); + + list_done_count = (value & G2D_DMA_LIST_DONE_COUNT) >> G2D_DMA_LIST_DONE_COUNT_OFFSET; - g2d_finish_event(g2d, cmdlist_no); + g2d_finish_event(g2d, list_done_count); writel_relaxed(0, g2d->regs + G2D_DMA_HOLD_CMD); if (!(pending & G2D_INTP_ACMD_FIN)) { @@ -461,6 +465,8 @@ static irqreturn_t g2d_irq_handler(int irq, void *dev_id) if (pending & G2D_INTP_ACMD_FIN) queue_work(g2d->g2d_workq, &g2d->runqueue_work); + writel_relaxed(pending, g2d->regs + G2D_INTC_PEND); + return IRQ_HANDLED; } @@ -653,7 +659,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, return 0; err_unmap: - g2d_unmap_cmdlist_gem(drm_dev, node, file, 0); + g2d_unmap_cmdlist_gem(g2d, node); err_free_event: if (node->event) { spin_lock_irqsave(&drm_dev->event_lock, flags); @@ -724,11 +730,57 @@ out: } EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl); -static int g2d_open(struct drm_device *drm_dev, struct device *dev, +static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + struct exynos_drm_private *drm_priv; + struct g2d_data *g2d; + int ret; + + drm_priv = drm_dev->dev_private; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + /* allocate dma-aware cmdlist buffer and map it with iommu table. */ + ret = g2d_init_cmdlist(g2d, drm_priv); + if (ret < 0) + return ret; + + /* enable iommu to g2d hardware */ + ret = exynos_drm_iommu_activate(drm_priv->vmm, dev); + if (ret < 0) { + dev_err(dev, "failed to activate iommu\n"); + goto err_fini_cmdlist; + } + + return ret; + +err_fini_cmdlist: + g2d_fini_cmdlist(g2d); + return ret; +} + +static void g2d_subdrv_remove(struct drm_device *drm_dev, struct device *dev) +{ + struct exynos_drm_private *drm_priv; + + drm_priv = drm_dev->dev_private; + + if (drm_priv->vmm) + exynos_drm_iommu_deactivate(drm_priv->vmm, dev); +} + +static int g2d_subdrv_open(struct drm_device *drm_dev, struct device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv = file->driver_priv; struct exynos_drm_g2d_private *g2d_priv; + struct g2d_data *g2d; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; g2d_priv = kzalloc(sizeof(*g2d_priv), GFP_KERNEL); if (!g2d_priv) { @@ -741,20 +793,17 @@ static int g2d_open(struct drm_device *drm_dev, struct device *dev, INIT_LIST_HEAD(&g2d_priv->inuse_cmdlist); INIT_LIST_HEAD(&g2d_priv->event_list); - INIT_LIST_HEAD(&g2d_priv->iommu_map_list); return 0; } -static void g2d_close(struct drm_device *drm_dev, struct device *dev, +static void g2d_subdrv_close(struct drm_device *drm_dev, struct device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv = file->driver_priv; struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct g2d_data *g2d; struct g2d_cmdlist_node *node, *n; - struct iommu_info_node *im, *t_im; - struct iommu_gem_map_params params; if (!dev) return; @@ -764,27 +813,18 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev, return; mutex_lock(&g2d->cmdlist_mutex); - list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) + list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) { + /* + * unmap all gem objects not completed. + * + * P.S. if current process was terminated forcely then + * there may be some commands in inuse_cmdlist so unmap + * them. + */ + g2d_unmap_cmdlist_gem(g2d, node); list_move_tail(&node->list, &g2d->free_cmdlist); - mutex_unlock(&g2d->cmdlist_mutex); - - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; - - /* - * unmap all device address spaces from iommu table and - * release all lists. - */ - list_for_each_entry_safe(im, t_im, &g2d_priv->iommu_map_list, list) { - params.gem_obj = im->gem_obj; - - exynos_drm_iommu_unmap_gem(¶ms, im->dma_addr, - IOMMU_G2D); - list_del(&im->list); - kfree(im); - im = NULL; } + mutex_unlock(&g2d->cmdlist_mutex); kfree(file_priv->g2d_priv); } @@ -826,29 +866,11 @@ static int __devinit g2d_probe(struct platform_device *pdev) mutex_init(&g2d->cmdlist_mutex); mutex_init(&g2d->runqueue_mutex); - /* setup device address space for g2d iommu */ - ret = exynos_drm_iommu_setup(dev); - if (ret < 0) { - dev_err(dev, "failed to setup iommu\n"); - goto err_destroy_workqueue; - } - - /* enable iommu to g2d hardware */ - ret = exynos_drm_iommu_activate(dev); - if (ret < 0) { - dev_err(dev, "failed to activate iommu\n"); - goto err_iommu_cleanup; - } - - ret = g2d_init_cmdlist(g2d); - if (ret < 0) - goto err_iommu_deactivate; - g2d->gate_clk = clk_get(dev, "fimg2d"); if (IS_ERR(g2d->gate_clk)) { dev_err(dev, "failed to get gate clock\n"); ret = PTR_ERR(g2d->gate_clk); - goto err_fini_cmdlist; + goto err_destory_workqueue; } pm_runtime_enable(dev); @@ -892,8 +914,10 @@ static int __devinit g2d_probe(struct platform_device *pdev) subdrv = &g2d->subdrv; subdrv->dev = dev; - subdrv->open = g2d_open; - subdrv->close = g2d_close; + subdrv->probe = g2d_subdrv_probe; + subdrv->remove = g2d_subdrv_remove; + subdrv->open = g2d_subdrv_open; + subdrv->close = g2d_subdrv_close; ret = exynos_drm_subdrv_register(subdrv); if (ret < 0) { @@ -918,13 +942,7 @@ err_release_res: err_put_clk: pm_runtime_disable(dev); clk_put(g2d->gate_clk); -err_fini_cmdlist: - g2d_fini_cmdlist(g2d); -err_iommu_deactivate: - exynos_drm_iommu_deactivate(dev); -err_iommu_cleanup: - exynos_drm_iommu_cleanup(dev); -err_destroy_workqueue: +err_destory_workqueue: destroy_workqueue(g2d->g2d_workq); err_destroy_slab: kmem_cache_destroy(g2d->runqueue_slab); @@ -955,8 +973,6 @@ static int __devexit g2d_remove(struct platform_device *pdev) clk_put(g2d->gate_clk); g2d_fini_cmdlist(g2d); - exynos_drm_iommu_deactivate(&pdev->dev); - exynos_drm_iommu_cleanup(&pdev->dev); destroy_workqueue(g2d->g2d_workq); kmem_cache_destroy(g2d->runqueue_slab); kfree(g2d); @@ -968,6 +984,8 @@ static int __devexit g2d_remove(struct platform_device *pdev) static int g2d_suspend(struct device *dev) { struct g2d_data *g2d = dev_get_drvdata(dev); + struct drm_device *drm_dev = g2d->subdrv.drm_dev; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; mutex_lock(&g2d->runqueue_mutex); g2d->suspended = true; @@ -979,12 +997,25 @@ static int g2d_suspend(struct device *dev) flush_work_sync(&g2d->runqueue_work); + /* disable iommu to g2d device. */ + exynos_drm_iommu_deactivate(drm_priv->vmm, dev); + return 0; } static int g2d_resume(struct device *dev) { struct g2d_data *g2d = dev_get_drvdata(dev); + struct drm_device *drm_dev = g2d->subdrv.drm_dev; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; + int ret; + + /* enable iommu to g2d hardware */ + ret = exynos_drm_iommu_activate(drm_priv->vmm, dev); + if (ret < 0) { + dev_err(dev, "failed to activate iommu\n"); + return ret; + } g2d->suspended = false; g2d_exec_runqueue(g2d); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 35d2cd9..7d12f6c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -35,6 +35,8 @@ #include "exynos_drm_buf.h" #include "exynos_drm_iommu.h" +#define USERPTR_MAX_SIZE SZ_64M + static struct exynos_drm_private_cb *private_cb; void exynos_drm_priv_cb_register(struct exynos_drm_private_cb *cb) @@ -132,6 +134,45 @@ static void put_vma(struct vm_area_struct *vma) kfree(vma); } +/* + * lock_userptr_vma - lock VMAs within user address space + * + * this function locks vma within user address space to avoid pages + * to the userspace from being swapped out. + * if this vma isn't locked, the pages to the userspace could be swapped out + * so unprivileged user might access different pages and dma of any device + * could access physical memory region not intended once swap-in. + */ +static int lock_userptr_vma(struct exynos_drm_gem_buf *buf, unsigned int lock) +{ + struct vm_area_struct *vma; + unsigned long start, end; + + start = buf->userptr; + end = buf->userptr + buf->size - 1; + + down_write(¤t->mm->mmap_sem); + + do { + vma = find_vma(current->mm, start); + if (!vma) { + up_write(¤t->mm->mmap_sem); + return -EFAULT; + } + + if (lock) + vma->vm_flags |= VM_LOCKED; + else + vma->vm_flags &= ~VM_LOCKED; + + start = vma->vm_end + 1; + } while (vma->vm_end < end); + + up_write(¤t->mm->mmap_sem); + + return 0; +} + static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj, struct vm_area_struct *vma) { @@ -165,25 +206,17 @@ out: struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) { - struct inode *inode; - struct address_space *mapping; struct page *p, **pages; int i, npages; - /* This is the shared memory object that backs the GEM resource */ - inode = obj->filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - npages = obj->size >> PAGE_SHIFT; pages = drm_malloc_ab(npages, sizeof(struct page *)); if (pages == NULL) return ERR_PTR(-ENOMEM); - gfpmask |= mapping_gfp_mask(mapping); - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + p = alloc_page(gfpmask); if (IS_ERR(p)) goto fail; pages[i] = p; @@ -192,31 +225,22 @@ struct page **exynos_gem_get_pages(struct drm_gem_object *obj, return pages; fail: - while (i--) - page_cache_release(pages[i]); + while (--i) + __free_page(pages[i]); drm_free_large(pages); return ERR_PTR(PTR_ERR(p)); } static void exynos_gem_put_pages(struct drm_gem_object *obj, - struct page **pages, - bool dirty, bool accessed) + struct page **pages) { - int i, npages; + int npages; npages = obj->size >> PAGE_SHIFT; - for (i = 0; i < npages; i++) { - if (dirty) - set_page_dirty(pages[i]); - - if (accessed) - mark_page_accessed(pages[i]); - - /* Undo the reference we took when populating the table */ - page_cache_release(pages[i]); - } + while (--npages >= 0) + __free_page(pages[npages]); drm_free_large(pages); } @@ -236,7 +260,7 @@ static int exynos_drm_gem_map_pages(struct drm_gem_object *obj, pfn = page_to_pfn(buf->pages[page_offset++]); } else - pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset; + pfn = (buf->paddr >> PAGE_SHIFT) + page_offset; return vm_insert_mixed(vma, f_vaddr, pfn); } @@ -255,7 +279,7 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) return -EINVAL; } - pages = exynos_gem_get_pages(obj, GFP_KERNEL); + pages = exynos_gem_get_pages(obj, GFP_HIGHUSER_MOVABLE); if (IS_ERR(pages)) { DRM_ERROR("failed to get pages.\n"); return PTR_ERR(pages); @@ -288,15 +312,13 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) sgl = sg_next(sgl); } - /* add some codes for UNCACHED type here. TODO */ - buf->pages = pages; return ret; err1: kfree(buf->sgt); buf->sgt = NULL; err: - exynos_gem_put_pages(obj, pages, true, false); + exynos_gem_put_pages(obj, pages); return ret; } @@ -314,7 +336,7 @@ static void exynos_drm_gem_put_pages(struct drm_gem_object *obj) kfree(buf->sgt); buf->sgt = NULL; - exynos_gem_put_pages(obj, buf->pages, true, false); + exynos_gem_put_pages(obj, buf->pages); buf->pages = NULL; /* add some codes for UNCACHED type here. TODO */ @@ -338,6 +360,9 @@ static void exynos_drm_put_userptr(struct drm_gem_object *obj) npages = buf->size >> PAGE_SHIFT; + if (exynos_gem_obj->flags & EXYNOS_BO_USERPTR && !buf->pfnmap) + lock_userptr_vma(buf, 0); + npages--; while (npages >= 0) { if (buf->write) @@ -381,10 +406,12 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) { struct drm_gem_object *obj; struct exynos_drm_gem_buf *buf; + struct exynos_drm_private *private; DRM_DEBUG_KMS("%s\n", __FILE__); obj = &exynos_gem_obj->base; + private = obj->dev->dev_private; buf = exynos_gem_obj->buffer; DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); @@ -401,6 +428,18 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) if (!buf->pages) return; + /* + * do not release memory region from exporter. + * + * the region will be released by exporter + * once dmabuf's refcount becomes 0. + */ + if (obj->import_attach) + goto out; + + if (private->vmm) + exynos_drm_iommu_unmap_gem(obj); + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) exynos_drm_gem_put_pages(obj); else if (exynos_gem_obj->flags & EXYNOS_BO_USERPTR) @@ -408,6 +447,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) else exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf); +out: exynos_drm_fini_buf(obj->dev, buf); exynos_gem_obj->buffer = NULL; @@ -441,6 +481,27 @@ struct exynos_drm_gem_obj *exynos_drm_gem_get_obj(struct drm_device *dev, return exynos_gem_obj; } +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(dev, file_priv, gem_handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return 0; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + drm_gem_object_unreference_unlocked(obj); + + return exynos_gem_obj->buffer->size; +} + + struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, unsigned long size) { @@ -474,7 +535,9 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, unsigned long size) { struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_private *private = dev->dev_private; struct exynos_drm_gem_buf *buf; + unsigned long packed_size = size; int ret; if (!size) { @@ -499,6 +562,7 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, goto err_fini_buf; } + exynos_gem_obj->packed_size = packed_size; exynos_gem_obj->buffer = buf; /* set memory type and cache attribute from user side. */ @@ -522,6 +586,31 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, } } + if (private->vmm) { + exynos_gem_obj->vmm = private->vmm; + + buf->dev_addr = exynos_drm_iommu_map_gem(dev, + &exynos_gem_obj->base); + if (!buf->dev_addr) { + DRM_ERROR("failed to map gem with iommu table.\n"); + ret = -EFAULT; + + if (flags & EXYNOS_BO_NONCONTIG) + exynos_drm_gem_put_pages(&exynos_gem_obj->base); + else + exynos_drm_free_buf(dev, flags, buf); + + drm_gem_object_release(&exynos_gem_obj->base); + + goto err_fini_buf; + } + + buf->dma_addr = buf->dev_addr; + } else + buf->dma_addr = buf->paddr; + + DRM_DEBUG_KMS("dma_addr = 0x%x\n", buf->dma_addr); + return exynos_gem_obj; err_fini_buf: @@ -549,63 +638,49 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, return ret; } - return 0; + return ret; } void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, unsigned int gem_handle, - struct drm_file *file_priv) + struct drm_file *filp, + unsigned int *gem_obj) { struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; struct drm_gem_object *obj; - obj = drm_gem_object_lookup(dev, file_priv, gem_handle); + obj = drm_gem_object_lookup(dev, filp, gem_handle); if (!obj) { DRM_ERROR("failed to lookup gem object.\n"); return ERR_PTR(-EINVAL); } exynos_gem_obj = to_exynos_gem_obj(obj); + buf = exynos_gem_obj->buffer; - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - DRM_DEBUG_KMS("not support NONCONTIG type.\n"); - drm_gem_object_unreference_unlocked(obj); + *gem_obj = (unsigned int)obj; - /* TODO */ - return ERR_PTR(-EINVAL); - } - - return &exynos_gem_obj->buffer->dma_addr; + return &buf->dma_addr; } -void exynos_drm_gem_put_dma_addr(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv) +void exynos_drm_gem_put_dma_addr(struct drm_device *dev, void *gem_obj) { struct exynos_drm_gem_obj *exynos_gem_obj; struct drm_gem_object *obj; - obj = drm_gem_object_lookup(dev, file_priv, gem_handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); + if (!gem_obj) return; - } - exynos_gem_obj = to_exynos_gem_obj(obj); + /* use gem handle instead of object. TODO */ - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - DRM_DEBUG_KMS("not support NONCONTIG type.\n"); - drm_gem_object_unreference_unlocked(obj); + obj = gem_obj; - /* TODO */ - return; - } - - drm_gem_object_unreference_unlocked(obj); + exynos_gem_obj = to_exynos_gem_obj(obj); /* - * decrease obj->refcount one more time because we has already - * increased it at exynos_drm_gem_get_dma_addr(). + * unreference this gem object because this had already been + * referenced at exynos_drm_gem_get_dma_addr(). */ drm_gem_object_unreference_unlocked(obj); } @@ -681,7 +756,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, * get page frame number to physical memory to be mapped * to user space. */ - pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> + pfn = ((unsigned long)exynos_gem_obj->buffer->paddr) >> PAGE_SHIFT; DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); @@ -749,7 +824,9 @@ static int exynos_drm_get_userptr(struct drm_device *dev, unsigned long npages = 0; struct vm_area_struct *vma; struct exynos_drm_gem_buf *buf = obj->buffer; + int ret; + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, userptr); /* the memory region mmaped with VM_PFNMAP. */ @@ -767,11 +844,11 @@ static int exynos_drm_get_userptr(struct drm_device *dev, for (prev_pfn = 0; start < end; start += PAGE_SIZE) { ret = follow_pfn(vma, start, &this_pfn); if (ret) - return ret; + goto err; if (prev_pfn == 0) { pa = this_pfn << PAGE_SHIFT; - buf->dma_addr = pa + offset; + buf->paddr = pa + offset; } else if (this_pfn != prev_pfn + 1) { ret = -EINVAL; goto err; @@ -791,14 +868,30 @@ static int exynos_drm_get_userptr(struct drm_device *dev, goto err; } + up_read(¤t->mm->mmap_sem); buf->pfnmap = true; return npages; err: - buf->dma_addr = 0; + buf->paddr = 0; + up_read(¤t->mm->mmap_sem); + return ret; } + up_read(¤t->mm->mmap_sem); + + /* + * lock the vma within userptr to avoid userspace buffer + * from being swapped out. + */ + ret = lock_userptr_vma(buf, 1); + if (ret < 0) { + DRM_ERROR("failed to lock vma for userptr.\n"); + lock_userptr_vma(buf, 0); + return 0; + } + buf->write = write; npages = buf->size >> PAGE_SHIFT; @@ -809,6 +902,7 @@ err: if (get_npages != npages) DRM_ERROR("failed to get user_pages.\n"); + buf->userptr = userptr; buf->pfnmap = false; return get_npages; @@ -817,11 +911,12 @@ err: int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct exynos_drm_private *priv = dev->dev_private; struct exynos_drm_gem_obj *exynos_gem_obj; struct drm_exynos_gem_userptr *args = data; struct exynos_drm_gem_buf *buf; struct scatterlist *sgl; - unsigned long size, userptr; + unsigned long size, userptr, packed_size; unsigned int npages; int ret, get_npages; @@ -836,7 +931,15 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, if (ret) return ret; + packed_size = args->size; + size = roundup_gem_size(args->size, EXYNOS_BO_USERPTR); + + if (size > priv->userptr_limit) { + DRM_ERROR("excessed maximum size of userptr.\n"); + return -EINVAL; + } + userptr = args->userptr; buf = exynos_drm_init_buf(dev, size); @@ -849,6 +952,8 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, goto err_free_buffer; } + exynos_gem_obj->packed_size = packed_size; + buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); if (!buf->sgt) { DRM_ERROR("failed to allocate buf->sgt.\n"); @@ -909,8 +1014,30 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, /* always use EXYNOS_BO_USERPTR as memory type for userptr. */ exynos_gem_obj->flags |= EXYNOS_BO_USERPTR; + if (priv->vmm) { + exynos_gem_obj->vmm = priv->vmm; + + buf->dev_addr = exynos_drm_iommu_map_gem(dev, + &exynos_gem_obj->base); + if (!buf->dev_addr) { + DRM_ERROR("failed to map gem with iommu table.\n"); + ret = -EFAULT; + + exynos_drm_free_buf(dev, exynos_gem_obj->flags, buf); + + drm_gem_object_release(&exynos_gem_obj->base); + + goto err_release_handle; + } + + buf->dma_addr = buf->dev_addr; + } else + buf->dma_addr = buf->paddr; + return 0; +err_release_handle: + drm_gem_handle_delete(file_priv, args->handle); err_release_userptr: get_npages--; while (get_npages >= 0) @@ -957,6 +1084,26 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, return 0; } +int exynos_drm_gem_user_limit_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct exynos_drm_private *priv = dev->dev_private; + struct drm_exynos_user_limit *limit = data; + + if (limit->userptr_limit < PAGE_SIZE || + limit->userptr_limit > USERPTR_MAX_SIZE) { + DRM_DEBUG_KMS("invalid userptr_limit size.\n"); + return -EINVAL; + } + + if (priv->userptr_limit == limit->userptr_limit) + return 0; + + priv->userptr_limit = limit->userptr_limit; + + return 0; +} + int exynos_drm_gem_export_ump_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { @@ -1034,13 +1181,16 @@ static int exynos_gem_l1_cache_ops(struct drm_device *drm_dev, } static int exynos_gem_l2_cache_ops(struct drm_device *drm_dev, - struct drm_exynos_gem_cache_op *op) { - phys_addr_t phy_start, phy_end; - + struct drm_file *filp, + struct drm_exynos_gem_cache_op *op) +{ if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE || op->flags & EXYNOS_DRM_CACHE_INV_RANGE || op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) { + unsigned long virt_start = op->usr_addr, pfn; + phys_addr_t phy_start, phy_end; struct vm_area_struct *vma; + int ret; down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, op->usr_addr); @@ -1052,44 +1202,90 @@ static int exynos_gem_l2_cache_ops(struct drm_device *drm_dev, } /* - * for range flush to l2 cache, mmaped memory region should - * be physically continuous because l2 cache uses PIPT. + * Range operation to l2 cache(PIPT) */ if (vma && (vma->vm_flags & VM_PFNMAP)) { - unsigned long virt_start = op->usr_addr, pfn; - int ret; - ret = follow_pfn(vma, virt_start, &pfn); if (ret < 0) { - DRM_ERROR("failed to get pfn from usr_addr.\n"); + DRM_ERROR("failed to get pfn.\n"); return ret; } + /* + * the memory region with VM_PFNMAP is contiguous + * physically so do range operagion just one time. + */ phy_start = pfn << PAGE_SHIFT; phy_end = phy_start + op->size; + + if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE) + outer_flush_range(phy_start, phy_end); + else if (op->flags & EXYNOS_DRM_CACHE_INV_RANGE) + outer_inv_range(phy_start, phy_end); + else if (op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) + outer_clean_range(phy_start, phy_end); + + return 0; } else { - DRM_ERROR("not mmaped memory region with PFNMAP.\n"); - return -EINVAL; + struct exynos_drm_gem_obj *exynos_obj; + struct exynos_drm_gem_buf *buf; + struct drm_gem_object *obj; + struct scatterlist *sgl; + unsigned int npages, i = 0; + + mutex_lock(&drm_dev->struct_mutex); + + obj = drm_gem_object_lookup(drm_dev, filp, + op->gem_handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + mutex_unlock(&drm_dev->struct_mutex); + return -EINVAL; + } + + exynos_obj = to_exynos_gem_obj(obj); + buf = exynos_obj->buffer; + npages = buf->size >> PAGE_SHIFT; + sgl = buf->sgt->sgl; + + drm_gem_object_unreference(obj); + mutex_unlock(&drm_dev->struct_mutex); + + /* + * in this case, the memory region is non-contiguous + * physically so do range operation to all the pages. + */ + while (i < npages) { + phy_start = sg_dma_address(sgl); + phy_end = phy_start + buf->page_size; + + if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE) + outer_flush_range(phy_start, phy_end); + else if (op->flags & EXYNOS_DRM_CACHE_INV_RANGE) + outer_inv_range(phy_start, phy_end); + else if (op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) + outer_clean_range(phy_start, phy_end); + + i++; + sgl = sg_next(sgl); + } + + return 0; } } if (op->flags & EXYNOS_DRM_CACHE_FSH_ALL) outer_flush_all(); - else if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE) - outer_flush_range(phy_start, phy_end); else if (op->flags & EXYNOS_DRM_CACHE_INV_ALL) outer_inv_all(); else if (op->flags & EXYNOS_DRM_CACHE_CLN_ALL) outer_clean_all(); - else if (op->flags & EXYNOS_DRM_CACHE_INV_RANGE) - outer_inv_range(phy_start, phy_end); - else if (op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) - outer_clean_range(phy_start, phy_end); else { DRM_ERROR("invalid l2 cache operation.\n"); return -EINVAL; } + return 0; } @@ -1105,6 +1301,33 @@ int exynos_drm_gem_cache_op_ioctl(struct drm_device *drm_dev, void *data, if (ret) return -EINVAL; + /* + * do cache operation for all cache range if op->size is bigger + * than SZ_1M because cache range operation with bit size has + * big cost. + */ + if (op->size >= SZ_1M) { + if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE) { + if (op->flags & EXYNOS_DRM_L1_CACHE) + __cpuc_flush_user_all(); + + if (op->flags & EXYNOS_DRM_L2_CACHE) + outer_flush_all(); + + return 0; + } else if (op->flags & EXYNOS_DRM_CACHE_INV_RANGE) { + if (op->flags & EXYNOS_DRM_L2_CACHE) + outer_inv_all(); + + return 0; + } else if (op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) { + if (op->flags & EXYNOS_DRM_L2_CACHE) + outer_clean_all(); + + return 0; + } + } + if (op->flags & EXYNOS_DRM_L1_CACHE || op->flags & EXYNOS_DRM_ALL_CACHES) { ret = exynos_gem_l1_cache_ops(drm_dev, op); @@ -1114,7 +1337,7 @@ int exynos_drm_gem_cache_op_ioctl(struct drm_device *drm_dev, void *data, if (op->flags & EXYNOS_DRM_L2_CACHE || op->flags & EXYNOS_DRM_ALL_CACHES) - ret = exynos_gem_l2_cache_ops(drm_dev, op); + ret = exynos_gem_l2_cache_ops(drm_dev, file_priv, op); err: return ret; } @@ -1150,7 +1373,7 @@ int exynos_drm_gem_get_phy_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - get_phy->phy_addr = exynos_gem_obj->buffer->dma_addr; + get_phy->phy_addr = exynos_gem_obj->buffer->paddr; get_phy->size = exynos_gem_obj->buffer->size; drm_gem_object_unreference(obj); @@ -1164,24 +1387,34 @@ int exynos_drm_gem_phy_imp_ioctl(struct drm_device *drm_dev, void *data, { struct drm_exynos_gem_phy_imp *args = data; struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_private *private = drm_dev->dev_private; struct exynos_drm_gem_buf *buffer; + unsigned long size, packed_size; + unsigned int flags = EXYNOS_BO_CONTIG; + unsigned int npages, i = 0; + struct scatterlist *sgl; + dma_addr_t start_addr; int ret = 0; DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_gem_obj = exynos_drm_gem_init(drm_dev, args->size); + packed_size = args->size; + size = roundup_gem_size(args->size, flags); + + exynos_gem_obj = exynos_drm_gem_init(drm_dev, size); if (!exynos_gem_obj) return -ENOMEM; - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + buffer = exynos_drm_init_buf(drm_dev, size); if (!buffer) { DRM_DEBUG_KMS("failed to allocate buffer\n"); ret = -ENOMEM; - goto err; + goto err_release_gem_obj; } - buffer->dma_addr = (dma_addr_t)args->phy_addr; - buffer->size = args->size; + exynos_gem_obj->packed_size = packed_size; + buffer->paddr = (dma_addr_t)args->phy_addr; + buffer->size = size; /* * if shared is true, this bufer wouldn't be released. @@ -1194,15 +1427,90 @@ int exynos_drm_gem_phy_imp_ioctl(struct drm_device *drm_dev, void *data, ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, &args->gem_handle); if (ret) - goto err_kfree_buffer; + goto err_fini_buf; DRM_DEBUG_KMS("got gem handle = 0x%x\n", args->gem_handle); + if (buffer->size >= SZ_1M) { + npages = buffer->size >> SECTION_SHIFT; + buffer->page_size = SECTION_SIZE; + } else if (buffer->size >= SZ_64K) { + npages = buffer->size >> 16; + buffer->page_size = SZ_64K; + } else { + npages = buffer->size >> PAGE_SHIFT; + buffer->page_size = PAGE_SIZE; + } + + buffer->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!buffer->sgt) { + DRM_ERROR("failed to allocate sg table.\n"); + ret = -ENOMEM; + goto err_release_handle; + } + + ret = sg_alloc_table(buffer->sgt, npages, GFP_KERNEL); + if (ret < 0) { + DRM_ERROR("failed to initialize sg table.\n"); + goto err_free_sgt; + } + + buffer->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); + if (!buffer->pages) { + DRM_ERROR("failed to allocate pages.\n"); + ret = -ENOMEM; + goto err_sg_free_table; + } + + sgl = buffer->sgt->sgl; + start_addr = buffer->paddr; + + while (i < npages) { + buffer->pages[i] = phys_to_page(start_addr); + sg_set_page(sgl, buffer->pages[i], buffer->page_size, 0); + sg_dma_address(sgl) = start_addr; + start_addr += buffer->page_size; + sgl = sg_next(sgl); + i++; + } + + if (private->vmm) { + exynos_gem_obj->vmm = private->vmm; + + buffer->dev_addr = exynos_drm_iommu_map_gem(drm_dev, + &exynos_gem_obj->base); + if (!buffer->dev_addr) { + DRM_ERROR("failed to map gem with iommu table.\n"); + ret = -EFAULT; + + exynos_drm_free_buf(drm_dev, flags, buffer); + + drm_gem_object_release(&exynos_gem_obj->base); + + goto err_free_pages; + } + + buffer->dma_addr = buffer->dev_addr; + } else + buffer->dma_addr = buffer->paddr; + + DRM_DEBUG_KMS("dma_addr = 0x%x\n", buffer->dma_addr); + return 0; -err_kfree_buffer: - kfree(buffer); -err: +err_free_pages: + kfree(buffer->pages); + buffer->pages = NULL; +err_sg_free_table: + sg_free_table(buffer->sgt); +err_free_sgt: + kfree(buffer->sgt); + buffer->sgt = NULL; +err_release_handle: + drm_gem_handle_delete(file_priv, args->gem_handle); +err_fini_buf: + exynos_drm_fini_buf(drm_dev, buffer); +err_release_gem_obj: drm_gem_object_release(&exynos_gem_obj->base); kfree(exynos_gem_obj); return ret; @@ -1267,7 +1575,6 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset) { - struct exynos_drm_gem_obj *exynos_gem_obj; struct drm_gem_object *obj; int ret = 0; @@ -1288,15 +1595,13 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, goto unlock; } - exynos_gem_obj = to_exynos_gem_obj(obj); - - if (!exynos_gem_obj->base.map_list.map) { - ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base); + if (!obj->map_list.map) { + ret = drm_gem_create_mmap_offset(obj); if (ret) goto out; } - *offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT; + *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); out: @@ -1331,53 +1636,9 @@ int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, void exynos_drm_gem_close_object(struct drm_gem_object *obj, struct drm_file *file) { - struct exynos_drm_gem_obj *exynos_gem_obj; - struct iommu_gem_map_params params; - unsigned int type = 0; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* check this gem object was mapped to iommu at here. TODO */ - - exynos_gem_obj = to_exynos_gem_obj(obj); - - while (type < MAX_IOMMU_NR) { - /* - * unmap device address space already mapped to iommu. - * - this codes would be performed with user gem release - * request but in case of no request, when device driver - * using iommu is released, also same things should be - * performed by each driver. - */ - if (exynos_gem_obj->iommu_info.mapped & (1 << type)) { - dma_addr_t dma_addr; - struct list_head *list; - - params.dev = exynos_gem_obj->iommu_info.devs[type]; - params.drm_dev = obj->dev; - params.file = file; - params.gem_obj = exynos_gem_obj; - dma_addr = exynos_gem_obj->iommu_info.dma_addrs[type]; - - exynos_drm_iommu_unmap_gem(¶ms, - dma_addr, - type); - - exynos_gem_obj->iommu_info.mapped &= ~(1 << type); - exynos_gem_obj->iommu_info.dma_addrs[type] = 0; - - list = exynos_gem_obj->iommu_info.iommu_lists[type]; - - /* - * this gem has been unmapped from iommu so also - * remove a iommu node from current device's own - * iommu list. - */ - exynos_drm_remove_iommu_list(list, exynos_gem_obj); - } - - type++; - } + /* TODO */ } int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 1227ae3..c6cd6e0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -61,9 +61,10 @@ struct exynos_drm_iommu_info { * exynos drm gem buffer structure. * * @kvaddr: kernel virtual address to allocated memory region. + * *userptr: user space address. * @dma_addr: bus address(accessed by dma) to allocated memory region. - * - this address could be physical address without IOMMU and - * device address with IOMMU. + * @dev_addr: device address for IOMMU. + * @paddr: physical address to allocated buffer. * @write: whether pages will be written to by the caller. * @sgt: sg table to transfer page data. * @pages: contain all pages to allocated memory region. @@ -71,21 +72,22 @@ struct exynos_drm_iommu_info { * @size: size of allocated memory region. * @shared: indicate shared mfc memory region. * (temporarily used and it should be removed later.) - * @shared_refcount: a reference count for this buffer being shared with others. * @pfnmap: indicate whether memory region from userptr is mmaped with * VM_PFNMAP or not. */ struct exynos_drm_gem_buf { struct device *dev; void __iomem *kvaddr; + unsigned long userptr; dma_addr_t dma_addr; + dma_addr_t dev_addr; + dma_addr_t paddr; unsigned int write; struct sg_table *sgt; struct page **pages; unsigned long page_size; unsigned long size; bool shared; - atomic_t shared_refcount; bool pfnmap; }; @@ -102,8 +104,12 @@ struct exynos_drm_gem_buf { * or at framebuffer creation. * @iommu_info: contain iommu mapping information to each device driver * using its own iommu. - * @size: total memory size to physically non-continuous memory region. + * @size: size requested from user, in bytes and this size is aligned + * in page unit. + * @packed_size: real size of the gem object, in bytes and + * this size isn't aligned in page unit. * @flags: indicate memory type to allocated buffer and cache attruibute. + * @vmm: vmm object for iommu framework. * @priv_handle: handle to specific buffer object. * @priv_id: unique id to specific buffer object. * @@ -115,10 +121,10 @@ struct exynos_drm_gem_obj { struct exynos_drm_gem_buf *buffer; struct exynos_drm_iommu_info iommu_info; unsigned long size; + unsigned long packed_size; struct vm_area_struct *vma; unsigned int flags; - void *dma_buf_vmapping; - int vmapping_count; + void *vmm; unsigned int priv_handle; unsigned int priv_id; }; @@ -132,6 +138,9 @@ int register_buf_to_priv_mgr(struct exynos_drm_gem_obj *obj, struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); +int exynos_drm_gem_user_limit_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + /* destroy a buffer with gem object */ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj); @@ -159,16 +168,15 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, */ void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, unsigned int gem_handle, - struct drm_file *file_priv); + struct drm_file *filp, + unsigned int *gem_obj); /* * put dma address from gem handle and this function could be used for * other drivers such as 2d/3d acceleration drivers. * with this function call, gem object reference count would be decreased. */ -void exynos_drm_gem_put_dma_addr(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv); +void exynos_drm_gem_put_dma_addr(struct drm_device *dev, void *gem_obj); /* get buffer offset to map to user space. */ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, @@ -189,6 +197,11 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* get buffer size to gem handle. */ +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv); + /* initialize gem object. */ int exynos_drm_gem_init_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c new file mode 100644 index 0000000..d5e3a68 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include "drmP.h" +#include "drm_backlight.h" +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/cma.h> +#include <plat/map-base.h> + +#include "regs-gsc.h" +#include <drm/exynos_drm.h> +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" +#include "exynos_drm_ipp.h" +#include "exynos_drm_gsc.h" + +/* + * GSC is stand for General SCaler and + * supports image scaler/rotator and input/output DMA operations. + * input DMA reads image data from the memory. + * output DMA writes image data to memory. + * GSC supports image rotation and image effect functions. + */ + +#define GSC_MAX_DEVS 4 +#define GSC_MAX_SRC 8 +#define GSC_MAX_DST 32 +#define GSC_RESET_TIMEOUT 50 +#ifdef CONFIG_SLP_DISP_DEBUG +#define GSC_MAX_REG 128 +#define GSC_BASE_REG(id) (0x13E00000 + (0x10000 * id)) +#endif +#define GSC_CLK_RATE 166750000 +#define GSC_BUF_STOP 1 +#define GSC_BUF_START 2 +#define GSC_REG_SZ 32 +#define GSC_WIDTH_ITU_709 1280 + +#define get_gsc_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ + struct gsc_context, ippdrv); +#define gsc_read(offset) readl(ctx->regs + (offset)); +#define gsc_write(cfg, offset) writel(cfg, ctx->regs + (offset)); + +enum gsc_wb { + GSC_WB_NONE, + GSC_WB_A, + GSC_WB_B, +}; + +/* + * A structure of scaler. + * + * @range: narrow, wide. + * @pre_shfactor: pre sclaer shift factor. + * @pre_hratio: horizontal ratio of the prescaler. + * @pre_vratio: vertical ratio of the prescaler. + * @main_hratio: the main scaler's horizontal ratio. + * @main_vratio: the main scaler's vertical ratio. + */ +struct gsc_scaler { + bool range; + u32 pre_shfactor; + u32 pre_hratio; + u32 pre_vratio; + unsigned long main_hratio; + unsigned long main_vratio; +}; + +/* + * A structure of scaler capability. + * + * find user manual 49.2 features. + * @tile_w: tile mode or rotation width. + * @tile_h: tile mode or rotation height. + * @w: other cases width. + * @h: other cases height. + */ +struct gsc_capability { + /* tile or rotation */ + u32 tile_w; + u32 tile_h; + /* other cases */ + u32 w; + u32 h; +}; + +/* + * A structure of gsc context. + * + * @ippdrv: prepare initialization using ippdrv. + * @regs_res: register resources. + * @regs: memory mapped io registers. + * @lock: locking of operations. + * @gsc_clk: gsc clock. + * @sc: scaler infomations. + * @capa: scaler capability. + * @id: gsc id. + * @irq: irq number. + * @suspended: qos operations. + */ +struct gsc_context { + struct exynos_drm_ippdrv ippdrv; + struct resource *regs_res; + void __iomem *regs; + struct mutex lock; + struct clk *gsc_clk; + struct gsc_scaler sc; + struct gsc_capability *capa; + int id; + int irq; + bool suspended; +}; + +struct gsc_capability gsc51_capa[GSC_MAX_DEVS] = { + { + .tile_w = 2048, + .tile_h = 2048, + .w = 4800, + .h = 3344, + }, { + .tile_w = 2048, + .tile_h = 2048, + .w = 4800, + .h = 3344, + }, { + .tile_w = 2048, + .tile_h = 2048, + .w = 4800, + .h = 3344, + }, { + .tile_w = 2048, + .tile_h = 2048, + .w = 4800, + .h = 3344, + }, +}; + +static int gsc_sw_reset(struct gsc_context *ctx) +{ + u32 cfg; + int count = GSC_RESET_TIMEOUT; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* s/w reset */ + cfg = (GSC_SW_RESET_SRESET); + gsc_write(cfg, GSC_SW_RESET); + + /* wait s/w reset complete */ + while (count--) { + cfg = gsc_read(GSC_SW_RESET); + if (!cfg) + break; + usleep_range(1000, 2000); + } + + if (cfg) { + DRM_ERROR("failed to reset gsc h/w.\n"); + return -EBUSY; + } + + /* display fifo reset */ + cfg = readl(SYSREG_GSCBLK_CFG0); + /* + * GSCBLK Pixel asyncy FIFO S/W reset sequence + * set PXLASYNC_SW_RESET as 0 then, + * set PXLASYNC_SW_RESET as 1 again + */ + cfg &= ~GSC_PXLASYNC_RST(ctx->id); + writel(cfg, SYSREG_GSCBLK_CFG0); + cfg |= GSC_PXLASYNC_RST(ctx->id); + writel(cfg, SYSREG_GSCBLK_CFG0); + + /* pixel async reset */ + cfg = readl(SYSREG_DISP1BLK_CFG); + /* + * DISPBLK1 FIFO S/W reset sequence + * set FIFORST_DISP1 as 0 then, + * set FIFORST_DISP1 as 1 again + */ + cfg &= ~FIFORST_DISP1; + writel(cfg, SYSREG_DISP1BLK_CFG); + cfg |= FIFORST_DISP1; + writel(cfg, SYSREG_DISP1BLK_CFG); + + /* reset sequence */ + cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); + cfg |= (GSC_IN_BASE_ADDR_MASK | + GSC_IN_BASE_ADDR_PINGPONG(0)); + gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK); + gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK); + gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK); + + cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); + cfg |= (GSC_OUT_BASE_ADDR_MASK | + GSC_OUT_BASE_ADDR_PINGPONG(0)); + gsc_write(cfg, GSC_OUT_BASE_ADDR_Y_MASK); + gsc_write(cfg, GSC_OUT_BASE_ADDR_CB_MASK); + gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); + + return 0; +} + +static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) +{ + u32 gscblk_cfg; + + DRM_DEBUG_KMS("%s\n", __func__); + + gscblk_cfg = readl(SYSREG_GSCBLK_CFG1); + + if (enable) + gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) | + GSC_BLK_GSCL_WB_IN_SRC_SEL(ctx->id) | + GSC_BLK_SW_RESET_WB_DEST(ctx->id); + else + gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id); + + writel(gscblk_cfg, SYSREG_GSCBLK_CFG1); +} + +static void gsc_handle_irq(struct gsc_context *ctx, bool enable, + bool overflow, bool done) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, + enable, overflow, done); + + cfg = gsc_read(GSC_IRQ); + cfg |= (GSC_IRQ_OR_MASK | GSC_IRQ_FRMDONE_MASK); + + if (enable) { + cfg |= GSC_IRQ_ENABLE; + if (overflow) + cfg &= ~GSC_IRQ_OR_MASK; + if (done) + cfg &= ~GSC_IRQ_FRMDONE_MASK; + } else + cfg &= ~GSC_IRQ_ENABLE; + + gsc_write(cfg, GSC_IRQ); +} + +static int gsc_set_planar_addr(struct drm_exynos_ipp_buf_info *buf_info, + u32 fmt, struct drm_exynos_sz *sz) +{ + dma_addr_t *y_addr = &buf_info->base[EXYNOS_DRM_PLANAR_Y]; + dma_addr_t *cb_addr = &buf_info->base[EXYNOS_DRM_PLANAR_CB]; + dma_addr_t *cr_addr = &buf_info->base[EXYNOS_DRM_PLANAR_CR]; + uint64_t y_ofs, cb_ofs, cr_ofs; + + /* + * ToDo: check the buffer size between gem allocated buffers + * and each planar size. + */ + switch (fmt) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + y_ofs = sz->hsize * sz->vsize; + cb_ofs = y_ofs >> 1; + cr_ofs = 0; + break; + case DRM_FORMAT_NV12M: + y_ofs = ALIGN(ALIGN(sz->hsize, 16) * + ALIGN(sz->vsize, 16), SZ_2K); + cb_ofs = ALIGN(ALIGN(sz->hsize, 16) * + ALIGN(sz->vsize >> 1, 16), SZ_2K); + cr_ofs = 0; + break; + case DRM_FORMAT_NV12MT: + y_ofs = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize, 32), SZ_8K); + cb_ofs = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize >> 1, 32), SZ_8K); + cr_ofs = 0; + break; + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU444: + case DRM_FORMAT_YUV420M: + y_ofs = sz->hsize * sz->vsize; + cb_ofs = cr_ofs = y_ofs >> 2; + break; + default: + y_ofs = cb_ofs = cr_ofs = 0; + break; + } + + if (y_ofs && *y_addr) { + *cb_addr = *y_addr + y_ofs; + + if (cb_ofs && *cb_addr) + *cr_addr = *cb_addr + cb_ofs; + } + + return 0; +} + +static int gsc_src_set_fmt(struct device *dev, u32 fmt) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = gsc_read(GSC_IN_CON); + cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK | + GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK | + GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE); + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= GSC_IN_RGB565; + break; + case DRM_FORMAT_XRGB8888: + cfg |= GSC_IN_XRGB8888; + break; + case DRM_FORMAT_YUYV: + cfg |= (GSC_IN_YUV422_1P | + GSC_IN_YUV422_1P_ORDER_LSB_Y | + GSC_IN_CHROMA_ORDER_CBCR); + break; + case DRM_FORMAT_YVYU: + cfg |= (GSC_IN_YUV422_1P | + GSC_IN_YUV422_1P_ORDER_LSB_Y | + GSC_IN_CHROMA_ORDER_CRCB); + break; + case DRM_FORMAT_UYVY: + cfg |= (GSC_IN_YUV422_1P | + GSC_IN_YUV422_1P_OEDER_LSB_C | + GSC_IN_CHROMA_ORDER_CBCR); + break; + case DRM_FORMAT_VYUY: + cfg |= (GSC_IN_YUV422_1P | + GSC_IN_YUV422_1P_OEDER_LSB_C | + GSC_IN_CHROMA_ORDER_CRCB); + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + cfg |= (GSC_IN_CHROMA_ORDER_CRCB | + GSC_IN_YUV420_2P); + break; + case DRM_FORMAT_YUV422: + cfg |= GSC_IN_YUV422_3P; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= GSC_IN_YUV420_3P; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + case DRM_FORMAT_NV16: + cfg |= (GSC_IN_CHROMA_ORDER_CBCR | + GSC_IN_YUV420_2P); + break; + case DRM_FORMAT_NV12MT: + cfg |= (GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE); + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + gsc_write(cfg, GSC_IN_CON); + + return 0; +} + +static int gsc_src_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = gsc_read(GSC_IN_CON); + cfg &= ~GSC_IN_ROT_MASK; + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= GSC_IN_ROT_XFLIP; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= GSC_IN_ROT_YFLIP; + break; + case EXYNOS_DRM_DEGREE_90: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= GSC_IN_ROT_90_XFLIP; + else if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= GSC_IN_ROT_90_YFLIP; + else + cfg |= GSC_IN_ROT_90; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= GSC_IN_ROT_180; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= GSC_IN_ROT_270; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + gsc_write(cfg, GSC_IN_CON); + + return cfg ? 1 : 0; +} + +static int gsc_src_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_SRC]; + struct drm_exynos_pos img_pos = *pos; + struct drm_exynos_sz img_sz = *sz; + u32 cfg; + + /* ToDo: check width and height */ + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + img_sz.hsize = sz->vsize; + img_sz.vsize = sz->hsize; + } + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h); + + /* pixel offset */ + cfg = (GSC_SRCIMG_OFFSET_X(img_pos.x) | + GSC_SRCIMG_OFFSET_Y(img_pos.y)); + gsc_write(cfg, GSC_SRCIMG_OFFSET); + + /* cropped size */ + cfg = (GSC_CROPPED_WIDTH(img_pos.w) | + GSC_CROPPED_HEIGHT(img_pos.h)); + gsc_write(cfg, GSC_CROPPED_SIZE); + + DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* original size */ + cfg = gsc_read(GSC_SRCIMG_SIZE); + cfg &= ~(GSC_SRCIMG_HEIGHT_MASK | + GSC_SRCIMG_WIDTH_MASK); + + cfg |= (GSC_SRCIMG_WIDTH(sz->hsize) | + GSC_SRCIMG_HEIGHT(sz->vsize)); + + gsc_write(cfg, GSC_SRCIMG_SIZE); + + config->sz = img_sz; + config->pos = img_pos; + + return 0; +} + +static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + bool masked; + u32 cfg; + u32 mask = 0x00000001 << buf_id; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + /* mask register set */ + cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); + + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + masked = false; + break; + case IPP_BUF_CTRL_DEQUEUE: + masked = true; + break; + default: + dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); + return -EINVAL; + } + + /* sequence id */ + cfg &= (~mask); + cfg |= masked << buf_id; + gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK); + gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK); + gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK); + + return 0; +} + +static int gsc_src_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_SRC]; + int ret; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + if (buf_id > GSC_MAX_SRC) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + case IPP_BUF_CTRL_DEQUEUE: + ret = gsc_set_planar_addr(buf_info, config->fmt, &config->sz); + + if (ret) { + dev_err(dev, "failed to set plane addr.\n"); + return ret; + } + + gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + GSC_IN_BASE_ADDR_Y(buf_id)); + gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + GSC_IN_BASE_ADDR_CB(buf_id)); + gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + GSC_IN_BASE_ADDR_CR(buf_id)); + break; + default: + /* bypass */ + break; + } + + return gsc_src_set_buf_seq(ctx, buf_id, buf_ctrl); +} + +static struct exynos_drm_ipp_ops gsc_src_ops = { + .set_fmt = gsc_src_set_fmt, + .set_transf = gsc_src_set_transf, + .set_size = gsc_src_set_size, + .set_addr = gsc_src_set_addr, +}; + +static int gsc_dst_set_fmt(struct device *dev, u32 fmt) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = gsc_read(GSC_OUT_CON); + cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK | + GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK | + GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE); + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= GSC_OUT_RGB565; + break; + case DRM_FORMAT_XRGB8888: + cfg |= GSC_OUT_XRGB8888; + break; + case DRM_FORMAT_YUYV: + cfg |= (GSC_OUT_YUV422_1P | + GSC_OUT_YUV422_1P_ORDER_LSB_Y | + GSC_OUT_CHROMA_ORDER_CBCR); + break; + case DRM_FORMAT_YVYU: + cfg |= (GSC_OUT_YUV422_1P | + GSC_OUT_YUV422_1P_ORDER_LSB_Y | + GSC_OUT_CHROMA_ORDER_CRCB); + break; + case DRM_FORMAT_UYVY: + cfg |= (GSC_OUT_YUV422_1P | + GSC_OUT_YUV422_1P_OEDER_LSB_C | + GSC_OUT_CHROMA_ORDER_CBCR); + break; + case DRM_FORMAT_VYUY: + cfg |= (GSC_OUT_YUV422_1P | + GSC_OUT_YUV422_1P_OEDER_LSB_C | + GSC_OUT_CHROMA_ORDER_CRCB); + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + cfg |= (GSC_OUT_CHROMA_ORDER_CRCB | + GSC_OUT_YUV420_2P); + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= GSC_OUT_YUV420_3P; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + case DRM_FORMAT_NV16: + cfg |= (GSC_OUT_CHROMA_ORDER_CBCR | + GSC_OUT_YUV420_2P); + break; + case DRM_FORMAT_NV12MT: + cfg |= (GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE); + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + gsc_write(cfg, GSC_OUT_CON); + + return 0; +} + +static int gsc_dst_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = gsc_read(GSC_IN_CON); + cfg &= ~GSC_IN_ROT_MASK; + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= GSC_IN_ROT_XFLIP; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= GSC_IN_ROT_YFLIP; + break; + case EXYNOS_DRM_DEGREE_90: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= GSC_IN_ROT_90_XFLIP; + else if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= GSC_IN_ROT_90_YFLIP; + else + cfg |= GSC_IN_ROT_90; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= GSC_IN_ROT_180; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= GSC_IN_ROT_270; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + gsc_write(cfg, GSC_IN_CON); + + return cfg ? 1 : 0; +} + +static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift) +{ + DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); + + if (src >= dst * 64) { + DRM_ERROR("failed to make ratio and shift.\n"); + return -EINVAL; + } else if (src >= dst * 32) { + *ratio = 32; + *shift = 5; + } else if (src >= dst * 16) { + *ratio = 16; + *shift = 4; + } else if (src >= dst * 8) { + *ratio = 8; + *shift = 3; + } else if (src >= dst * 4) { + *ratio = 4; + *shift = 2; + } else if (src >= dst * 2) { + *ratio = 2; + *shift = 1; + } else { + *ratio = 1; + *shift = 0; + } + + return 0; +} + +static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, + struct drm_exynos_pos *src, struct drm_exynos_pos *dst) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + u32 hfactor, vfactor; + int ret = 0; + + ret = gsc_get_ratio_shift(src->w, dst->w, &sc->pre_hratio, &hfactor); + if (ret) { + dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); + return ret; + } + + ret = gsc_get_ratio_shift(src->h, dst->h, &sc->pre_vratio, &vfactor); + if (ret) { + dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); + return ret; + } + + DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", + __func__, sc->pre_hratio, hfactor, sc->pre_vratio, vfactor); + + sc->main_hratio = (src->w << 16) / (dst->w << hfactor); + sc->main_vratio = (src->h << 16) / (dst->h << vfactor); + DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n", + __func__, sc->main_hratio, sc->main_vratio); + + sc->pre_shfactor = 10 - (hfactor + vfactor); + DRM_DEBUG_KMS("%s:pre_shfactor[%d]\n", __func__, + sc->pre_shfactor); + + cfg = (GSC_PRESC_SHFACTOR(sc->pre_shfactor) | + GSC_PRESC_H_RATIO(sc->pre_hratio) | + GSC_PRESC_V_RATIO(sc->pre_vratio)); + gsc_write(cfg, GSC_PRE_SCALE_RATIO); + + return ret; +} + +static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n", + __func__, sc->main_hratio, sc->main_vratio); + + cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio); + gsc_write(cfg, GSC_MAIN_H_RATIO); + + cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio); + gsc_write(cfg, GSC_MAIN_V_RATIO); +} + +static int gsc_dst_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct drm_exynos_pos img_pos = *pos; + struct drm_exynos_sz img_sz = *sz; + struct gsc_scaler *sc = &ctx->sc; + u32 cfg; + + DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", + __func__, swap, pos->x, pos->y, pos->w, pos->h); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + img_sz.hsize = sz->vsize; + img_sz.vsize = sz->hsize; + } + + /* pixel offset */ + cfg = (GSC_DSTIMG_OFFSET_X(img_pos.x) | + GSC_DSTIMG_OFFSET_Y(img_pos.y)); + gsc_write(cfg, GSC_DSTIMG_OFFSET); + + /* scaled size */ + cfg = (GSC_SCALED_WIDTH(pos->w) | GSC_SCALED_HEIGHT(pos->h)); + gsc_write(cfg, GSC_SCALED_SIZE); + + DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n", + __func__, sz->hsize, sz->vsize); + + /* original size */ + cfg = gsc_read(GSC_DSTIMG_SIZE); + cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | + GSC_DSTIMG_WIDTH_MASK); + cfg |= (GSC_DSTIMG_WIDTH(img_sz.hsize) | + GSC_DSTIMG_HEIGHT(img_sz.vsize)); + gsc_write(cfg, GSC_DSTIMG_SIZE); + + cfg = gsc_read(GSC_OUT_CON); + cfg &= ~GSC_OUT_RGB_TYPE_MASK; + + if (pos->w >= GSC_WIDTH_ITU_709) + if (sc->range) + cfg |= GSC_OUT_RGB_HD_WIDE; + else + cfg |= GSC_OUT_RGB_HD_NARROW; + else + if (sc->range) + cfg |= GSC_OUT_RGB_SD_WIDE; + else + cfg |= GSC_OUT_RGB_SD_NARROW; + + gsc_write(cfg, GSC_OUT_CON); + + return 0; +} + +static int gsc_dst_get_buf_seq(struct gsc_context *ctx) +{ + u32 cfg, i, buf_num = GSC_REG_SZ; + u32 mask = 0x00000001; + + cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); + + for (i = 0; i < GSC_REG_SZ; i++) + if (cfg & (mask << i)) + buf_num--; + + DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); + + return buf_num; +} + +static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + bool masked; + u32 cfg; + u32 mask = 0x00000001 << buf_id; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + /* mask register set */ + cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); + + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + masked = false; + break; + case IPP_BUF_CTRL_DEQUEUE: + masked = true; + break; + default: + dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); + return -EINVAL; + } + + /* sequence id */ + cfg &= (~mask); + cfg |= masked << buf_id; + gsc_write(cfg, GSC_OUT_BASE_ADDR_Y_MASK); + gsc_write(cfg, GSC_OUT_BASE_ADDR_CB_MASK); + gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); + + /* interrupt enable */ + if (buf_ctrl == IPP_BUF_CTRL_QUEUE && + gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START) + gsc_handle_irq(ctx, true, false, true); + + return 0; +} + +static int gsc_dst_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_DST]; + int ret; + + DRM_DEBUG_KMS("%s:buf_id[%d]buf_ctrl[%d]\n", __func__, + buf_id, buf_ctrl); + + if (buf_id > GSC_MAX_DST) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + case IPP_BUF_CTRL_DEQUEUE: + ret = gsc_set_planar_addr(buf_info, config->fmt, &config->sz); + + if (ret) { + dev_err(dev, "failed to set plane addr.\n"); + return ret; + } + + gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + GSC_OUT_BASE_ADDR_Y(buf_id)); + gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + GSC_OUT_BASE_ADDR_CB(buf_id)); + gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + GSC_OUT_BASE_ADDR_CR(buf_id)); + break; + default: + /* bypass */ + break; + } + + return gsc_dst_set_buf_seq(ctx, buf_id, buf_ctrl); +} + +static struct exynos_drm_ipp_ops gsc_dst_ops = { + .set_fmt = gsc_dst_set_fmt, + .set_transf = gsc_dst_set_transf, + .set_size = gsc_dst_set_size, + .set_addr = gsc_dst_set_addr, +}; + +static int gsc_power_on(struct gsc_context *ctx, bool enable) +{ + DRM_DEBUG_KMS("%s:\n", __func__); + + if (enable) { + clk_enable(ctx->gsc_clk); + /* ToDo : wb_b_clk */ + ctx->suspended = false; + } else { + clk_disable(ctx->gsc_clk); + /* ToDo : wb_b_clk */ + ctx->suspended = true; + } + + return 0; +} + +static irqreturn_t gsc_irq_handler(int irq, void *dev_id) +{ + struct gsc_context *ctx = dev_id; + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg, status; + int buf_id = 0; + + DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); + + status = gsc_read(GSC_IRQ); + if (status & GSC_IRQ_STATUS_OR_IRQ) { + dev_err(ippdrv->dev, "occured overflow at %d, status 0x%x.\n", + ctx->id, status); + return IRQ_NONE; + } + + if (status & GSC_IRQ_STATUS_OR_FRM_DONE) { + dev_err(ippdrv->dev, "occured frame done at %d, status 0x%x.\n", + ctx->id, status); + /* ToDo: Frame control */ + } + + if (list_empty(&ippdrv->event_list)) { + DRM_DEBUG_KMS("%s:event list empty.\n", __func__); + + return IRQ_HANDLED; + } + + cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); + buf_id = GSC_IN_CURR_GET_INDEX(cfg); + if (buf_id < 0) + return IRQ_HANDLED; + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + + if (gsc_dst_set_buf_seq(ctx, buf_id, + IPP_BUF_CTRL_DEQUEUE) < 0) { + DRM_ERROR("failed to dequeue.\n"); + + return IRQ_HANDLED; + } + + ipp_send_event_handler(ippdrv, buf_id); + + if (gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP) + gsc_handle_irq(ctx, false, false, true); + + return IRQ_HANDLED; +} + +static int gsc_ippdrv_check_property(struct device *dev, + struct drm_exynos_ipp_property *property) +{ + /* ToDo: check valid using property information */ + DRM_DEBUG_KMS("%s\n", __func__); + + return 0; +} + +static int gsc_ippdrv_reset(struct device *dev) +{ + struct gsc_context *ctx = get_gsc_context(dev); + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* reset h/w block */ + ret = gsc_sw_reset(ctx); + if (ret < 0) { + dev_err(dev, "failed to reset hardware.\n"); + return ret; + } + + memset(&ctx->sc, 0x0, sizeof(ctx->sc)); + + return 0; +} + +static int gsc_check_prepare(struct gsc_context *ctx) +{ + /* ToDo: check prepare using read register */ + DRM_DEBUG_KMS("%s\n", __func__); + + return 0; +} + +static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_config *config = + &property->config[EXYNOS_DRM_OPS_DST]; + u32 cfg; + int ret; + int enable = 1; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + ret = gsc_check_prepare(ctx); + if (ret) { + dev_err(dev, "failed to check prepare.\n"); + return ret; + } + + ippdrv->cmd = cmd; + gsc_handle_irq(ctx, true, false, true); + + switch (cmd) { + case IPP_CMD_M2M: + /* bypass */ + break; + case IPP_CMD_WB: + gsc_set_gscblk_fimd_wb(ctx, enable); + exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)enable); + break; + case IPP_CMD_OUTPUT: + default: + ret = -EINVAL; + dev_err(dev, "invalid operations.\n"); + return ret; + } + + ret = gsc_set_prescaler(ctx, &ctx->sc, &config->pos, + &config->pos); + if (ret) { + dev_err(dev, "failed to set precalser.\n"); + return ret; + } + + gsc_set_scaler(ctx, &ctx->sc); + + cfg = gsc_read(GSC_ENABLE); + cfg |= GSC_ENABLE_ON; + gsc_write(cfg, GSC_ENABLE); + + return 0; +} + +static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + int enable = 0; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + switch (cmd) { + case IPP_CMD_M2M: + /* bypass */ + break; + case IPP_CMD_WB: + gsc_set_gscblk_fimd_wb(ctx, enable); + exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)enable); + break; + case IPP_CMD_OUTPUT: + default: + dev_err(dev, "invalid operations.\n"); + break; + } + + ippdrv->cmd = IPP_CMD_NONE; + gsc_handle_irq(ctx, false, false, true); + + /* reset sequence */ + gsc_write(0xff, GSC_OUT_BASE_ADDR_Y_MASK); + gsc_write(0xff, GSC_OUT_BASE_ADDR_CB_MASK); + gsc_write(0xff, GSC_OUT_BASE_ADDR_CR_MASK); + + cfg = gsc_read(GSC_ENABLE); + cfg &= ~GSC_ENABLE_ON; + gsc_write(cfg, GSC_ENABLE); +} + +#ifdef CONFIG_SLP_DISP_DEBUG +static int gsc_read_reg(struct gsc_context *ctx, char *buf) +{ + u32 cfg; + int i; + int pos = 0; + + pos += sprintf(buf+pos, "0x%.8x | ", GSC_BASE_REG(ctx->id)); + for (i = 1; i < GSC_MAX_REG + 1; i++) { + cfg = gsc_read((i-1) * sizeof(u32)); + pos += sprintf(buf+pos, "0x%.8x ", cfg); + if (i % 4 == 0) + pos += sprintf(buf+pos, "\n0x%.8x | ", + GSC_BASE_REG(ctx->id) + (i * sizeof(u32))); + } + + pos += sprintf(buf+pos, "\n"); + + return pos; +} + +static ssize_t show_read_reg(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gsc_context *ctx = get_gsc_context(dev); + + if (!ctx->regs) { + dev_err(dev, "failed to get current register.\n"); + return -EINVAL; + } + + return gsc_read_reg(ctx, buf); +} + +static struct device_attribute device_attrs[] = { + __ATTR(read_reg, S_IRUGO, show_read_reg, NULL), +}; +#endif + +static int __devinit gsc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gsc_context *ctx; + struct resource *res; + struct exynos_drm_ippdrv *ippdrv; + struct exynos_drm_gsc_pdata *pdata; + int ret = -EINVAL; +#ifdef CONFIG_SLP_DISP_DEBUG + int i; +#endif + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data specified.\n"); + return -EINVAL; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + /* clock control */ + ctx->gsc_clk = clk_get(dev, "gscl"); + if (IS_ERR(ctx->gsc_clk)) { + dev_err(dev, "failed to get gsc clock.\n"); + ret = PTR_ERR(ctx->gsc_clk); + goto err_ctx; + } + + /* resource memory */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to find registers.\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!ctx->regs_res) { + dev_err(dev, "failed to claim register region.\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs = ioremap(res->start, resource_size(res)); + if (!ctx->regs) { + dev_err(dev, "failed to map registers.\n"); + ret = -ENXIO; + goto err_req_region; + } + + /* resource irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to request irq resource.\n"); + goto err_get_regs; + } + + ctx->irq = res->start; + ret = request_threaded_irq(ctx->irq, NULL, gsc_irq_handler, + IRQF_ONESHOT, "drm_gsc", ctx); + if (ret < 0) { + dev_err(dev, "failed to request irq.\n"); + goto err_get_regs; + } + + /* context initailization */ + ctx->id = pdev->id; + ctx->capa = gsc51_capa; + if (!ctx->capa) { + dev_err(dev, "failed to get capability.\n"); + goto err_get_irq; + } + +#ifdef CONFIG_SLP_DISP_DEBUG + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { + ret = device_create_file(&(pdev->dev), + &device_attrs[i]); + if (ret) + break; + } + + if (ret < 0) + dev_err(&pdev->dev, "failed to add sysfs entries\n"); +#endif + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + ippdrv = &ctx->ippdrv; + ippdrv->dev = dev; + ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &gsc_src_ops; + ippdrv->ops[EXYNOS_DRM_OPS_DST] = &gsc_dst_ops; + ippdrv->check_property = gsc_ippdrv_check_property; + ippdrv->reset = gsc_ippdrv_reset; + ippdrv->start = gsc_ippdrv_start; + ippdrv->stop = gsc_ippdrv_stop; + + mutex_init(&ctx->lock); + platform_set_drvdata(pdev, ctx); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = exynos_drm_ippdrv_register(ippdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm gsc device.\n"); + goto err_get_irq; + } + + dev_info(&pdev->dev, "drm gsc registered successfully.\n"); + + return 0; + +err_get_irq: + free_irq(ctx->irq, ctx); +err_get_regs: + iounmap(ctx->regs); +err_req_region: + release_resource(ctx->regs_res); + kfree(ctx->regs_res); +err_clk: + clk_put(ctx->gsc_clk); +err_ctx: + kfree(ctx); + return ret; +} + +static int __devexit gsc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gsc_context *ctx = get_gsc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + + exynos_drm_ippdrv_unregister(ippdrv); + + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + free_irq(ctx->irq, ctx); + iounmap(ctx->regs); + release_resource(ctx->regs_res); + kfree(ctx->regs_res); + + clk_put(ctx->gsc_clk); + + kfree(ctx); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gsc_suspend(struct device *dev) +{ + struct gsc_context *ctx = get_gsc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + if (pm_runtime_suspended(dev)) + return 0; + /* ToDo */ + return gsc_power_on(ctx, false); +} + +static int gsc_resume(struct device *dev) +{ + struct gsc_context *ctx = get_gsc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + if (!pm_runtime_suspended(dev)) + return gsc_power_on(ctx, true); + /* ToDo */ + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int gsc_runtime_suspend(struct device *dev) +{ + struct gsc_context *ctx = get_gsc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + /* ToDo */ + return gsc_power_on(ctx, false); +} + +static int gsc_runtime_resume(struct device *dev) +{ + struct gsc_context *ctx = get_gsc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __FILE__, ctx->id); + /* ToDo */ + return gsc_power_on(ctx, true); +} +#endif + +static const struct dev_pm_ops gsc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(gsc_suspend, gsc_resume) + SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) +}; + +/* ToDo: need to check use case platform_device_id */ +struct platform_driver gsc_driver = { + .probe = gsc_probe, + .remove = __devexit_p(gsc_remove), + .driver = { + .name = "exynos-drm-gsc", + .owner = THIS_MODULE, + .pm = &gsc_pm_ops, + }, +}; + diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_connector.h b/drivers/gpu/drm/exynos/exynos_drm_gsc.h index 1c7b2b5..546089e 100644 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_connector.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.h @@ -1,9 +1,8 @@ /* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> + * Eunchul Kim <chulspro.kim@samsung.com> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -25,10 +24,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef _EXYNOS_DRM_CONNECTOR_H_ -#define _EXYNOS_DRM_CONNECTOR_H_ +#ifndef _EXYNOS_DRM_GSC_H_ +#define _EXYNOS_DRM_GSC_H_ + +/* ToDo */ -struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, - struct drm_encoder *encoder); +#endif /* _EXYNOS_DRM_GSC_H_ */ -#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index f2ffa68..b0a8e1c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c @@ -37,13 +37,13 @@ static DEFINE_MUTEX(iommu_mutex); struct exynos_iommu_ops { - int (*setup)(struct device *dev); - void (*cleanup)(struct device *dev); - int (*activate)(struct device *dev); - void (*deactivate)(struct device *dev); - dma_addr_t (*map)(struct device *dev, struct scatterlist *sg, + void *(*setup)(unsigned long s_iova, unsigned long size); + void (*cleanup)(void *in_vmm); + int (*activate)(void *in_vmm, struct device *dev); + void (*deactivate)(void *in_vmm, struct device *dev); + dma_addr_t (*map)(void *in_vmm, struct scatterlist *sg, off_t offset, size_t size); - void (*unmap)(struct device *dev, dma_addr_t iova); + void (*unmap)(void *in_vmm, dma_addr_t iova); }; static const struct exynos_iommu_ops iommu_ops = { @@ -55,74 +55,20 @@ static const struct exynos_iommu_ops iommu_ops = { .unmap = iovmm_unmap }; -static bool check_iommu_map_params(struct iommu_gem_map_params *params) -{ - if (!params) { - DRM_ERROR("params is null.\n"); - return false; - } - - if (!params->dev || !params->drm_dev || !params->file) { - DRM_ERROR("invalid params.\n"); - return false; - } - - return true; -} - -void exynos_drm_remove_iommu_list(struct list_head *iommu_list, - void *gem_obj) -{ - struct iommu_info_node *im, *t_im; - - list_for_each_entry_safe(im, t_im, iommu_list, list) { - if (im->gem_obj == gem_obj) { - list_del(&im->list); - kfree(im); - im = NULL; - break; - } - } - -} - -dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, - struct list_head *iommu_list, - unsigned int gem_handle, - enum iommu_types type) +dma_addr_t exynos_drm_iommu_map_gem(struct drm_device *drm_dev, + struct drm_gem_object *obj) { + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; struct sg_table *sgt; - struct iommu_info_node *node; - struct exynos_drm_gem_obj *obj; - dma_addr_t dma_addr; - - if (!is_iommu_type_valid(type)) { - DRM_ERROR("invalid iommu type.\n"); - return 0; - } - - if (!check_iommu_map_params(params)) - return 0; - - /* get gem object from specific gem framework. */ - obj = exynos_drm_gem_get_obj(params->drm_dev, gem_handle, - params->file); - if (IS_ERR(obj)) - return 0; + dma_addr_t dev_addr; mutex_lock(&iommu_mutex); - /* - * if this gem object had already been mapped to iommu then - * return dma address mapped before this time. - */ - if (obj->iommu_info.mapped & (1 << type)) { - DRM_DEBUG_KMS("already mapped to iommu"); - mutex_unlock(&iommu_mutex); - return obj->iommu_info.dma_addrs[type]; - } + exynos_gem_obj = to_exynos_gem_obj(obj); - sgt = obj->buffer->sgt; + buf = exynos_gem_obj->buffer; + sgt = buf->sgt; /* * if not using iommu, just return base address to physical @@ -132,102 +78,64 @@ dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, mutex_unlock(&iommu_mutex); return sg_dma_address(&sgt->sgl[0]); } - mutex_unlock(&iommu_mutex); /* - * allocate device address space for this driver and then - * map all pages contained in sg list to iommu table. + * if a gem buffer was already mapped with iommu table then + * just return dev_addr; + * + * Note: device address is unique to system globally. */ - dma_addr = iommu_ops.map(params->dev, sgt->sgl, (off_t)0, - (size_t)obj->size); - if (!dma_addr) { + if (buf->dev_addr) { mutex_unlock(&iommu_mutex); - return dma_addr; + return buf->dev_addr; } - mutex_lock(&iommu_mutex); - /* - * check map flag bit and device address mapped to iommu. - * this data would be used to avoid duplicated mapping. - */ - obj->iommu_info.mapped |= (1 << type); - obj->iommu_info.dma_addrs[type] = dma_addr; - obj->iommu_info.devs[type] = params->dev; - obj->iommu_info.iommu_lists[type] = iommu_list; - - params->gem_obj = obj; - - /* - * this gem object is referenced by this driver so - * the object refcount should be increased. + * allocate device address space for this driver and then + * map all pages contained in sg list to iommu table. */ - drm_gem_object_reference(&obj->base); - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - DRM_ERROR("failed to allocate iommu node.\n"); - dma_addr = 0; - goto err; + dev_addr = iommu_ops.map(exynos_gem_obj->vmm, sgt->sgl, (off_t)0, + (size_t)obj->size); + if (!dev_addr) { + mutex_unlock(&iommu_mutex); + return dev_addr; } - node->gem_obj = obj; - node->dma_addr = dma_addr; mutex_unlock(&iommu_mutex); - list_add_tail(&node->list, iommu_list); - - return dma_addr; -err: - mutex_unlock(&iommu_mutex); - iommu_ops.unmap(params->dev, dma_addr); - return dma_addr; + return dev_addr; } -void exynos_drm_iommu_unmap_gem(struct iommu_gem_map_params *params, - dma_addr_t dma_addr, - enum iommu_types type) +void exynos_drm_iommu_unmap_gem(struct drm_gem_object *obj) { - struct exynos_drm_gem_obj *obj; - - if (!iommu_ops.unmap) - return; + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; - if (!is_iommu_type_valid(type)) { - DRM_ERROR("invalid iommu type.\n"); + if (!iommu_ops.unmap || !obj) return; - } - if (!check_iommu_map_params(params)) - return; - - if (!params->gem_obj) - return; + exynos_gem_obj = to_exynos_gem_obj(obj); + buf = exynos_gem_obj->buffer; - obj = (struct exynos_drm_gem_obj *)params->gem_obj; + /* workaround */ + usleep_range(15000, 20000); mutex_lock(&iommu_mutex); - if (!(obj->iommu_info.mapped & (1 << type))) { - DRM_DEBUG_KMS("not already mapped to iommu so just return\n"); + + if (!buf->dev_addr) { mutex_unlock(&iommu_mutex); + DRM_DEBUG_KMS("not mapped with iommu table.\n"); return; } - /* uncheck map flag bit. */ - obj->iommu_info.mapped &= ~(1 << type); - obj->iommu_info.dma_addrs[type] = 0; - mutex_unlock(&iommu_mutex); - - iommu_ops.unmap(params->dev, dma_addr); + if (exynos_gem_obj->vmm) + iommu_ops.unmap(exynos_gem_obj->vmm, buf->dev_addr); - /* - * drop this gem object refcount to release allocated buffer - * and resources. - */ - drm_gem_object_unreference_unlocked(&obj->base); + buf->dev_addr = 0; + mutex_unlock(&iommu_mutex); } -dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, +dma_addr_t exynos_drm_iommu_map(void *in_vmm, dma_addr_t paddr, size_t size) { struct sg_table *sgt; @@ -244,13 +152,13 @@ dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, sgt = kzalloc(sizeof(struct sg_table) * npages, GFP_KERNEL); if (!sgt) { - dev_err(dev, "failed to allocate sg table.\n"); + DRM_ERROR("failed to allocate sg table.\n"); return dma_addr; } ret = sg_alloc_table(sgt, npages, GFP_KERNEL); if (ret < 0) { - dev_err(dev, "failed to initialize sg table.\n"); + DRM_ERROR("failed to initialize sg table.\n"); goto err; } @@ -270,9 +178,9 @@ dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, * allocate device address space for this driver and then * map all pages contained in sg list to iommu table. */ - dma_addr = iommu_ops.map(dev, sgt->sgl, (off_t)0, (size_t)size); + dma_addr = iommu_ops.map(in_vmm, sgt->sgl, (off_t)0, (size_t)size); if (!dma_addr) - dev_err(dev, "failed to map cmdlist pool.\n"); + DRM_ERROR("failed to map cmdlist pool.\n"); sg_free_table(sgt); err: @@ -283,45 +191,45 @@ err: } -void exynos_drm_iommu_unmap(struct device *dev, dma_addr_t dma_addr) +void exynos_drm_iommu_unmap(void *in_vmm, dma_addr_t dma_addr) { if (iommu_ops.unmap) - iommu_ops.unmap(dev, dma_addr); + iommu_ops.unmap(in_vmm, dma_addr); } -int exynos_drm_iommu_setup(struct device *dev) +void *exynos_drm_iommu_setup(unsigned long s_iova, unsigned long size) { /* * allocate device address space to this driver and add vmm object * to s5p_iovmm_list. please know that each iommu will use * 1GB as its own device address apace. * - * the device address space : 0x80000000 ~ 0xA0000000 + * the device address space : s_iova ~ s_iova + size */ if (iommu_ops.setup) - return iommu_ops.setup(dev); + return iommu_ops.setup(s_iova, size); - return 0; + return ERR_PTR(-EINVAL); } -int exynos_drm_iommu_activate(struct device *dev) +int exynos_drm_iommu_activate(void *in_vmm, struct device *dev) { if (iommu_ops.activate) - return iovmm_activate(dev); + return iovmm_activate(in_vmm, dev); return 0; } -void exynos_drm_iommu_deactivate(struct device *dev) +void exynos_drm_iommu_deactivate(void *in_vmm, struct device *dev) { if (iommu_ops.deactivate) - iommu_ops.deactivate(dev); + iommu_ops.deactivate(in_vmm, dev); } -void exynos_drm_iommu_cleanup(struct device *dev) +void exynos_drm_iommu_cleanup(void *in_vmm) { if (iommu_ops.cleanup) - iommu_ops.cleanup(dev); + iommu_ops.cleanup(in_vmm); } MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h index aa267ba..35d923d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h @@ -26,54 +26,33 @@ #ifndef _EXYNOS_DRM_IOMMU_H_ #define _EXYNOS_DRM_IOMMU_H_ -enum iommu_types { - IOMMU_FIMD = 0, - IOMMU_HDMI, - IOMMU_G2D, - IOMMU_FIMC, - IOMMU_G3D, - IOMMU_ROTATOR, - IOMMU_MASK = 0x3f +struct exynos_iommu_gem_data { + unsigned int gem_handle_in; + void *gem_obj_out; }; -struct iommu_gem_map_params { - struct device *dev; - struct drm_device *drm_dev; - struct drm_file *file; - void *gem_obj; -}; - -#define is_iommu_type_valid(t) (((1 << (t)) & ~(IOMMU_MASK)) ? false : true) - -void exynos_drm_remove_iommu_list(struct list_head *iommu_list, - void *gem_obj); - /* get all pages to gem object and map them to iommu table. */ -dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, - struct list_head *iommu_list, - unsigned int gem_handle, - enum iommu_types type); +dma_addr_t exynos_drm_iommu_map_gem(struct drm_device *drm_dev, + struct drm_gem_object *obj); /* unmap device address space to gem object from iommu table. */ -void exynos_drm_iommu_unmap_gem(struct iommu_gem_map_params *params, - dma_addr_t dma_addr, - enum iommu_types type); +void exynos_drm_iommu_unmap_gem(struct drm_gem_object *obj); /* map physical memory region pointed by paddr to iommu table. */ -dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, +dma_addr_t exynos_drm_iommu_map(void *in_vmm, dma_addr_t paddr, size_t size); -/* unmap device address space pointed by dma_addr from iommu table. */ -void exynos_drm_iommu_unmap(struct device *dev, dma_addr_t dma_addr); +/* unmap device address space pointed by dev_addr from iommu table. */ +void exynos_drm_iommu_unmap(void *in_vmm, dma_addr_t dev_addr); /* setup device address space for device iommu. */ -int exynos_drm_iommu_setup(struct device *dev); +void *exynos_drm_iommu_setup(unsigned long s_iova, unsigned long size); -int exynos_drm_iommu_activate(struct device *dev); +int exynos_drm_iommu_activate(void *in_vmm, struct device *dev); -void exynos_drm_iommu_deactivate(struct device *dev); +void exynos_drm_iommu_deactivate(void *in_vmm, struct device *dev); /* clean up allocated device address space for device iommu. */ -void exynos_drm_iommu_cleanup(struct device *dev); +void exynos_drm_iommu_cleanup(void *in_vmm); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c new file mode 100644 index 0000000..0293fbf --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -0,0 +1,1638 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include "drmP.h" +#include "drm_backlight.h" +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/cma.h> +#include <plat/map-base.h> + +#include <drm/exynos_drm.h> +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" +#include "exynos_drm_iommu.h" +#include "exynos_drm_ipp.h" + +/* + * IPP is stand for Image Post Processing and + * supports image scaler/rotator and input/output DMA operations. + * using FIMC, GSC, Rotator, so on. + * IPP is integration device driver of same attribute h/w + */ + +#define get_ipp_context(dev) platform_get_drvdata(to_platform_device(dev)) + +/* + * A structure of event. + * + * @base: base of event. + * @event: ipp event. + */ +struct drm_exynos_ipp_send_event { + struct drm_pending_event base; + struct drm_exynos_ipp_event event; +}; + +/* + * A structure of command node. + * + * @list: list head to command queue information. + * @mem_list: list head to source,destination memory queue information. + * @property: property information. + */ +struct drm_exynos_ipp_cmd_node { + struct list_head list; + struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; + struct drm_exynos_ipp_property property; +}; + +/* + * A structure of memory node. + * + * @list: list head to memory queue information. + * @ops_id: id of operations. + * @prop_id: id of property. + * @buf_id: id of buffer. + * @buf_info: gem objects and dma address, size. + */ +struct drm_exynos_ipp_mem_node { + struct list_head list; + enum drm_exynos_ops_id ops_id; + u32 prop_id; + u32 buf_id; + struct drm_exynos_ipp_buf_info buf_info; +}; + +/* + * A structure of ipp context. + * + * @subdrv: prepare initialization using subdrv. + * @lock: locking of operations. + * @ipp_idr: ipp driver idr. + * @sched_event: schdule event list + * @sched_cmd: schdule command list + */ +struct ipp_context { + struct exynos_drm_subdrv subdrv; + struct mutex lock; + struct idr ipp_idr; + struct work_struct sched_event; + struct work_struct sched_cmd; +}; + +static LIST_HEAD(exynos_drm_ippdrv_list); +static BLOCKING_NOTIFIER_HEAD(exynos_drm_ippnb_list); + +int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ippdrv) + return -EINVAL; + + list_add_tail(&ippdrv->list, &exynos_drm_ippdrv_list); + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_drm_ippdrv_register); + +int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ippdrv) + return -EINVAL; + + list_del(&ippdrv->list); + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_drm_ippdrv_unregister); + +int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct exynos_drm_ippdrv *ippdrv; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, list) { + /* check idle state and dedicated state */ + if (ippdrv->state == IPP_STATE_START && + ippdrv->dedicated) + continue; + + /* ToDo: get property */ + DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, ippdrv->ipp_id); + + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(exynos_drm_ipp_get_property); + +static int ipp_create_id(struct idr *id_idr, void *obj, u32 *idp) +{ + int ret = -EINVAL; + + /* ToDo: need spin_lock ? */ + +again: + /* ensure there is space available to allocate a handle */ + if (idr_pre_get(id_idr, GFP_KERNEL) == 0) + return -ENOMEM; + + ret = idr_get_new_above(id_idr, obj, 1, (int *)idp); + if (ret == -EAGAIN) + goto again; + + return ret; +} + +static void *ipp_find_id(struct idr *id_idr, u32 id) +{ + void *obj; + + /* ToDo: need spin_lock ? */ + + /* find object using handle */ + obj = idr_find(id_idr, id); + if (obj == NULL) + return NULL; + + return obj; +} + +static struct exynos_drm_ippdrv + *ipp_find_driver(struct ipp_context *ctx, + struct drm_exynos_ipp_property *property) +{ + struct exynos_drm_ippdrv *ippdrv; + u32 ipp_id = property->ipp_id; + + DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, ipp_id); + + if (ipp_id) { + /* find ipp driver */ + ippdrv = ipp_find_id(&ctx->ipp_idr, ippdrv->ipp_id); + if (!ippdrv) { + DRM_ERROR("not found ipp%d driver.\n", ipp_id); + return NULL; + } + + /* check idle state and dedicated state */ + if (ippdrv->state == IPP_STATE_START && + ippdrv->dedicated) { + DRM_ERROR("used choose device.\n"); + return NULL; + } + + /* check property */ + if (ippdrv->check_property && + ippdrv->check_property(ippdrv->dev, property)) { + DRM_ERROR("not support property.\n"); + return NULL; + } + + return ippdrv; + } else { + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, list) { + /* check idle state and dedicated state */ + if (ippdrv->state == IPP_STATE_IDLE && + ippdrv->dedicated) + continue; + + /* check property */ + if (ippdrv->check_property && + ippdrv->check_property(ippdrv->dev, property)) { + DRM_DEBUG_KMS("not support property.\n"); + continue; + } + + return ippdrv; + } + + DRM_ERROR("not support ipp driver operations.\n"); + } + + return NULL; +} + +int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct device *dev = priv->dev; + struct ipp_context *ctx = get_ipp_context(dev); + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_property *property = data; + int ret, i; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ctx) { + DRM_ERROR("invalid context.\n"); + return -EINVAL; + } + + if (!property) { + DRM_ERROR("invalid property parameter.\n"); + return -EINVAL; + } + + /* find ipp driver using ipp id */ + ippdrv = ipp_find_driver(ctx, property); + if (!ippdrv) { + DRM_ERROR("failed to get ipp driver.\n"); + return -EINVAL; + } + + /* allocate command node */ + c_node = kzalloc(sizeof(*c_node), GFP_KERNEL); + if (!c_node) { + DRM_ERROR("failed to allocate map node.\n"); + return -ENOMEM; + } + + /* create property id */ + ret = ipp_create_id(&ippdrv->prop_idr, c_node, &property->prop_id); + if (ret) { + DRM_ERROR("failed to create id.\n"); + goto err_clear; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + + /* stored property information and ippdrv in private data */ + c_node->property = *property; + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) + INIT_LIST_HEAD(&c_node->mem_list[i]); + + /* make dedicated state without m2m */ + if (property->cmd != IPP_CMD_M2M) + ippdrv->dedicated = true; + priv->ippdrv = ippdrv; + + list_add_tail(&c_node->list, &ippdrv->cmd_list); + + return 0; + +err_clear: + kfree(c_node); + + return ret; +} +EXPORT_SYMBOL_GPL(exynos_drm_ipp_set_property); + +static struct drm_exynos_ipp_cmd_node + *ipp_find_cmd_node(struct exynos_drm_ippdrv *ippdrv, u32 prop_id) +{ + struct drm_exynos_ipp_cmd_node *c_node; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + + /* ToDo: same with find_cmd_node and find_id */ + + /* find ipp driver */ + c_node = ipp_find_id(&ippdrv->prop_idr, prop_id); + if (!c_node) { + DRM_ERROR("not found property%d.\n", prop_id); + return NULL; + } + + return c_node; +} + +static struct drm_exynos_ipp_mem_node + *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_buf *buf) +{ + struct drm_exynos_ipp_mem_node *m_node; + int count = 0; + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf->buf_id); + + /* find memory node entry */ + list_for_each_entry(m_node, &c_node->mem_list[buf->ops_id], list) { + DRM_DEBUG_KMS("%s:count[%d]c_node[0x%x]\n", + __func__, count++, (int)c_node); + + /* compare buffer id */ + if (m_node->buf_id == buf->buf_id) + return m_node; + } + + return NULL; +} + +static struct drm_exynos_ipp_property + *ipp_find_property(struct exynos_drm_ippdrv *ippdrv, u32 prop_id) +{ + struct drm_exynos_ipp_property *property; + struct drm_exynos_ipp_cmd_node *c_node; + int count = 0; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + + /* find command node entry */ + list_for_each_entry(c_node, &ippdrv->cmd_list, list) { + DRM_DEBUG_KMS("%s:count[%d]c_node[0x%x]\n", + __func__, count++, (int)c_node); + + property = &c_node->property; + /* compare property id */ + if (property->prop_id == prop_id) + return property; + } + + return NULL; +} + +static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_property *property) +{ + struct exynos_drm_ipp_ops *ops = NULL; + int ret, i, swap = 0; + + if (!property) { + DRM_ERROR("invalid property parameter.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + + /* reset h/w block */ + if (ippdrv->reset && + ippdrv->reset(ippdrv->dev)) { + DRM_ERROR("failed to reset.\n"); + return -EINVAL; + } + + /* set source,destination operations */ + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* ToDo: integrate property and config */ + struct drm_exynos_ipp_config *config = + &property->config[i]; + + ops = ippdrv->ops[i]; + if (!ops || !config) { + DRM_ERROR("not support ops and config.\n"); + return -EINVAL; + } + + /* set format */ + if (ops->set_fmt) { + ret = ops->set_fmt(ippdrv->dev, config->fmt); + if (ret) { + DRM_ERROR("not support format.\n"); + return ret; + } + } + + /* set transform for rotation, flip */ + if (ops->set_transf) { + swap = ops->set_transf(ippdrv->dev, config->degree, + config->flip); + if (swap < 0) { + DRM_ERROR("not support tranf.\n"); + return -EINVAL; + } + } + + /* set size */ + if (ops->set_size) { + ret = ops->set_size(ippdrv->dev, swap, &config->pos, + &config->sz); + if (ret) { + DRM_ERROR("not support size.\n"); + return ret; + } + } + } + + return 0; +} + +static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_mem_node *node) +{ + struct exynos_drm_ipp_ops *ops = NULL; + int ret; + + DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)node); + + if (!node) { + DRM_ERROR("invalid queue node.\n"); + ret = -EFAULT; + return ret; + } + + DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, node->ops_id); + + /* get operations callback */ + ops = ippdrv->ops[node->ops_id]; + if (!ops) { + DRM_DEBUG_KMS("not support ops.\n"); + ret = -EIO; + return ret; + } + + /* set address and enable irq */ + if (ops->set_addr) { + ret = ops->set_addr(ippdrv->dev, &node->buf_info, + node->buf_id, IPP_BUF_CTRL_QUEUE); + if (ret) { + if (ret != -ENOMEM) + DRM_ERROR("failed to set addr.\n"); + return ret; + } + } + + return 0; +} + +static int ipp_free_mem_node(struct drm_device *drm_dev, + struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_mem_node *node) +{ + int ret, i; + + DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)node); + + if (!node) { + DRM_ERROR("invalid queue node.\n"); + ret = -EFAULT; + return ret; + } + + DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, node->ops_id); + + /* put gem buffer */ + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) { + void *gem_obj = node->buf_info.gem_objs[i]; + + if (gem_obj) + exynos_drm_gem_put_dma_addr(drm_dev, gem_obj); + } + + /* delete list in queue */ + list_del(&node->list); + kfree(node); + + return 0; +} + +/* ToDo: Merge with stop property */ +static void ipp_free_cmd_list(struct drm_device *drm_dev, + struct exynos_drm_ippdrv *ippdrv) +{ + struct drm_exynos_ipp_cmd_node *c_node, *tc_node; + struct drm_exynos_ipp_mem_node *m_node, *tm_node; + struct list_head *head; + int ret, i, count = 0; + + /* get command node entry */ + list_for_each_entry_safe(c_node, tc_node, + &ippdrv->cmd_list, list) { + DRM_DEBUG_KMS("%s:count[%d]c_node[0x%x]\n", + __func__, count++, (int)c_node); + + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + /* get memory node entry */ + list_for_each_entry_safe(m_node, tm_node, head, list) { + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, + m_node); + if (ret) + DRM_ERROR("failed to free m node.\n"); + } + } + + /* delete list */ + list_del(&c_node->list); + kfree(c_node); + } + + return; +} + +static int ipp_start_property(struct drm_device *drm_dev, + struct exynos_drm_ippdrv *ippdrv, u32 prop_id) +{ + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node, tm_node; + struct drm_exynos_ipp_property *property; + struct list_head *head; + int ret, i; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + + /* find command node */ + c_node = ipp_find_cmd_node(ippdrv, prop_id); + if (!c_node) { + DRM_ERROR("invalid command node list.\n"); + return -EINVAL; + } + + /* get property */ + property = &c_node->property; + if (property->prop_id != prop_id) { + DRM_ERROR("invalid property id.\n"); + return -EINVAL; + } + + /* set current property in ippdrv */ + ippdrv->property = property; + ret = ipp_set_property(ippdrv, property); + if (ret) { + DRM_ERROR("failed to set property.\n"); + ippdrv->property = NULL; + return ret; + } + + /* check command type */ + switch (property->cmd) { + case IPP_CMD_M2M: + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + if (list_empty(head)) { + DRM_DEBUG_KMS("bypass empty list.\n"); + return 0; + } + + /* get first entry */ + m_node = list_first_entry(head, + struct drm_exynos_ipp_mem_node, list); + if (!m_node) { + DRM_DEBUG_KMS("failed to get node.\n"); + ret = -EFAULT; + return ret; + } + + DRM_DEBUG_KMS("%s:m_node[0x%x]\n", + __func__, (int)m_node); + + /* must be set 0 src buffer id in m2m */ + if (i == EXYNOS_DRM_OPS_SRC) { + tm_node = *m_node; + tm_node.buf_id = 0; + m_node = &tm_node; + } + + /* set memory node to ippdrv */ + ret = ipp_set_mem_node(ippdrv, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + break; + case IPP_CMD_WB: + /* destination memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; + + /* get list entry */ + list_for_each_entry(m_node, head, list) { + /* set memory node to ippdrv */ + ret = ipp_set_mem_node(ippdrv, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + break; + case IPP_CMD_OUTPUT: + /* source memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; + + /* get list entry */ + list_for_each_entry(m_node, head, list) { + /* set memory node to ippdrv */ + ret = ipp_set_mem_node(ippdrv, m_node); + if (ret) { + DRM_ERROR("failed to set m node.\n"); + return ret; + } + } + break; + default: + DRM_ERROR("invalid operations.\n"); + ret = -EINVAL; + return ret; + } + + /* start operations */ + if (ippdrv->start) { + ret = ippdrv->start(ippdrv->dev, property->cmd); + if (ret) { + DRM_ERROR("failed to start ops.\n"); + return ret; + } + } + + return 0; +} + +static int ipp_stop_property(struct drm_device *drm_dev, + struct exynos_drm_ippdrv *ippdrv, u32 prop_id) +{ + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node, *tm_node; + struct drm_exynos_ipp_property *property; + enum drm_exynos_ipp_cmd cmd; + struct list_head *head; + int ret, i; + + DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + + /* find command node */ + c_node = ipp_find_cmd_node(ippdrv, prop_id); + if (!c_node) { + DRM_ERROR("invalid command node list.\n"); + return -EINVAL; + } + + /* get property */ + property = &c_node->property; + if (property->prop_id != prop_id) { + DRM_ERROR("invalid property id.\n"); + return -EINVAL; + } + + /* copy current command for memory list */ + cmd = property->cmd; + + /* stop operations */ + if (ippdrv->stop) + ippdrv->stop(ippdrv->dev, property->cmd); + + /* check command type */ + switch (property->cmd) { + case IPP_CMD_M2M: + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + /* get list entry */ + list_for_each_entry_safe(m_node, tm_node, + head, list) { + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, + m_node); + if (ret) { + DRM_ERROR("failed to free m node.\n"); + return ret; + } + } + } + break; + case IPP_CMD_WB: + /* destination memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; + + /* get list entry */ + list_for_each_entry_safe(m_node, tm_node, head, list) { + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, m_node); + if (ret) { + DRM_ERROR("failed to free m node.\n"); + return ret; + } + } + break; + case IPP_CMD_OUTPUT: + /* source memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; + + /* get list entry */ + list_for_each_entry_safe(m_node, tm_node, head, list) { + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, m_node); + if (ret) { + DRM_ERROR("failed to free m node.\n"); + return ret; + } + } + break; + default: + DRM_ERROR("invalid operations.\n"); + ret = -EINVAL; + return ret; + } + + /* delete list */ + list_del(&c_node->list); + kfree(c_node); + + return 0; +} + +static void ipp_free_event(struct drm_pending_event *event) +{ + kfree(event); +} + +static int ipp_make_event(struct drm_device *drm_dev, struct drm_file *file, + struct exynos_drm_ippdrv *ippdrv, struct drm_exynos_ipp_buf *buf) +{ + struct drm_exynos_ipp_send_event *e; + unsigned long flags; + + DRM_DEBUG_KMS("%s:ops_id[%d]buf_id[%d]\n", __func__, + buf->ops_id, buf->buf_id); + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + DRM_ERROR("failed to allocate event.\n"); + + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + return -ENOMEM; + } + + DRM_DEBUG_KMS("%s:e[0x%x]\n", __func__, (int)e); + + /* make event */ + e->event.base.type = DRM_EXYNOS_IPP_EVENT; + e->event.base.length = sizeof(e->event); + e->event.user_data = buf->user_data; + e->event.buf_id[EXYNOS_DRM_OPS_DST] = buf->buf_id; + e->base.event = &e->event.base; + e->base.file_priv = file; + e->base.destroy = ipp_free_event; + + list_add_tail(&e->base.link, &ippdrv->event_list); + + return 0; +} + +static struct drm_exynos_ipp_mem_node + *ipp_make_mem_node(struct drm_device *drm_dev, + struct drm_file *file, + struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_buf *buf) +{ + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node; + struct drm_exynos_ipp_buf_info buf_info; + void *addr; + unsigned long size; + int i; + + m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); + if (!m_node) { + DRM_ERROR("failed to allocate queue node.\n"); + return NULL; + } + + /* clear base address for error handling */ + memset(&buf_info, 0x0, sizeof(buf_info)); + + /* find command node */ + c_node = ipp_find_cmd_node(ippdrv, buf->prop_id); + if (!c_node) { + DRM_ERROR("failed to get command node.\n"); + goto err_clear; + } + + /* operations, buffer id */ + m_node->ops_id = buf->ops_id; + m_node->prop_id = buf->prop_id; + m_node->buf_id = buf->buf_id; + + DRM_DEBUG_KMS("%s:m_node[0x%x]ops_id[%d]\n", __func__, + (int)m_node, buf->ops_id); + DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]\n", __func__, + buf->prop_id, m_node->buf_id); + + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) { + unsigned int gem_obj; + + DRM_DEBUG_KMS("%s:i[%d]handle[0x%x]\n", __func__, + i, buf->handle[i]); + + /* get dma address by handle */ + if (buf->handle[i] != 0) { + addr = exynos_drm_gem_get_dma_addr(drm_dev, + buf->handle[i], file, &gem_obj); + if (!addr) { + DRM_ERROR("failed to get addr.\n"); + goto err_clear; + } + + size = exynos_drm_gem_get_size(drm_dev, + buf->handle[i], file); + if (!size) { + DRM_ERROR("failed to get size.\n"); + goto err_clear; + } + + buf_info.gem_objs[i] = (void *)gem_obj; + buf_info.base[i] = *(dma_addr_t *) addr; + buf_info.size[i] = (uint64_t) size; + } + } + + m_node->buf_info = buf_info; + list_add_tail(&m_node->list, &c_node->mem_list[buf->ops_id]); + + return m_node; + +err_clear: + kfree(m_node); + + return NULL; +} + +int exynos_drm_ipp_buf(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct exynos_drm_ippdrv *ippdrv = priv->ippdrv; + struct drm_exynos_ipp_buf *buf = data; + struct exynos_drm_ipp_ops *ops = NULL; + struct drm_exynos_ipp_send_event *e, *te; + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node = NULL, *tm_node; + struct drm_exynos_ipp_property *property; + struct drm_exynos_ipp_buf_info buf_info; + struct list_head *head; + int ret, i; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!buf) { + DRM_ERROR("invalid buf parameter.\n"); + return -EINVAL; + } + + if (!ippdrv) { + DRM_ERROR("failed to get ipp driver.\n"); + return -EINVAL; + } + + if (buf->ops_id >= EXYNOS_DRM_OPS_MAX) { + DRM_ERROR("invalid ops parameter.\n"); + return -EINVAL; + } + + ops = ippdrv->ops[buf->ops_id]; + if (!ops) { + DRM_ERROR("failed to get ops.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:ops_id[%s]buf_id[%d]buf_ctrl[%d]\n", + __func__, buf->ops_id ? "dst" : "src", + buf->buf_id, buf->buf_ctrl); + + /* clear base address for error handling */ + memset(&buf_info, 0x0, sizeof(buf_info)); + + /* find command node */ + c_node = ipp_find_cmd_node(ippdrv, buf->prop_id); + if (!c_node) { + DRM_ERROR("failed to get command node.\n"); + ret = -EINVAL; + goto err_clear; + } + + /* get property */ + property = &c_node->property; + if (!property) { + DRM_ERROR("invalid property parameter.\n"); + goto err_clear; + } + + /* buffer control */ + switch (buf->buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + /* make memory node */ + m_node = ipp_make_mem_node(drm_dev, file, ippdrv, buf); + if (!m_node) { + DRM_ERROR("failed to make queue node.\n"); + ret = -EINVAL; + goto err_clear; + } + + buf_info = m_node->buf_info; + + if (pm_runtime_suspended(ippdrv->dev)) + break; + + /* set address */ + if (property->cmd != IPP_CMD_M2M && ops->set_addr) { + ret = ops->set_addr(ippdrv->dev, &buf_info, buf->buf_id, + buf->buf_ctrl); + if (ret) { + DRM_ERROR("failed to set addr.\n"); + goto err_clear; + } + } + break; + case IPP_BUF_CTRL_DEQUEUE: + /* free node */ + list_for_each_entry_safe(m_node, tm_node, + &c_node->mem_list[buf->ops_id], list) { + if (m_node->buf_id == buf->buf_id && + m_node->ops_id == buf->ops_id) { + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, + m_node); + if (ret) { + DRM_ERROR("failed to free m node.\n"); + goto err_clear; + } + } + } + + if (pm_runtime_suspended(ippdrv->dev)) { + DRM_ERROR("suspended:invalid operations.\n"); + ret = -EINVAL; + goto err_clear; + } + + /* clear address */ + if (ops->set_addr) { + ret = ops->set_addr(ippdrv->dev, &buf_info, buf->buf_id, + buf->buf_ctrl); + if (ret) { + DRM_ERROR("failed to set addr.\n"); + goto err_clear; + } + } + break; + default: + DRM_ERROR("invalid buffer control.\n"); + return -EINVAL; + } + + /* destination buffer need event control */ + if (buf->ops_id == EXYNOS_DRM_OPS_DST) { + switch (buf->buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + /* make event */ + ret = ipp_make_event(drm_dev, file, ippdrv, buf); + if (ret) { + DRM_ERROR("failed to make event.\n"); + goto err_clear; + } + break; + case IPP_BUF_CTRL_DEQUEUE: + /* free event */ + list_for_each_entry_safe(e, te, + &ippdrv->event_list, base.link) { + if (e->event.buf_id[EXYNOS_DRM_OPS_DST] == + buf->buf_id) { + /* delete list */ + list_del(&e->base.link); + kfree(e); + } + } + break; + default: + /* no action */ + break; + } + } + + /* + * If set source, destination buffer and enable pm + * m2m operations need start operations in queue + */ + if (property->cmd == IPP_CMD_M2M) { + /* start operations was not set */ + if (pm_runtime_suspended(ippdrv->dev)) { + DRM_DEBUG_KMS("suspended state.\n"); + return 0; + } + + /* check source/destination buffer status */ + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + /* check list empty */ + if (list_empty(head)) { + DRM_DEBUG_KMS("list empty.\n"); + return 0; + } + } + + /* check property id and buffer property id */ + if (property->prop_id != buf->prop_id) { + DRM_ERROR("invalid property id.\n"); + goto err_clear; + } + + /* start property */ + ret = ipp_start_property(drm_dev, ippdrv, property->prop_id); + if (ret) { + DRM_ERROR("failed to start property.\n"); + goto err_clear; + } + } + + return 0; + +err_clear: + DRM_ERROR("%s:failed to set buf.\n", __func__); + + /* delete list */ + list_for_each_entry_safe(m_node, tm_node, + &c_node->mem_list[buf->ops_id], list) { + if (m_node->buf_id == buf->buf_id && + m_node->ops_id == buf->ops_id) { + list_del(&m_node->list); + kfree(m_node); + } + } + + /* put gem buffer */ + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) { + void *gem_obj = buf_info.gem_objs[i]; + + if (gem_obj) + exynos_drm_gem_put_dma_addr(drm_dev, + gem_obj); + } + + /* free address */ + switch (buf->buf_ctrl) { + case IPP_BUF_CTRL_QUEUE: + case IPP_BUF_CTRL_DEQUEUE: + if (pm_runtime_suspended(ippdrv->dev)) { + DRM_ERROR("suspended:invalid error operations.\n"); + return -EINVAL; + } + + /* clear base address for error handling */ + memset(&buf_info, 0x0, sizeof(buf_info)); + + /* don't need check error case */ + if (ops->set_addr) + ops->set_addr(ippdrv->dev, &buf_info, + buf->buf_id, IPP_BUF_CTRL_DEQUEUE); + break; + default: + /* no action */ + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(exynos_drm_ipp_buf); + +int exynos_drm_ipp_ctrl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct exynos_drm_ippdrv *ippdrv = priv->ippdrv; + struct drm_exynos_ipp_ctrl *ctrl = data; + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (!ctrl) { + DRM_ERROR("invalid control parameter.\n"); + return -EINVAL; + } + + if (!ippdrv) { + DRM_ERROR("failed to get ipp driver.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:use[%d]\n", __func__, ctrl->use); + + /* ToDo: expand ctrl operation */ + + /* + * start/stop operations, + * set use to 1, you can use start operations + * other case is stop opertions + */ + if (ctrl->use) { + if (pm_runtime_suspended(ippdrv->dev)) + pm_runtime_get_sync(ippdrv->dev); + + ret = ipp_start_property(drm_dev, ippdrv, ctrl->prop_id); + if (ret) { + DRM_ERROR("failed to start property.\n"); + goto err_clear; + } + + ippdrv->state = IPP_STATE_START; + } else { + ippdrv->state = IPP_STATE_STOP; + ippdrv->dedicated = false; + ippdrv->property = NULL; + + ret = ipp_stop_property(drm_dev, ippdrv, ctrl->prop_id); + if (ret) { + DRM_ERROR("failed to stop property.\n"); + goto err_clear; + } + + if (!pm_runtime_suspended(ippdrv->dev)) + pm_runtime_put_sync(ippdrv->dev); + } + + return 0; + +err_clear: + /* + * ToDo: register clear if needed + * If failed choose device using property. then + * revert register clearing if needed + */ + + return ret; +} +EXPORT_SYMBOL_GPL(exynos_drm_ipp_ctrl); + +int exynos_drm_ippnb_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &exynos_drm_ippnb_list, nb); +} +EXPORT_SYMBOL_GPL(exynos_drm_ippnb_register); + +int exynos_drm_ippnb_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister( + &exynos_drm_ippnb_list, nb); +} +EXPORT_SYMBOL_GPL(exynos_drm_ippnb_unregister); + +int exynos_drm_ippnb_send_event(unsigned long val, void *v) +{ + return blocking_notifier_call_chain( + &exynos_drm_ippnb_list, val, v); +} +EXPORT_SYMBOL_GPL(exynos_drm_ippnb_send_event); + +void ipp_send_event_handler(struct exynos_drm_ippdrv *ippdrv, + int buf_id) +{ + struct drm_device *drm_dev = ippdrv->drm_dev; + struct drm_exynos_ipp_property *property = ippdrv->property; + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node; + struct drm_exynos_ipp_buf buf; + struct drm_exynos_ipp_send_event *e; + struct list_head *head; + struct timeval now; + unsigned long flags; + u32 q_buf_id[EXYNOS_DRM_OPS_MAX] = {0, }; + int ret, i; + + DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + + if (!drm_dev) { + DRM_ERROR("failed to get drm_dev.\n"); + return; + } + + if (list_empty(&ippdrv->event_list)) { + DRM_ERROR("event list is empty.\n"); + return; + } + + if (!property) { + DRM_ERROR("failed to get property.\n"); + return; + } + + /* find command node */ + c_node = ipp_find_cmd_node(ippdrv, property->prop_id); + if (!c_node) { + DRM_ERROR("invalid command node list.\n"); + return; + } + + /* check command type */ + switch (property->cmd) { + case IPP_CMD_M2M: + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* source/destination memory list */ + head = &c_node->mem_list[i]; + + if (list_empty(head)) { + DRM_ERROR("empty list.\n"); + return; + } + + /* get first entry */ + m_node = list_first_entry(head, + struct drm_exynos_ipp_mem_node, list); + if (!m_node) { + DRM_ERROR("empty list.\n"); + return; + } + + q_buf_id[i] = m_node->buf_id; + + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, m_node); + if (ret) + DRM_ERROR("failed to free m node.\n"); + } + break; + case IPP_CMD_WB: + /* clear buf for finding */ + memset(&buf, 0x0, sizeof(buf)); + buf.ops_id = EXYNOS_DRM_OPS_DST; + buf.buf_id = buf_id; + + /* get memory node entry */ + m_node = ipp_find_mem_node(c_node, &buf); + if (!m_node) { + DRM_ERROR("empty list.\n"); + return; + } + + q_buf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id; + + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, m_node); + if (ret) + DRM_ERROR("failed to free m node.\n"); + break; + case IPP_CMD_OUTPUT: + /* source memory list */ + head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; + + /* get first entry */ + m_node = list_first_entry(head, + struct drm_exynos_ipp_mem_node, list); + if (!m_node) { + DRM_ERROR("empty list.\n"); + return; + } + + q_buf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id; + + /* free memory node to ippdrv */ + ret = ipp_free_mem_node(drm_dev, ippdrv, m_node); + if (ret) + DRM_ERROR("failed to free m node.\n"); + break; + default: + DRM_ERROR("invalid operations.\n"); + return; + } + + /* ToDo: Fix buffer id */ + if (q_buf_id[EXYNOS_DRM_OPS_DST] != buf_id) + DRM_ERROR("failed to match buffer id %d, %d.\n", + q_buf_id[EXYNOS_DRM_OPS_DST], buf_id); + + /* get first event entry */ + e = list_first_entry(&ippdrv->event_list, + struct drm_exynos_ipp_send_event, base.link); + + do_gettimeofday(&now); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + e->event.prop_id = property->prop_id; + + /* set buffer id about source destination */ + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + /* ToDo: compare index. If needed */ + e->event.buf_id[i] = q_buf_id[i]; + } + + spin_lock_irqsave(&drm_dev->event_lock, flags); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + /* ToDo: Need to handle the property queue */ + + switch (property->cmd) { + case IPP_CMD_M2M: + for (i = 0; i < EXYNOS_DRM_OPS_MAX; i++) { + head = &c_node->mem_list[i]; + if (list_empty(head)) + return; + } + + ret = ipp_start_property(drm_dev, ippdrv, property->prop_id); + if (ret) { + DRM_ERROR("failed to start property.\n"); + return; + } + break; + case IPP_CMD_WB: + case IPP_CMD_OUTPUT: + default: + break; + } + + DRM_DEBUG_KMS("%s:finish cmd[%d]\n", __func__, property->cmd); +} + +static void ipp_sched_event(struct work_struct *sched_event) +{ + struct ipp_context *ctx = container_of(sched_event, + struct ipp_context, sched_event); + + DRM_DEBUG_KMS("%s\n", __func__); + /* ToDo:send event handler */ +} + +static void ipp_sched_cmd(struct work_struct *sched_cmd) +{ + struct ipp_context *ctx = container_of(sched_cmd, + struct ipp_context, sched_cmd); + + DRM_DEBUG_KMS("%s\n", __func__); + /* ToDo: schedule next work */ +} + +static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + struct exynos_drm_ippdrv *ippdrv; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, list) { + ippdrv->drm_dev = drm_dev; + + /* ToDo: need move open ? */ + + /* init prop idr */ + idr_init(&ippdrv->prop_idr); + + /* check iommu use case */ + if (ippdrv->iommu_used) { + ret = exynos_drm_iommu_activate(drm_priv->vmm, + ippdrv->dev); + if (ret) { + DRM_ERROR("failed to activate iommu\n"); + goto err_clear; + } + } + } + + return 0; + +err_clear: + /* get ipp driver entry */ + list_for_each_entry_reverse(ippdrv, &exynos_drm_ippdrv_list, list) + if ((ippdrv->iommu_used) && (drm_priv->vmm)) + exynos_drm_iommu_deactivate(drm_priv->vmm, ippdrv->dev); + + return ret; +} + +static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) +{ + struct exynos_drm_ippdrv *ippdrv; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, list) { + + /* ToDo: need move close ? */ + + /* remove,destroy property idr */ + idr_remove_all(&ippdrv->prop_idr); + idr_destroy(&ippdrv->prop_idr); + + if (drm_priv->vmm) + exynos_drm_iommu_deactivate(drm_priv->vmm, ippdrv->dev); + + ippdrv->drm_dev = NULL; + exynos_drm_ippdrv_unregister(ippdrv); + } + + /* ToDo: free notifier callback list if needed */ +} + +static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv; + struct exynos_drm_ippdrv *ippdrv; + int count = 0; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* ToDo: multi device open */ + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + DRM_ERROR("failed to allocate priv.\n"); + return -ENOMEM; + } + + priv->dev = dev; + file_priv->ipp_priv = priv; + INIT_LIST_HEAD(&priv->event_list); + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, list) { + DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", __func__, + count++, (int)ippdrv); + + /* check idle state */ + if (ippdrv->state != IPP_STATE_IDLE) + continue; + + INIT_LIST_HEAD(&ippdrv->event_list); + INIT_LIST_HEAD(&ippdrv->cmd_list); + list_splice_init(&priv->event_list, &ippdrv->event_list); + } + + return 0; +} + +static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; + struct exynos_drm_ippdrv *ippdrv_cur = priv->ippdrv; + struct exynos_drm_ippdrv *ippdrv; + struct drm_exynos_ipp_send_event *e, *te; + int count; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* ToDo: for multi device close */ + + /* get ipp driver entry */ + list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, list) { + DRM_DEBUG_KMS("%s:ippdrv_cur[0x%x]ippdrv[0x%x]\n", + __func__, (int)ippdrv_cur, (int)ippdrv); + + DRM_DEBUG_KMS("%s:state[%d]dedicated[%d]\n", __func__, + ippdrv->state, ippdrv->dedicated); + + /* current used ippdrv stop needed */ + if (ippdrv_cur && ippdrv_cur == ippdrv) { + if (ippdrv->state == IPP_STATE_START) { + if (ippdrv->stop) + ippdrv->stop(ippdrv->dev, ippdrv->cmd); + + if (!pm_runtime_suspended(ippdrv->dev)) + pm_runtime_put_sync(ippdrv->dev); + } + + ippdrv->state = IPP_STATE_IDLE; + ippdrv->dedicated = false; + } + + /* check idle state */ + if (ippdrv->state != IPP_STATE_IDLE) + continue; + + /* free event */ + count = 0; + list_for_each_entry_safe(e, te, + &ippdrv->event_list, base.link) { + DRM_DEBUG_KMS("%s:count[%d]e[0x%x]\n", + __func__, count++, (int)e); + + /* delete list */ + list_del(&e->base.link); + kfree(e); + } + + /* free property list */ + ipp_free_cmd_list(drm_dev, ippdrv); + /* ToDo: How can get current fd property ? */ + } + + kfree(file_priv->ipp_priv); + + return; +} + +static int __devinit ipp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ipp_context *ctx; + struct exynos_drm_subdrv *subdrv; + struct exynos_drm_ippdrv *tippdrv; + int ret = -EINVAL; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* init ioctl lock */ + mutex_init(&ctx->lock); + /* init event, cmd work thread */ + INIT_WORK(&ctx->sched_event, ipp_sched_event); + INIT_WORK(&ctx->sched_cmd, ipp_sched_cmd); + /* init ipp driver idr */ + idr_init(&ctx->ipp_idr); + + /* get ipp driver entry */ + list_for_each_entry(tippdrv, &exynos_drm_ippdrv_list, list) { + /* create ipp id */ + ret = ipp_create_id(&ctx->ipp_idr, tippdrv, &tippdrv->ipp_id); + if (ret) { + DRM_ERROR("failed to create id.\n"); + goto err_clear; + } + + DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, tippdrv->ipp_id); + + if (tippdrv->ipp_id == 0) + DRM_ERROR("failed to get ipp_id[%d]\n", + tippdrv->ipp_id); + } + + /* set sub driver informations */ + subdrv = &ctx->subdrv; + subdrv->dev = dev; + subdrv->probe = ipp_subdrv_probe; + subdrv->remove = ipp_subdrv_remove; + subdrv->open = ipp_subdrv_open; + subdrv->close = ipp_subdrv_close; + + /* set driver data */ + platform_set_drvdata(pdev, ctx); + + /* register sub driver */ + ret = exynos_drm_subdrv_register(subdrv); + if (ret < 0) { + DRM_ERROR("failed to register drm ipp device.\n"); + goto err_clear; + } + + dev_info(&pdev->dev, "drm ipp registered successfully.\n"); + + return 0; + +err_clear: + kfree(ctx); + + return ret; +} + +static int __devexit ipp_remove(struct platform_device *pdev) +{ + struct ipp_context *ctx = platform_get_drvdata(pdev); + + DRM_DEBUG_KMS("%s\n", __func__); + + /* unregister sub driver */ + exynos_drm_subdrv_unregister(&ctx->subdrv); + + /* remove,destroy ipp idr */ + idr_remove_all(&ctx->ipp_idr); + idr_destroy(&ctx->ipp_idr); + + kfree(ctx); + + return 0; +} + + +struct platform_driver ipp_driver = { + .probe = ipp_probe, + .remove = __devexit_p(ipp_remove), + .driver = { + .name = "exynos-drm-ipp", + .owner = THIS_MODULE, + }, +}; + diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h new file mode 100644 index 0000000..458f747 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _EXYNOS_DRM_IPP_H_ +#define _EXYNOS_DRM_IPP_H_ + +#define IPP_GET_LCD_WIDTH _IOR('F', 302, int) +#define IPP_GET_LCD_HEIGHT _IOR('F', 303, int) +#define IPP_SET_WRITEBACK _IOW('F', 304, u32) + +/* definition of state */ +enum drm_exynos_ipp_state { + IPP_STATE_IDLE, + IPP_STATE_START, + IPP_STATE_STOP, +}; + +/* + * A structure of buffer information. + * + * @gem_objs: Y, Cb, Cr each gem object. + * @base: Y, Cb, Cr each planar address. + * @size: Y, Cb, Cr each planar size. + */ +struct drm_exynos_ipp_buf_info { + void *gem_objs[EXYNOS_DRM_PLANAR_MAX]; + dma_addr_t base[EXYNOS_DRM_PLANAR_MAX]; + uint64_t size[EXYNOS_DRM_PLANAR_MAX]; +}; + +/* + * A structure of source,destination operations. + * + * @set_fmt: set format of image. + * @set_transf: set transform(rotations, flip). + * @set_size: set size of region. + * @set_addr: set address for dma. + */ +struct exynos_drm_ipp_ops { + int (*set_fmt)(struct device *dev, u32 fmt); + int (*set_transf)(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip); + int (*set_size)(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz); + int (*set_addr)(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, + enum drm_exynos_ipp_buf_ctrl buf_ctrl); +}; + +/* + * A structure of ipp driver. + * + * @list: list head. + * @dev: platform device. + * @drm_dev: drm device. + * @state: state of ipp drivers. + * @ipp_id: id of ipp driver. + * @dedicated: dedicated ipp device. + * @iommu_used: iommu used status. + * @cmd: used command. + * @ops: source, destination operations. + * @property: current property. + * @prop_idr: property idr. + * @cmd_list: list head to command information. + * @event_list: list head to event information. + * @reset: reset ipp block. + * @check_property: check property about format, size, buffer. + * @start: ipp each device start. + * @stop: ipp each device stop. + */ +struct exynos_drm_ippdrv { + struct list_head list; + struct device *dev; + struct drm_device *drm_dev; + enum drm_exynos_ipp_state state; + u32 ipp_id; + bool dedicated; + bool iommu_used; + enum drm_exynos_ipp_cmd cmd; + struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX]; + struct drm_exynos_ipp_property *property; + struct idr prop_idr; + struct list_head cmd_list; + struct list_head event_list; + + int (*check_property)(struct device *dev, + struct drm_exynos_ipp_property *property); + int (*reset)(struct device *dev); + int (*start)(struct device *dev, enum drm_exynos_ipp_cmd cmd); + void (*stop)(struct device *dev, enum drm_exynos_ipp_cmd cmd); +}; + +#ifdef CONFIG_DRM_EXYNOS_IPP +extern int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv); +extern int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv); +extern int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ipp_buf(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ipp_ctrl(struct drm_device *drm_dev, void *data, + struct drm_file *file); +extern int exynos_drm_ippnb_register(struct notifier_block *nb); +extern int exynos_drm_ippnb_unregister(struct notifier_block *nb); +extern int exynos_drm_ippnb_send_event(unsigned long val, void *v); +#else +static inline int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) +{ + return -ENODEV; +} + +static inline int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) +{ + return -ENODEV; +} + +static inline int exynos_drm_ipp_get_property(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ipp_set_property(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ipp_buf(struct drm_device *drm_dev, + void *data, + struct drm_file *file) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ipp_ctrl(struct drm_device *drm_dev, + void *data, + struct drm_file *file) +{ + return -ENOTTY; +} + +static inline int exynos_drm_ippnb_register(struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline int exynos_drm_ippnb_unregister(struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline int exynos_drm_ippnb_send_event(unsigned long val, void *v) +{ + return -ENOTTY; +} +#endif + +/* ToDo: Must be change to queue_work */ +void ipp_send_event_handler(struct exynos_drm_ippdrv *ippdrv, + int buf_idx); + +#endif /* _EXYNOS_DRM_IPP_H_ */ + diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index c4c6525..b89829e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -12,9 +12,12 @@ #include "drmP.h" #include "exynos_drm.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_gem.h" + +#define to_exynos_plane(x) container_of(x, struct exynos_plane, base) struct exynos_plane { struct drm_plane base; @@ -30,6 +33,108 @@ static const uint32_t formats[] = { DRM_FORMAT_NV12MT, }; +int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + unsigned int actual_w; + unsigned int actual_h; + int nr; + int i; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + nr = exynos_drm_format_num_buffers(fb->pixel_format); + for (i = 0; i < nr; i++) { + struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i); + + if (!buffer) { + DRM_LOG_KMS("buffer is null\n"); + return -EFAULT; + } + + overlay->dma_addr[i] = buffer->dma_addr; + overlay->vaddr[i] = buffer->kvaddr; + + DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", + i, (unsigned long)overlay->vaddr[i], + (unsigned long)overlay->dma_addr[i]); + } + + actual_w = min((unsigned)(crtc->mode.hdisplay - crtc_x), crtc_w); + actual_h = min((unsigned)(crtc->mode.vdisplay - crtc_y), crtc_h); + + /* set drm framebuffer data. */ + overlay->fb_x = src_x; + overlay->fb_y = src_y; + overlay->fb_width = fb->width; + overlay->fb_height = fb->height; + overlay->src_width = src_w; + overlay->src_height = src_h; + overlay->bpp = fb->bits_per_pixel; + overlay->pitch = fb->pitches[0]; + overlay->pixel_format = fb->pixel_format; + + /* set overlay range to be displayed. */ + overlay->crtc_x = crtc_x; + overlay->crtc_y = crtc_y; + overlay->crtc_width = actual_w; + overlay->crtc_height = actual_h; + + /* set drm mode data. */ + overlay->mode_width = crtc->mode.hdisplay; + overlay->mode_height = crtc->mode.vdisplay; + overlay->refresh = crtc->mode.vrefresh; + overlay->scan_flag = crtc->mode.flags; + + DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", + overlay->crtc_x, overlay->crtc_y, + overlay->crtc_width, overlay->crtc_height); + + exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set); + + return 0; +} + +void exynos_plane_commit(struct drm_plane *plane) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_commit); +} + +void exynos_plane_dpms(struct drm_plane *plane, int mode) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (mode == DRM_MODE_DPMS_ON) { + if (exynos_plane->enabled) + return; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_enable); + + exynos_plane->enabled = true; + } else { + if (!exynos_plane->enabled) + return; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_disable); + + exynos_plane->enabled = false; + } +} + static int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -37,64 +142,37 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - struct exynos_drm_crtc_pos pos; int ret; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - pos.crtc_x = crtc_x; - pos.crtc_y = crtc_y; - pos.crtc_w = crtc_w; - pos.crtc_h = crtc_h; - - /* considering 16.16 fixed point of source values */ - pos.fb_x = src_x >> 16; - pos.fb_y = src_y >> 16; - pos.src_w = src_w >> 16; - pos.src_h = src_h >> 16; - - ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos); + ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, + crtc_w, crtc_h, src_x >> 16, src_y >> 16, + src_w >> 16, src_h >> 16); if (ret < 0) return ret; - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &overlay->zpos, - exynos_drm_encoder_crtc_plane_commit); + plane->crtc = crtc; + plane->fb = crtc->fb; - exynos_plane->enabled = true; + exynos_plane_commit(plane); + exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); return 0; } static int exynos_disable_plane(struct drm_plane *plane) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!exynos_plane->enabled) - return 0; - - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_crtc_disable); - - exynos_plane->enabled = false; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; + exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF); return 0; } static void exynos_plane_destroy(struct drm_plane *plane) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); + struct exynos_plane *exynos_plane = to_exynos_plane(plane); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -103,69 +181,79 @@ static void exynos_plane_destroy(struct drm_plane *plane) kfree(exynos_plane); } +static int exynos_plane_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_private *dev_priv = dev->dev_private; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (property == dev_priv->plane_zpos_property) { + exynos_plane->overlay.zpos = val; + return 0; + } + + return -EINVAL; +} + static struct drm_plane_funcs exynos_plane_funcs = { .update_plane = exynos_update_plane, .disable_plane = exynos_disable_plane, .destroy = exynos_plane_destroy, + .set_property = exynos_plane_set_property, }; -int exynos_plane_init(struct drm_device *dev, unsigned int nr) +static void exynos_plane_attach_zpos_property(struct drm_plane *plane) { - struct exynos_plane *exynos_plane; - uint32_t possible_crtcs; + struct drm_device *dev = plane->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; - exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); - if (!exynos_plane) - return -ENOMEM; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - /* all CRTCs are available */ - possible_crtcs = (1 << MAX_CRTC) - 1; + prop = dev_priv->plane_zpos_property; + if (!prop) { + prop = drm_property_create_range(dev, 0, "zpos", 0, + MAX_PLANE - 1); + if (!prop) + return; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; + dev_priv->plane_zpos_property = prop; + } - return drm_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, formats, ARRAY_SIZE(formats), - false); + drm_object_attach_property(&plane->base, prop, 0); } -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +struct drm_plane *exynos_plane_init(struct drm_device *dev, + unsigned int possible_crtcs, bool priv) { - struct drm_exynos_plane_set_zpos *zpos_req = data; - struct drm_mode_object *obj; - struct drm_plane *plane; struct exynos_plane *exynos_plane; - int ret = 0; + int err; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) { - if (zpos_req->zpos != DEFAULT_ZPOS) { - DRM_ERROR("zpos not within limits\n"); - return -EINVAL; - } + exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); + if (!exynos_plane) { + DRM_ERROR("failed to allocate plane\n"); + return NULL; } - mutex_lock(&dev->mode_config.mutex); - - obj = drm_mode_object_find(dev, zpos_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - zpos_req->plane_id); - ret = -EINVAL; - goto out; + err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs, + &exynos_plane_funcs, formats, ARRAY_SIZE(formats), + priv); + if (err) { + DRM_ERROR("failed to initialize plane\n"); + kfree(exynos_plane); + return NULL; } - plane = obj_to_plane(obj); - exynos_plane = container_of(plane, struct exynos_plane, base); - - exynos_plane->overlay.zpos = zpos_req->zpos; + if (priv) + exynos_plane->overlay.zpos = DEFAULT_ZPOS; + else + exynos_plane_attach_zpos_property(&exynos_plane->base); -out: - mutex_unlock(&dev->mode_config.mutex); - return ret; + return &exynos_plane->base; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index 16b71f8..8831245 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -9,6 +9,12 @@ * */ -int exynos_plane_init(struct drm_device *dev, unsigned int nr); -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); +void exynos_plane_commit(struct drm_plane *plane); +void exynos_plane_dpms(struct drm_plane *plane, int mode); +struct drm_plane *exynos_plane_init(struct drm_device *dev, + unsigned int possible_crtcs, bool priv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 5bf1d6e..9e40790 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -15,13 +15,12 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/pm_runtime.h> -#include <linux/pm_qos_params.h> #include "drmP.h" #include "exynos_drm.h" #include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" #include "exynos_drm_iommu.h" +#include "exynos_drm_ipp.h" /* Configuration */ #define ROT_CONFIG 0x00 @@ -49,39 +48,31 @@ #define ROT_STATUS_IRQ_VAL_COMPLETE 1 #define ROT_STATUS_IRQ_VAL_ILLEGAL 2 -/* Sourc Buffer Address */ +/* Buffer Address */ #define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2)) +#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) -/* Source Buffer Size */ +/* Buffer Size */ #define ROT_SRC_BUF_SIZE 0x3c -#define ROT_SRC_BUF_SIZE_H(x) ((x) << 16) -#define ROT_SRC_BUF_SIZE_W(x) ((x) << 0) +#define ROT_DST_BUF_SIZE 0x5c +#define ROT_SET_BUF_SIZE_H(x) ((x) << 16) +#define ROT_SET_BUF_SIZE_W(x) ((x) << 0) +#define ROT_GET_BUF_SIZE_H(x) ((x) >> 16) +#define ROT_GET_BUF_SIZE_W(x) ((x) & 0xffff) -/* Source Crop Position */ +/* Crop Position */ #define ROT_SRC_CROP_POS 0x40 -#define ROT_SRC_CROP_POS_Y(x) ((x) << 16) -#define ROT_SRC_CROP_POS_X(x) ((x) << 0) +#define ROT_DST_CROP_POS 0x60 +#define ROT_CROP_POS_Y(x) ((x) << 16) +#define ROT_CROP_POS_X(x) ((x) << 0) /* Source Crop Size */ #define ROT_SRC_CROP_SIZE 0x44 #define ROT_SRC_CROP_SIZE_H(x) ((x) << 16) #define ROT_SRC_CROP_SIZE_W(x) ((x) << 0) -/* Destination Buffer Address */ -#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) - -/* Destination Buffer Size */ -#define ROT_DST_BUF_SIZE 0x5c -#define ROT_DST_BUF_SIZE_H(x) ((x) << 16) -#define ROT_DST_BUF_SIZE_W(x) ((x) << 0) - -/* Destination Crop Position */ -#define ROT_DST_CROP_POS 0x60 -#define ROT_DST_CROP_POS_Y(x) ((x) << 16) -#define ROT_DST_CROP_POS_X(x) ((x) << 0) - /* Round to nearest aligned value */ -#define ROT_ALIGN(x, align, mask) ((*(x) + (1 << ((align) - 1))) & (mask)) +#define ROT_ALIGN(x, align, mask) (((x) + (1 << ((align) - 1))) & (mask)) /* Minimum limit value */ #define ROT_MIN(min, mask) (((min) + ~(mask)) & (mask)) /* Maximum limit value */ @@ -111,28 +102,11 @@ struct rot_context { struct resource *regs_res; void __iomem *regs; int irq; - int exec_ret; - struct exynos_drm_subdrv subdrv; - struct completion complete; - struct mutex exec_mutex; - spinlock_t irq_lock; - struct pm_qos_request_list pm_qos; + struct exynos_drm_ippdrv ippdrv; + int cur_buf_id[EXYNOS_DRM_OPS_MAX]; bool suspended; }; -struct rot_buffer { - dma_addr_t src_addr[DRM_EXYNOS_ROT_MAX_BUF]; - dma_addr_t dst_addr[DRM_EXYNOS_ROT_MAX_BUF]; - void *src_gem_obj[DRM_EXYNOS_ROT_MAX_BUF]; - void *dst_gem_obj[DRM_EXYNOS_ROT_MAX_BUF]; - u32 src_cnt; - u32 dst_cnt; - u32 src_w; - u32 src_h; - u32 dst_w; - u32 dst_h; -}; - static void rotator_reg_set_irq(struct rot_context *rot, bool enable) { u32 value = readl(rot->regs + ROT_CONFIG); @@ -145,6 +119,14 @@ static void rotator_reg_set_irq(struct rot_context *rot, bool enable) writel(value, rot->regs + ROT_CONFIG); } +static u32 rotator_reg_get_format(struct rot_context *rot) +{ + u32 value = readl(rot->regs + ROT_CONTROL); + value &= ROT_CONTROL_FMT_MASK; + + return value; +} + static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) { u32 value = readl(rot->regs + ROT_CONTROL); @@ -155,7 +137,7 @@ static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) case DRM_FORMAT_NV12M: value |= ROT_CONTROL_FMT_YCBCR420_2P; break; - case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: value |= ROT_CONTROL_FMT_RGB888; break; default: @@ -167,16 +149,16 @@ static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) } static void rotator_reg_set_flip(struct rot_context *rot, - enum drm_exynos_rot_flip flip) + enum drm_exynos_flip flip) { u32 value = readl(rot->regs + ROT_CONTROL); value &= ~ROT_CONTROL_FLIP_MASK; switch (flip) { - case ROT_FLIP_VERTICAL: + case EXYNOS_DRM_FLIP_VERTICAL: value |= ROT_CONTROL_FLIP_VERTICAL; break; - case ROT_FLIP_HORIZONTAL: + case EXYNOS_DRM_FLIP_HORIZONTAL: value |= ROT_CONTROL_FLIP_HORIZONTAL; break; default: @@ -188,19 +170,19 @@ static void rotator_reg_set_flip(struct rot_context *rot, } static void rotator_reg_set_rotation(struct rot_context *rot, - enum drm_exynos_rot_degree degree) + enum drm_exynos_degree degree) { u32 value = readl(rot->regs + ROT_CONTROL); value &= ~ROT_CONTROL_ROT_MASK; switch (degree) { - case ROT_DEGREE_90: + case EXYNOS_DRM_DEGREE_90: value |= ROT_CONTROL_ROT_90; break; - case ROT_DEGREE_180: + case EXYNOS_DRM_DEGREE_180: value |= ROT_CONTROL_ROT_180; break; - case ROT_DEGREE_270: + case EXYNOS_DRM_DEGREE_270: value |= ROT_CONTROL_ROT_270; break; default: @@ -247,16 +229,25 @@ static void rotator_reg_set_src_buf_addr(struct rot_context *rot, writel(addr, rot->regs + ROT_SRC_BUF_ADDR(i)); } +static void rotator_reg_get_src_buf_size(struct rot_context *rot, u32 *w, + u32 *h) +{ + u32 value = readl(rot->regs + ROT_SRC_BUF_SIZE); + + *w = ROT_GET_BUF_SIZE_W(value); + *h = ROT_GET_BUF_SIZE_H(value); +} + static void rotator_reg_set_src_buf_size(struct rot_context *rot, u32 w, u32 h) { - u32 value = ROT_SRC_BUF_SIZE_H(h) | ROT_SRC_BUF_SIZE_W(w); + u32 value = ROT_SET_BUF_SIZE_H(h) | ROT_SET_BUF_SIZE_W(w); writel(value, rot->regs + ROT_SRC_BUF_SIZE); } static void rotator_reg_set_src_crop_pos(struct rot_context *rot, u32 x, u32 y) { - u32 value = ROT_SRC_CROP_POS_Y(y) | ROT_SRC_CROP_POS_X(x); + u32 value = ROT_CROP_POS_Y(y) | ROT_CROP_POS_X(x); writel(value, rot->regs + ROT_SRC_CROP_POS); } @@ -274,16 +265,25 @@ static void rotator_reg_set_dst_buf_addr(struct rot_context *rot, writel(addr, rot->regs + ROT_DST_BUF_ADDR(i)); } +static void rotator_reg_get_dst_buf_size(struct rot_context *rot, u32 *w, + u32 *h) +{ + u32 value = readl(rot->regs + ROT_DST_BUF_SIZE); + + *w = ROT_GET_BUF_SIZE_W(value); + *h = ROT_GET_BUF_SIZE_H(value); +} + static void rotator_reg_set_dst_buf_size(struct rot_context *rot, u32 w, u32 h) { - u32 value = ROT_DST_BUF_SIZE_H(h) | ROT_DST_BUF_SIZE_W(w); + u32 value = ROT_SET_BUF_SIZE_H(h) | ROT_SET_BUF_SIZE_W(w); writel(value, rot->regs + ROT_DST_BUF_SIZE); } static void rotator_reg_set_dst_crop_pos(struct rot_context *rot, u32 x, u32 y) { - u32 value = ROT_DST_CROP_POS_Y(y) | ROT_DST_CROP_POS_X(x); + u32 value = ROT_CROP_POS_Y(y) | ROT_CROP_POS_X(x); writel(value, rot->regs + ROT_DST_CROP_POS); } @@ -294,71 +294,40 @@ static void rotator_reg_get_dump(struct rot_context *rot) for (i = 0; i <= ROT_DST_CROP_POS; i += 0x4) { value = readl(rot->regs + i); - DRM_INFO("+0x%x: 0x%x", i, value); + DRM_INFO("[%s] [0x%x] : 0x%x\n", __func__, i, value); } } -static bool rotator_check_format_n_handle_valid(u32 img_fmt, - u32 src_buf_handle_cnt, - u32 dst_buf_handle_cnt) +static irqreturn_t rotator_irq_handler(int irq, void *arg) { - bool ret = false; + struct rot_context *rot = arg; + struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; + enum rot_irq_status irq_status; - if ((src_buf_handle_cnt != dst_buf_handle_cnt) - || (src_buf_handle_cnt == 0)) - return ret; + /* Get execution result */ + irq_status = rotator_reg_get_irq_status(rot); + rotator_reg_set_irq_status_clear(rot, irq_status); - switch (img_fmt) { - case DRM_FORMAT_NV12M: - if (src_buf_handle_cnt == 2) - ret = true; - break; - case DRM_FORMAT_NV12: - case DRM_FORMAT_RGB888: - if (src_buf_handle_cnt == 1) - ret = true; - break; - default: - DRM_ERROR("invalid image format\n"); - break; + if (irq_status == ROT_IRQ_STATUS_COMPLETE) + ipp_send_event_handler(ippdrv, + rot->cur_buf_id[EXYNOS_DRM_OPS_DST]); + else { + DRM_ERROR("the SFR is set illegally\n"); + rotator_reg_get_dump(rot); } - return ret; -} - -static void rotator_align_size(struct rot_limit *limit, u32 mask, u32 *w, - u32 *h) -{ - u32 value; - - value = ROT_ALIGN(w, limit->align, mask); - if (value < limit->min_w) - *w = ROT_MIN(limit->min_w, mask); - else if (value > limit->max_w) - *w = ROT_MAX(limit->max_w, mask); - else - *w = value; - - value = ROT_ALIGN(h, limit->align, mask); - if (value < limit->min_h) - *h = ROT_MIN(limit->min_h, mask); - else if (value > limit->max_h) - *h = ROT_MAX(limit->max_h, mask); - else - *h = value; + return IRQ_HANDLED; } -static void rotator_align_buffer(struct rot_context *rot, - struct rot_buffer *buf, - struct drm_exynos_rot_buffer *req_buf, - struct drm_exynos_rot_control *control) +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize, + u32 *vsize) { struct rot_limit_table *limit_tbl = rot->limit_tbl; struct rot_limit *limit; - u32 mask; + u32 mask, value; /* Get size limit */ - if (control->img_fmt == DRM_FORMAT_RGB888) + if (fmt == ROT_CONTROL_FMT_RGB888) limit = &limit_tbl->rgb888; else limit = &limit_tbl->ycbcr420_2p; @@ -366,310 +335,333 @@ static void rotator_align_buffer(struct rot_context *rot, /* Get mask for rounding to nearest aligned value */ mask = ~((1 << limit->align) - 1); - /* For source buffer */ - buf->src_w = req_buf->src_w; - buf->src_h = req_buf->src_h; - rotator_align_size(limit, mask, &buf->src_w, &buf->src_h); + /* Set aligned width */ + value = ROT_ALIGN(*hsize, limit->align, mask); + if (value < limit->min_w) + *hsize = ROT_MIN(limit->min_w, mask); + else if (value > limit->max_w) + *hsize = ROT_MAX(limit->max_w, mask); + else + *hsize = value; - /* For destination buffer */ - buf->dst_w = req_buf->dst_w; - buf->dst_h = req_buf->dst_h; - rotator_align_size(limit, mask, &buf->dst_w, &buf->dst_h); + /* Set aligned height */ + value = ROT_ALIGN(*vsize, limit->align, mask); + if (value < limit->min_h) + *vsize = ROT_MIN(limit->min_h, mask); + else if (value > limit->max_h) + *vsize = ROT_MAX(limit->max_h, mask); + else + *vsize = value; } -static bool rotator_check_crop_boundary(struct rot_buffer *buf, - struct drm_exynos_rot_control *control, - struct drm_exynos_rot_crop *crop) +static int rotator_src_set_fmt(struct device *dev, u32 fmt) { - bool ret = true; + struct rot_context *rot = dev_get_drvdata(dev); - /* Check source crop position */ - if ((crop->src_x + crop->src_w > buf->src_w) - || (crop->src_y + crop->src_h > buf->src_h)) - return false; + /* Set format configuration */ + rotator_reg_set_format(rot, fmt); - /* Check destination crop position */ - switch (control->degree) { - case ROT_DEGREE_90: - case ROT_DEGREE_270: - if ((crop->dst_x + crop->src_h > buf->dst_w) - || (crop->dst_y + crop->src_w > buf->dst_h)) - ret = false; - break; - default: - if ((crop->dst_x + crop->src_w > buf->dst_w) - || (crop->dst_y + crop->src_h > buf->dst_h)) - ret = false; - break; - } - - return ret; + return 0; } -static int rotator_iommu_map(struct rot_buffer *buf, - struct drm_exynos_rot_buffer *req_buf, - struct iommu_gem_map_params *params, - struct list_head *iommu_list) -{ - /* For source buffer */ - buf->src_cnt = 0; - while (buf->src_cnt < req_buf->src_cnt) { - buf->src_addr[buf->src_cnt] = exynos_drm_iommu_map_gem(params, - iommu_list, - req_buf->src_handle[buf->src_cnt], - IOMMU_ROTATOR); - if (!buf->src_addr[buf->src_cnt]) { - DRM_ERROR("failed to map src handle[%u]\n", - buf->src_cnt); - return -EINVAL; - } - buf->src_gem_obj[(buf->src_cnt)++] = params->gem_obj; - } +static int rotator_src_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, + struct drm_exynos_sz *sz) +{ + struct rot_context *rot = dev_get_drvdata(dev); + u32 fmt, hsize, vsize; - /* For destination buffer */ - buf->dst_cnt = 0; - while (buf->dst_cnt < req_buf->dst_cnt) { - buf->dst_addr[buf->dst_cnt] = exynos_drm_iommu_map_gem(params, - iommu_list, - req_buf->dst_handle[buf->dst_cnt], - IOMMU_ROTATOR); - if (!buf->dst_addr[buf->dst_cnt]) { - DRM_ERROR("failed to map dst handle[%u]\n", - buf->dst_cnt); - return -EINVAL; - } - buf->dst_gem_obj[(buf->dst_cnt)++] = params->gem_obj; - } + /* Get format */ + fmt = rotator_reg_get_format(rot); - return 0; -} + /* Align buffer size */ + hsize = sz->hsize; + vsize = sz->vsize; + rotator_align_size(rot, fmt, &hsize, &vsize); -static void rotator_iommu_unmap(struct rot_buffer *buf, - struct iommu_gem_map_params *params) -{ - /* For destination buffer */ - while (buf->dst_cnt > 0) { - params->gem_obj = buf->dst_gem_obj[--(buf->dst_cnt)]; - exynos_drm_iommu_unmap_gem(params, - buf->dst_addr[buf->dst_cnt], - IOMMU_ROTATOR); - } + /* Set buffer size configuration */ + rotator_reg_set_src_buf_size(rot, hsize, vsize); - /* For source buffer */ - while (buf->src_cnt > 0) { - params->gem_obj = buf->src_gem_obj[--(buf->src_cnt)]; - exynos_drm_iommu_unmap_gem(params, - buf->src_addr[buf->src_cnt], - IOMMU_ROTATOR); - } + /* Set crop image position configuration */ + rotator_reg_set_src_crop_pos(rot, pos->x, pos->y); + rotator_reg_set_src_crop_size(rot, pos->w, pos->h); + + return 0; } -static void rotator_execute(struct rot_context *rot, - struct rot_buffer *buf, - struct drm_exynos_rot_control *control, - struct drm_exynos_rot_crop *crop) +static int rotator_src_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, + u32 buf_id, enum drm_exynos_ipp_buf_ctrl ctrl) { + struct rot_context *rot = dev_get_drvdata(dev); + dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; + u32 fmt, hsize, vsize; int i; - pm_runtime_get_sync(rot->subdrv.dev); - - /* Set interrupt enable */ - rotator_reg_set_irq(rot, true); + /* Set current buf_id */ + rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id; - /* Set control registers */ - rotator_reg_set_format(rot, control->img_fmt); - rotator_reg_set_flip(rot, control->flip); - rotator_reg_set_rotation(rot, control->degree); + switch (ctrl) { + case IPP_BUF_CTRL_QUEUE: + /* Set address configuration */ + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + addr[i] = buf_info->base[i]; - /* Set source buffer address */ - for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++) - rotator_reg_set_src_buf_addr(rot, buf->src_addr[i], i); + /* Get format */ + fmt = rotator_reg_get_format(rot); - /* Set source buffer size */ - rotator_reg_set_src_buf_size(rot, buf->src_w, buf->src_h); + /* Re-set cb planar for NV12 format */ + if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && + (addr[EXYNOS_DRM_PLANAR_CB] == 0x00)) { + /* Get buf size */ + rotator_reg_get_src_buf_size(rot, &hsize, &vsize); - /* Set destination buffer address */ - for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++) - rotator_reg_set_dst_buf_addr(rot, buf->dst_addr[i], i); + /* Set cb planar */ + addr[EXYNOS_DRM_PLANAR_CB] = + addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; + } - /* Set destination buffer size */ - rotator_reg_set_dst_buf_size(rot, buf->dst_w, buf->dst_h); + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_src_buf_addr(rot, addr[i], i); + break; + case IPP_BUF_CTRL_DEQUEUE: + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_src_buf_addr(rot, buf_info->base[i], i); + break; + default: + /* Nothing to do */ + break; + } - /* Set source crop image position */ - rotator_reg_set_src_crop_pos(rot, crop->src_x, crop->src_y); + return 0; +} - /* Set source crop image size */ - rotator_reg_set_src_crop_size(rot, crop->src_w, crop->src_h); +static int rotator_dst_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip) +{ + struct rot_context *rot = dev_get_drvdata(dev); - /* Set destination crop image position */ - rotator_reg_set_dst_crop_pos(rot, crop->dst_x, crop->dst_y); + /* Set transform configuration */ + rotator_reg_set_flip(rot, flip); + rotator_reg_set_rotation(rot, degree); - /* Start rotator operation */ - rotator_reg_set_start(rot); + /* Check degree for setting buffer size swap */ + if ((degree == EXYNOS_DRM_DEGREE_90) || + (degree == EXYNOS_DRM_DEGREE_270)) + return 1; + else + return 0; } -int exynos_drm_rotator_exec_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file) +static int rotator_dst_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, + struct drm_exynos_sz *sz) { - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv = file_priv->rot_priv; - struct device *dev = priv->dev; - struct rot_context *rot; - struct drm_exynos_rot_exec_data *req = data; - struct drm_exynos_rot_buffer *req_buf = &req->buf; - struct drm_exynos_rot_control *control = &req->control; - struct drm_exynos_rot_crop *crop = &req->crop; - struct rot_buffer buf; - struct iommu_gem_map_params params; - - if (!dev) { - DRM_ERROR("failed to get dev\n"); - return -ENODEV; - } + struct rot_context *rot = dev_get_drvdata(dev); + u32 fmt, hsize, vsize; - rot = dev_get_drvdata(dev); - if (!rot) { - DRM_ERROR("failed to get drvdata\n"); - return -EFAULT; - } + /* Get format */ + fmt = rotator_reg_get_format(rot); - if (rot->suspended) { - DRM_ERROR("suspended state\n"); - return -EPERM; - } - - if (!rotator_check_format_n_handle_valid(control->img_fmt, - req_buf->src_cnt, - req_buf->dst_cnt)) { - DRM_ERROR("format or handles are invalid\n"); - return -EINVAL; - } + /* Align buffer size */ + hsize = sz->hsize; + vsize = sz->vsize; + rotator_align_size(rot, fmt, &hsize, &vsize); - init_completion(&rot->complete); + /* Set buffer size configuration */ + rotator_reg_set_dst_buf_size(rot, hsize, vsize); - /* Align buffer */ - rotator_align_buffer(rot, &buf, req_buf, control); + /* Set crop image position configuration */ + rotator_reg_set_dst_crop_pos(rot, pos->x, pos->y); - /* Check crop boundary */ - if (!rotator_check_crop_boundary(&buf, control, crop)) { - DRM_ERROR("boundary errror\n"); - return -EINVAL; - } + return 0; +} - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; +static int rotator_dst_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, + u32 buf_id, enum drm_exynos_ipp_buf_ctrl ctrl) +{ + struct rot_context *rot = dev_get_drvdata(dev); + dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; + u32 fmt, hsize, vsize; + int i; - /* Map IOMMU */ - rot->exec_ret = rotator_iommu_map(&buf, req_buf, ¶ms, - &priv->iommu_list); - if (rot->exec_ret < 0) - goto err_iommu_map; + /* Set current buf_id */ + rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id; - /* Assign another src/dst_addr for NV12 image format */ - if (control->img_fmt == DRM_FORMAT_NV12) { - u32 size = crop->src_w * crop->src_h; + switch (ctrl) { + case IPP_BUF_CTRL_QUEUE: + /* Set address configuration */ + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + addr[i] = buf_info->base[i]; - buf.src_addr[buf.src_cnt + 1] = - buf.src_addr[buf.src_cnt] + size; - buf.dst_addr[buf.dst_cnt + 1] = - buf.dst_addr[buf.dst_cnt] + size; - } + /* Get format */ + fmt = rotator_reg_get_format(rot); - /* Execute */ - mutex_lock(&rot->exec_mutex); - rotator_execute(rot, &buf, control, crop); - if (!wait_for_completion_timeout(&rot->complete, 2 * HZ)) { - DRM_ERROR("timeout error\n"); - rot->exec_ret = -ETIMEDOUT; - mutex_unlock(&rot->exec_mutex); - goto err_iommu_map; - } - mutex_unlock(&rot->exec_mutex); + /* Re-set cb planar for NV12 format */ + if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && + (addr[EXYNOS_DRM_PLANAR_CB] == 0x00)) { + /* Get buf size */ + rotator_reg_get_dst_buf_size(rot, &hsize, &vsize); - /* Unmap IOMMU */ - rotator_iommu_unmap(&buf, ¶ms); + /* Set cb planar */ + addr[EXYNOS_DRM_PLANAR_CB] = + addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; + } - return rot->exec_ret; + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_dst_buf_addr(rot, addr[i], i); + break; + case IPP_BUF_CTRL_DEQUEUE: + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_dst_buf_addr(rot, buf_info->base[i], i); + break; + default: + /* Nothing to do */ + break; + } -err_iommu_map: - rotator_iommu_unmap(&buf, ¶ms); - return rot->exec_ret; + return 0; } -EXPORT_SYMBOL_GPL(exynos_drm_rotator_exec_ioctl); -static irqreturn_t rotator_irq_thread(int irq, void *arg) -{ - struct rot_context *rot = (struct rot_context *)arg; - enum rot_irq_status irq_status; - unsigned long flags; +static struct exynos_drm_ipp_ops rot_src_ops = { + .set_fmt = rotator_src_set_fmt, + .set_size = rotator_src_set_size, + .set_addr = rotator_src_set_addr, +}; - pm_qos_update_request(&rot->pm_qos, 0); +static struct exynos_drm_ipp_ops rot_dst_ops = { + .set_transf = rotator_dst_set_transf, + .set_size = rotator_dst_set_size, + .set_addr = rotator_dst_set_addr, +}; - /* Get execution result */ - spin_lock_irqsave(&rot->irq_lock, flags); - irq_status = rotator_reg_get_irq_status(rot); - rotator_reg_set_irq_status_clear(rot, irq_status); - spin_unlock_irqrestore(&rot->irq_lock, flags); +static int rotator_ippdrv_check_property(struct device *dev, + struct drm_exynos_ipp_property *property) +{ + struct drm_exynos_ipp_config *src_config = + &property->config[EXYNOS_DRM_OPS_SRC]; + struct drm_exynos_ipp_config *dst_config = + &property->config[EXYNOS_DRM_OPS_DST]; + struct drm_exynos_pos *src_pos = &src_config->pos; + struct drm_exynos_pos *dst_pos = &dst_config->pos; + struct drm_exynos_sz *src_sz = &src_config->sz; + struct drm_exynos_sz *dst_sz = &dst_config->sz; + bool swap = false; + + /* Check format configuration */ + if (src_config->fmt != dst_config->fmt) { + DRM_DEBUG_KMS("[%s]not support csc feature\n", __func__); + return -EINVAL; + } - rot->exec_ret = 0; - if (irq_status != ROT_IRQ_STATUS_COMPLETE) { - DRM_ERROR("the SFR is set illegally\n"); - rot->exec_ret = -EINVAL; - rotator_reg_get_dump(rot); + switch (src_config->fmt) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + /* No problem */ + break; + default: + DRM_DEBUG_KMS("[%s]not support format\n", __func__); + return -EINVAL; } - pm_runtime_put(rot->subdrv.dev); + /* Check transform configuration */ + if (src_config->degree != EXYNOS_DRM_DEGREE_0) { + DRM_DEBUG_KMS("[%s]not support source-side rotation\n", + __func__); + return -EINVAL; + } - complete(&rot->complete); + switch (dst_config->degree) { + case EXYNOS_DRM_DEGREE_90: + case EXYNOS_DRM_DEGREE_270: + swap = true; + case EXYNOS_DRM_DEGREE_0: + case EXYNOS_DRM_DEGREE_180: + /* No problem */ + break; + default: + DRM_DEBUG_KMS("[%s]invalid degree\n", __func__); + return -EINVAL; + } - return IRQ_HANDLED; -} + if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { + DRM_DEBUG_KMS("[%s]not support source-side flip\n", __func__); + return -EINVAL; + } -static int rotator_subdrv_open(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv; + switch (dst_config->flip) { + case EXYNOS_DRM_FLIP_NONE: + case EXYNOS_DRM_FLIP_VERTICAL: + case EXYNOS_DRM_FLIP_HORIZONTAL: + /* No problem */ + break; + default: + DRM_DEBUG_KMS("[%s]invalid flip\n", __func__); + return -EINVAL; + } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev, "failed to allocate priv\n"); - return -ENOMEM; + /* Check size configuration */ + if ((src_pos->x + src_pos->w > src_sz->hsize) || + (src_pos->y + src_pos->h > src_sz->vsize)) { + DRM_DEBUG_KMS("[%s]out of source buffer bound\n", __func__); + return -EINVAL; } - priv->dev = dev; - INIT_LIST_HEAD(&priv->iommu_list); + if (swap) { + if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || + (dst_pos->y + dst_pos->w > dst_sz->hsize)) { + DRM_DEBUG_KMS("[%s]out of destination buffer bound\n", + __func__); + return -EINVAL; + } - file_priv->rot_priv = priv; + if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { + DRM_DEBUG_KMS("[%s]not support scale feature\n", + __func__); + return -EINVAL; + } + } else { + if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || + (dst_pos->y + dst_pos->h > dst_sz->vsize)) { + DRM_DEBUG_KMS("[%s]out of destination buffer bound\n", + __func__); + return -EINVAL; + } + + if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { + DRM_DEBUG_KMS("[%s]not support scale feature\n", + __func__); + return -EINVAL; + } + } return 0; } -static void rotator_subdrv_close(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) +static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) { - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv = file_priv->rot_priv; - struct iommu_gem_map_params params; - struct iommu_info_node *node, *n; + struct rot_context *rot = dev_get_drvdata(dev); - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; + if (rot->suspended) { + DRM_ERROR("suspended state\n"); + return -EPERM; + } - list_for_each_entry_safe(node, n, &priv->iommu_list, list) { - params.gem_obj = node->gem_obj; - exynos_drm_iommu_unmap_gem(¶ms, node->dma_addr, - IOMMU_ROTATOR); - list_del(&node->list); - kfree(node); - node = NULL; + if (cmd != IPP_CMD_M2M) { + DRM_ERROR("not support cmd: %d\n", cmd); + return -EINVAL; } - kfree(priv); + /* Set interrupt enable */ + rotator_reg_set_irq(rot, true); + + /* start rotator operation */ + rotator_reg_set_start(rot); - return; + return 0; } static int __devinit rotator_probe(struct platform_device *pdev) @@ -677,7 +669,7 @@ static int __devinit rotator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rot_context *rot; struct resource *res; - struct exynos_drm_subdrv *subdrv; + struct exynos_drm_ippdrv *ippdrv; int ret; rot = kzalloc(sizeof(*rot), GFP_KERNEL); @@ -689,21 +681,6 @@ static int __devinit rotator_probe(struct platform_device *pdev) rot->limit_tbl = (struct rot_limit_table *) platform_get_device_id(pdev)->driver_data; - mutex_init(&rot->exec_mutex); - spin_lock_init(&rot->irq_lock); - - ret = exynos_drm_iommu_setup(dev); - if (ret < 0) { - dev_err(dev, "failed to setup iommu\n"); - goto err_iommu_setup; - } - - ret = exynos_drm_iommu_activate(dev); - if (ret < 0) { - dev_err(dev, "failed to activate iommu\n"); - goto err_iommu_activate; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "failed to find registers\n"); @@ -733,7 +710,7 @@ static int __devinit rotator_probe(struct platform_device *pdev) goto err_get_irq; } - ret = request_threaded_irq(rot->irq, NULL, rotator_irq_thread, + ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot); if (ret < 0) { dev_err(dev, "failed to request irq\n"); @@ -748,26 +725,28 @@ static int __devinit rotator_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - pm_qos_add_request(&rot->pm_qos, PM_QOS_BUS_DMA_THROUGHPUT, 0); - subdrv = &rot->subdrv; - subdrv->dev = dev; - subdrv->open = rotator_subdrv_open; - subdrv->close = rotator_subdrv_close; + ippdrv = &rot->ippdrv; + ippdrv->dev = dev; + ippdrv->iommu_used = true; + ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops; + ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops; + ippdrv->check_property = rotator_ippdrv_check_property; + ippdrv->start = rotator_ippdrv_start; platform_set_drvdata(pdev, rot); - ret = exynos_drm_subdrv_register(subdrv); + ret = exynos_drm_ippdrv_register(ippdrv); if (ret < 0) { dev_err(dev, "failed to register drm rotator device\n"); - goto err_subdrv_register; + goto err_ippdrv_register; } dev_info(dev, "The exynos rotator is probed successfully\n"); return 0; -err_subdrv_register: +err_ippdrv_register: pm_runtime_disable(dev); clk_put(rot->clock); err_clk_get: @@ -778,23 +757,18 @@ err_ioremap: release_resource(rot->regs_res); kfree(rot->regs_res); err_get_resource: - exynos_drm_iommu_deactivate(dev); -err_iommu_activate: - exynos_drm_iommu_cleanup(dev); -err_iommu_setup: kfree(rot); return ret; } static int __devexit rotator_remove(struct platform_device *pdev) { - struct rot_context *rot = platform_get_drvdata(pdev); - - pm_qos_remove_request(&rot->pm_qos); + struct device *dev = &pdev->dev; + struct rot_context *rot = dev_get_drvdata(dev); - exynos_drm_subdrv_unregister(&rot->subdrv); + exynos_drm_ippdrv_unregister(&rot->ippdrv); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); clk_put(rot->clock); free_irq(rot->irq, rot); @@ -804,9 +778,6 @@ static int __devexit rotator_remove(struct platform_device *pdev) release_resource(rot->regs_res); kfree(rot->regs_res); - exynos_drm_iommu_deactivate(&pdev->dev); - exynos_drm_iommu_cleanup(&pdev->dev); - kfree(rot); return 0; @@ -841,14 +812,13 @@ struct platform_device_id rotator_driver_ids[] = { static int rotator_suspend(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); - - /* Check & wait for running state */ - mutex_lock(&rot->exec_mutex); - mutex_unlock(&rot->exec_mutex); + struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; + struct drm_device *drm_dev = ippdrv->drm_dev; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; rot->suspended = true; - exynos_drm_iommu_deactivate(dev); + exynos_drm_iommu_deactivate(drm_priv->vmm, dev); return 0; } @@ -856,12 +826,18 @@ static int rotator_suspend(struct device *dev) static int rotator_resume(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); + struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; + struct drm_device *drm_dev = ippdrv->drm_dev; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; + int ret; - rot->suspended = false; + ret = exynos_drm_iommu_activate(drm_priv->vmm, dev); + if (ret) + DRM_ERROR("failed to activate iommu\n"); - exynos_drm_iommu_activate(dev); + rot->suspended = false; - return 0; + return ret; } #endif @@ -880,7 +856,6 @@ static int rotator_runtime_resume(struct device *dev) struct rot_context *rot = dev_get_drvdata(dev); clk_enable(rot->clock); - pm_qos_update_request(&rot->pm_qos, 400000); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h index 5f383d5..fe929c9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.h +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h @@ -10,16 +10,4 @@ #ifndef _EXYNOS_DRM_ROTATOR_H_ #define _EXYNOS_DRM_ROTATOR_H_ -#ifdef CONFIG_DRM_EXYNOS_ROTATOR -extern int exynos_drm_rotator_exec_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -#else -static inline int exynos_drm_rotator_exec_ioctl(struct drm_device *dev, - void *data, - struct drm_file *file_priv) -{ - return -ENOTTY; -} -#endif - #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_ump.c b/drivers/gpu/drm/exynos/exynos_drm_ump.c index fd9ba2a..f92759f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ump.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ump.c @@ -85,7 +85,7 @@ static int exynos_drm_ump_add_buffer(void *obj, * physically continuous buffer so let a ump descriptor * have one buffer address. */ - ump_mem_desc[0].addr = (unsigned long)buf->dma_addr; + ump_mem_desc[0].addr = (unsigned long)buf->paddr; ump_mem_desc[0].size = buf->size; } @@ -115,13 +115,6 @@ static int exynos_drm_ump_add_buffer(void *obj, static void exynos_drm_ump_release_buffer(unsigned int handle) { DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!handle) { - DRM_DEBUG_KMS("invalid ump handle.\n"); - return; - } - - ump_dd_reference_release((ump_dd_handle)handle); } static struct exynos_drm_private_cb ump_callback = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 876e460..44bcdc8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -468,7 +468,7 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) return 0; } -static void vidi_subdrv_remove(struct drm_device *drm_dev) +static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev) { DRM_DEBUG_KMS("%s\n", __FILE__); @@ -549,6 +549,8 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, struct exynos_drm_manager *manager; struct exynos_drm_display_ops *display_ops; struct drm_exynos_vidi_connection *vidi = data; + struct edid *raw_edid; + int edid_len; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -557,11 +559,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - if (!vidi->edid) { - DRM_DEBUG_KMS("edid data is null.\n"); - return -EINVAL; - } - if (vidi->connection > 1) { DRM_DEBUG_KMS("connection should be 0 or 1.\n"); return -EINVAL; @@ -588,8 +585,23 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - if (vidi->connection) - ctx->raw_edid = (struct edid *)vidi->edid; + if (vidi->connection) { + if (!vidi->edid) { + DRM_DEBUG_KMS("edid data is null.\n"); + return -EINVAL; + } + raw_edid = (struct edid *)vidi->edid; + edid_len = (1 + raw_edid->extensions) * EDID_LENGTH; + ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL); + if (!ctx->raw_edid) { + DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); + return -ENOMEM; + } + memcpy(ctx->raw_edid, raw_edid, edid_len); + } else { + kfree(ctx->raw_edid); + ctx->raw_edid = NULL; + } ctx->connected = vidi->connection; drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); @@ -644,6 +656,7 @@ static int __devexit vidi_remove(struct platform_device *pdev) exynos_drm_subdrv_unregister(&ctx->subdrv); + kfree(ctx->raw_edid); kfree(ctx); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 0c44cb7..48d7f98 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -37,6 +37,7 @@ #include "exynos_drm_drv.h" #include "exynos_drm_hdmi.h" +#include "exynos_drm_iommu.h" #include "exynos_hdmi.h" @@ -61,6 +62,7 @@ struct hdmi_context { bool powered; bool is_v13; bool dvi_mode; + bool iommu_on; struct mutex hdmi_mutex; struct resource *regs_res; @@ -927,8 +929,6 @@ static const struct hdmi_conf hdmi_confs[] = { { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 }, { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 }, { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, - { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, { 1920, 1080, 30, false, hdmiphy_conf74_176, &hdmi_conf_1080p30 }, }; @@ -1838,6 +1838,104 @@ static void hdmi_timing_apply(struct hdmi_context *hdata) hdmi_v14_timing_apply(hdata); } +static int hdmi_phy_ctrl(struct i2c_client *client, u8 reg, u8 bit, + u8 *read_buffer, bool enable) +{ + int ret; + u8 operation[2]; + + operation[0] = reg; + operation[1] = enable ? (read_buffer[reg] & (~(1 << bit))) : + (read_buffer[reg] | (1 << bit)); + read_buffer[reg] = operation[1]; + + ret = i2c_master_send(client, operation, 2); + if (ret != 2) { + DRM_ERROR("failed to turn %s HDMIPHY via I2C\n", + enable ? "enable" : "disable"); + return -EIO; + } + + return 0; +} + +static int hdmi_phy_power_ctrl(struct hdmi_context *hdata, bool enable) +{ + struct hdmi_resources *res = &hdata->res; + u8 operation[2]; + u8 read_buffer[32]; + int ret = 0, i; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + clk_enable(res->hdmiphy); + + /* read full register */ + operation[0] = 0x1; + i2c_master_send(hdata->hdmiphy_port, operation, 1); + + memset(read_buffer, 0x0, sizeof(read_buffer)); + ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32); + if (ret < 0) { + DRM_ERROR("failed to read hdmiphy config\n"); + goto err_clear; + } + + for (i = 0; i < ret; i++) + DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - " + "recv [0x%02x]\n", i, operation[i], read_buffer[i]); + + /* ocspad control */ + operation[0] = 0x0b; + if (enable) + operation[1] = 0xd8; + else + operation[1] = 0x18; + read_buffer[0x0b] = operation[1]; + + ret = i2c_master_send(hdata->hdmiphy_port, operation, 2); + if (ret != 2) { + DRM_ERROR("failed to %s osc pad\n", + enable ? "enable" : "disable"); + goto err_clear; + } + + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x7, read_buffer, enable); + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x0, read_buffer, enable); + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x1, read_buffer, enable); + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x2, read_buffer, enable); + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x4, read_buffer, enable); + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x5, read_buffer, enable); + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x1d, 0x6, read_buffer, enable); + + if (!enable) + hdmi_phy_ctrl(hdata->hdmiphy_port, 0x4, 0x3, read_buffer, 0); + + /* read full register */ + operation[0] = 0x1; + i2c_master_send(hdata->hdmiphy_port, operation, 1); + + memset(read_buffer, 0x0, sizeof(read_buffer)); + ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32); + if (ret < 0) { + DRM_ERROR("failed to read hdmiphy config\n"); + goto err_clear; + } + + for (i = 0; i < ret; i++) + DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - " + "recv [0x%02x]\n", i, operation[i], read_buffer[i]); + + clk_disable(res->hdmiphy); + + return 0; + +err_clear: + clk_disable(res->hdmiphy); + + return ret; +} + static void hdmiphy_conf_reset(struct hdmi_context *hdata) { u8 buffer[2]; @@ -2003,15 +2101,46 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width, static void hdmi_commit(void *ctx) { struct hdmi_context *hdata = ctx; + struct exynos_drm_private *drm_priv; + struct exynos_drm_hdmi_context *drm_hdmi_ctx; + struct drm_device *drm_dev; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmi_conf_apply(hdata); + + /* + * parent_ctx is created at hdmi_probe() and + * parent_ctx->drm_dev is set at hdmi_subdrv_probe() + */ + drm_hdmi_ctx = hdata->parent_ctx; + drm_dev = drm_hdmi_ctx->drm_dev; + if (drm_dev) + drm_priv = drm_dev->dev_private; + else + return; + + /* + * if iommu support for exynos drm was enabled, this function is + * called first time(!hdata->iommu_on) then enable iommu unit. + */ + if (drm_priv->vmm && !hdata->iommu_on) { + int ret; + + ret = exynos_drm_iommu_activate(drm_priv->vmm, hdata->dev); + if (ret < 0) { + DRM_ERROR("failed to activate iommu.\n"); + return; + } + + hdata->iommu_on = true; + } } static void hdmi_poweron(struct hdmi_context *hdata) { struct hdmi_resources *res = &hdata->res; + int ret; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -2029,6 +2158,32 @@ static void hdmi_poweron(struct hdmi_context *hdata) pm_runtime_get_sync(hdata->dev); + ret = hdmi_phy_power_ctrl(hdata, true); + if (ret) { + DRM_ERROR("failed to control phy power\n"); + return; + } + + if (hdata->iommu_on) { + struct exynos_drm_private *drm_priv; + struct exynos_drm_hdmi_context *drm_hdmi_ctx; + struct drm_device *drm_dev; + + drm_hdmi_ctx = hdata->parent_ctx; + drm_dev = drm_hdmi_ctx->drm_dev; + + if (drm_dev) + drm_priv = drm_dev->dev_private; + else + return; + + ret = exynos_drm_iommu_activate(drm_priv->vmm, hdata->dev); + if (ret < 0) { + DRM_ERROR("failed to activate iommu.\n"); + return; + } + } + regulator_bulk_enable(res->regul_count, res->regul_bulk); clk_enable(res->hdmiphy); clk_enable(res->hdmi); @@ -2038,6 +2193,7 @@ static void hdmi_poweron(struct hdmi_context *hdata) static void hdmi_poweroff(struct hdmi_context *hdata) { struct hdmi_resources *res = &hdata->res; + int ret; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -2057,16 +2213,48 @@ static void hdmi_poweroff(struct hdmi_context *hdata) clk_disable(res->hdmiphy); regulator_bulk_disable(res->regul_count, res->regul_bulk); - pm_runtime_put_sync(hdata->dev); - mutex_lock(&hdata->hdmi_mutex); if (hdata->cfg_hpd) hdata->cfg_hpd(false); + ret = hdmi_phy_power_ctrl(hdata, false); + if (ret) { + DRM_ERROR("failed to control phy power\n"); + return; + } + + if (hdata->iommu_on) { + struct exynos_drm_private *drm_priv; + struct exynos_drm_hdmi_context *drm_hdmi_ctx; + struct drm_device *drm_dev; + + drm_hdmi_ctx = hdata->parent_ctx; + drm_dev = drm_hdmi_ctx->drm_dev; + if (drm_dev) + drm_priv = drm_dev->dev_private; + else { + if (hdata->cfg_hpd) + hdata->cfg_hpd(true); + + regulator_bulk_enable(res->regul_count, + res->regul_bulk); + + clk_enable(res->hdmiphy); + clk_enable(res->hdmi); + clk_enable(res->sclk_hdmi); + + mutex_unlock(&hdata->hdmi_mutex); + return; + } + + exynos_drm_iommu_deactivate(drm_priv->vmm, hdata->dev); + } + hdata->powered = false; out: mutex_unlock(&hdata->hdmi_mutex); + pm_runtime_put_sync(hdata->dev); } static void hdmi_dpms(void *ctx, int mode) @@ -2350,6 +2538,17 @@ static int __devinit hdmi_probe(struct platform_device *pdev) hdata->hdmiphy_port = hdmi_hdmiphy; + /* + * HDMI PHY power off + * HDMI PHY is on as default configuration + * So, HDMI PHY must be turned off if it's not used + */ + ret = hdmi_phy_power_ctrl(hdata, false); + if (ret) { + DRM_ERROR("failed to control phy power\n"); + goto err_hdmiphy; + } + hdata->external_irq = platform_get_irq_byname(pdev, "external_irq"); if (hdata->external_irq < 0) { DRM_ERROR("failed to get platform irq\n"); @@ -2435,6 +2634,7 @@ static int __devexit hdmi_remove(struct platform_device *pdev) i2c_del_driver(&ddc_driver); kfree(hdata); + kfree(ctx); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 68ef010..206cbbc 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -601,18 +601,20 @@ static void mixer_win_reset(struct mixer_context *ctx) mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); /* setting graphical layers */ - val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ val |= MXR_GRP_CFG_WIN_BLEND_EN; + val |= MXR_GRP_CFG_BLEND_PRE_MUL; + val |= MXR_GRP_CFG_PIXEL_BLEND_EN; val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ /* the same configuration for both layers */ mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val); - - val |= MXR_GRP_CFG_BLEND_PRE_MUL; - val |= MXR_GRP_CFG_PIXEL_BLEND_EN; mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val); + /* setting video layers */ + val = MXR_GRP_CFG_ALPHA_VAL(0); + mixer_reg_write(res, MXR_VIDEO_CFG, val); + /* configuration of Video Processor Registers */ vp_win_reset(ctx); vp_default_filter(res); diff --git a/drivers/gpu/drm/exynos/regs-fimc.h b/drivers/gpu/drm/exynos/regs-fimc.h new file mode 100644 index 0000000..be014b3 --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-fimc.h @@ -0,0 +1,669 @@ +/* drivers/gpu/drm/exynos/regs-fimc.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Register definition file for Samsung Camera Interface (FIMC) driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef SAMSUNG_REGS_FIMC_H +#define SAMSUNG_REGS_FIMC_H + +/* + * Register part +*/ +/* Input source format */ +#define EXYNOS_CISRCFMT (0x00) +/* Window offset */ +#define EXYNOS_CIWDOFST (0x04) +/* Global control */ +#define EXYNOS_CIGCTRL (0x08) +/* Window offset 2 */ +#define EXYNOS_CIWDOFST2 (0x14) +/* Y 1st frame start address for output DMA */ +#define EXYNOS_CIOYSA1 (0x18) +/* Y 2nd frame start address for output DMA */ +#define EXYNOS_CIOYSA2 (0x1c) +/* Y 3rd frame start address for output DMA */ +#define EXYNOS_CIOYSA3 (0x20) +/* Y 4th frame start address for output DMA */ +#define EXYNOS_CIOYSA4 (0x24) +/* Cb 1st frame start address for output DMA */ +#define EXYNOS_CIOCBSA1 (0x28) +/* Cb 2nd frame start address for output DMA */ +#define EXYNOS_CIOCBSA2 (0x2c) +/* Cb 3rd frame start address for output DMA */ +#define EXYNOS_CIOCBSA3 (0x30) +/* Cb 4th frame start address for output DMA */ +#define EXYNOS_CIOCBSA4 (0x34) +/* Cr 1st frame start address for output DMA */ +#define EXYNOS_CIOCRSA1 (0x38) +/* Cr 2nd frame start address for output DMA */ +#define EXYNOS_CIOCRSA2 (0x3c) +/* Cr 3rd frame start address for output DMA */ +#define EXYNOS_CIOCRSA3 (0x40) +/* Cr 4th frame start address for output DMA */ +#define EXYNOS_CIOCRSA4 (0x44) +/* Target image format */ +#define EXYNOS_CITRGFMT (0x48) +/* Output DMA control */ +#define EXYNOS_CIOCTRL (0x4c) +/* Pre-scaler control 1 */ +#define EXYNOS_CISCPRERATIO (0x50) +/* Pre-scaler control 2 */ +#define EXYNOS_CISCPREDST (0x54) +/* Main scaler control */ +#define EXYNOS_CISCCTRL (0x58) +/* Target area */ +#define EXYNOS_CITAREA (0x5c) +/* Status */ +#define EXYNOS_CISTATUS (0x64) +/* Status2 */ +#define EXYNOS_CISTATUS2 (0x68) +/* Image capture enable command */ +#define EXYNOS_CIIMGCPT (0xc0) +/* Capture sequence */ +#define EXYNOS_CICPTSEQ (0xc4) +/* Image effects */ +#define EXYNOS_CIIMGEFF (0xd0) +/* Y frame start address for input DMA */ +#define EXYNOS_CIIYSA0 (0xd4) +/* Cb frame start address for input DMA */ +#define EXYNOS_CIICBSA0 (0xd8) +/* Cr frame start address for input DMA */ +#define EXYNOS_CIICRSA0 (0xdc) +/* Input DMA Y Line Skip */ +#define EXYNOS_CIILINESKIP_Y (0xec) +/* Input DMA Cb Line Skip */ +#define EXYNOS_CIILINESKIP_CB (0xf0) +/* Input DMA Cr Line Skip */ +#define EXYNOS_CIILINESKIP_CR (0xf4) +/* Real input DMA image size */ +#define EXYNOS_CIREAL_ISIZE (0xf8) +/* Input DMA control */ +#define EXYNOS_MSCTRL (0xfc) +/* Y frame start address for input DMA */ +#define EXYNOS_CIIYSA1 (0x144) +/* Cb frame start address for input DMA */ +#define EXYNOS_CIICBSA1 (0x148) +/* Cr frame start address for input DMA */ +#define EXYNOS_CIICRSA1 (0x14c) +/* Output DMA Y offset */ +#define EXYNOS_CIOYOFF (0x168) +/* Output DMA CB offset */ +#define EXYNOS_CIOCBOFF (0x16c) +/* Output DMA CR offset */ +#define EXYNOS_CIOCROFF (0x170) +/* Input DMA Y offset */ +#define EXYNOS_CIIYOFF (0x174) +/* Input DMA CB offset */ +#define EXYNOS_CIICBOFF (0x178) +/* Input DMA CR offset */ +#define EXYNOS_CIICROFF (0x17c) +/* Input DMA original image size */ +#define EXYNOS_ORGISIZE (0x180) +/* Output DMA original image size */ +#define EXYNOS_ORGOSIZE (0x184) +/* Real output DMA image size */ +#define EXYNOS_CIEXTEN (0x188) +/* DMA parameter */ +#define EXYNOS_CIDMAPARAM (0x18c) +/* MIPI CSI image format */ +#define EXYNOS_CSIIMGFMT (0x194) +/* FIMC Clock Source Select */ +#define EXYNOS_MISC_FIMC (0x198) + +/* Add for FIMC v5.1 */ +/* Output Frame Buffer Sequence */ +#define EXYNOS_CIFCNTSEQ (0x1fc) +/* Y 5th frame start address for output DMA */ +#define EXYNOS_CIOYSA5 (0x200) +/* Y 6th frame start address for output DMA */ +#define EXYNOS_CIOYSA6 (0x204) +/* Y 7th frame start address for output DMA */ +#define EXYNOS_CIOYSA7 (0x208) +/* Y 8th frame start address for output DMA */ +#define EXYNOS_CIOYSA8 (0x20c) +/* Y 9th frame start address for output DMA */ +#define EXYNOS_CIOYSA9 (0x210) +/* Y 10th frame start address for output DMA */ +#define EXYNOS_CIOYSA10 (0x214) +/* Y 11th frame start address for output DMA */ +#define EXYNOS_CIOYSA11 (0x218) +/* Y 12th frame start address for output DMA */ +#define EXYNOS_CIOYSA12 (0x21c) +/* Y 13th frame start address for output DMA */ +#define EXYNOS_CIOYSA13 (0x220) +/* Y 14th frame start address for output DMA */ +#define EXYNOS_CIOYSA14 (0x224) +/* Y 15th frame start address for output DMA */ +#define EXYNOS_CIOYSA15 (0x228) +/* Y 16th frame start address for output DMA */ +#define EXYNOS_CIOYSA16 (0x22c) +/* Y 17th frame start address for output DMA */ +#define EXYNOS_CIOYSA17 (0x230) +/* Y 18th frame start address for output DMA */ +#define EXYNOS_CIOYSA18 (0x234) +/* Y 19th frame start address for output DMA */ +#define EXYNOS_CIOYSA19 (0x238) +/* Y 20th frame start address for output DMA */ +#define EXYNOS_CIOYSA20 (0x23c) +/* Y 21th frame start address for output DMA */ +#define EXYNOS_CIOYSA21 (0x240) +/* Y 22th frame start address for output DMA */ +#define EXYNOS_CIOYSA22 (0x244) +/* Y 23th frame start address for output DMA */ +#define EXYNOS_CIOYSA23 (0x248) +/* Y 24th frame start address for output DMA */ +#define EXYNOS_CIOYSA24 (0x24c) +/* Y 25th frame start address for output DMA */ +#define EXYNOS_CIOYSA25 (0x250) +/* Y 26th frame start address for output DMA */ +#define EXYNOS_CIOYSA26 (0x254) +/* Y 27th frame start address for output DMA */ +#define EXYNOS_CIOYSA27 (0x258) +/* Y 28th frame start address for output DMA */ +#define EXYNOS_CIOYSA28 (0x25c) +/* Y 29th frame start address for output DMA */ +#define EXYNOS_CIOYSA29 (0x260) +/* Y 30th frame start address for output DMA */ +#define EXYNOS_CIOYSA30 (0x264) +/* Y 31th frame start address for output DMA */ +#define EXYNOS_CIOYSA31 (0x268) +/* Y 32th frame start address for output DMA */ +#define EXYNOS_CIOYSA32 (0x26c) + +/* CB 5th frame start address for output DMA */ +#define EXYNOS_CIOCBSA5 (0x270) +/* CB 6th frame start address for output DMA */ +#define EXYNOS_CIOCBSA6 (0x274) +/* CB 7th frame start address for output DMA */ +#define EXYNOS_CIOCBSA7 (0x278) +/* CB 8th frame start address for output DMA */ +#define EXYNOS_CIOCBSA8 (0x27c) +/* CB 9th frame start address for output DMA */ +#define EXYNOS_CIOCBSA9 (0x280) +/* CB 10th frame start address for output DMA */ +#define EXYNOS_CIOCBSA10 (0x284) +/* CB 11th frame start address for output DMA */ +#define EXYNOS_CIOCBSA11 (0x288) +/* CB 12th frame start address for output DMA */ +#define EXYNOS_CIOCBSA12 (0x28c) +/* CB 13th frame start address for output DMA */ +#define EXYNOS_CIOCBSA13 (0x290) +/* CB 14th frame start address for output DMA */ +#define EXYNOS_CIOCBSA14 (0x294) +/* CB 15th frame start address for output DMA */ +#define EXYNOS_CIOCBSA15 (0x298) +/* CB 16th frame start address for output DMA */ +#define EXYNOS_CIOCBSA16 (0x29c) +/* CB 17th frame start address for output DMA */ +#define EXYNOS_CIOCBSA17 (0x2a0) +/* CB 18th frame start address for output DMA */ +#define EXYNOS_CIOCBSA18 (0x2a4) +/* CB 19th frame start address for output DMA */ +#define EXYNOS_CIOCBSA19 (0x2a8) +/* CB 20th frame start address for output DMA */ +#define EXYNOS_CIOCBSA20 (0x2ac) +/* CB 21th frame start address for output DMA */ +#define EXYNOS_CIOCBSA21 (0x2b0) +/* CB 22th frame start address for output DMA */ +#define EXYNOS_CIOCBSA22 (0x2b4) +/* CB 23th frame start address for output DMA */ +#define EXYNOS_CIOCBSA23 (0x2b8) +/* CB 24th frame start address for output DMA */ +#define EXYNOS_CIOCBSA24 (0x2bc) +/* CB 25th frame start address for output DMA */ +#define EXYNOS_CIOCBSA25 (0x2c0) +/* CB 26th frame start address for output DMA */ +#define EXYNOS_CIOCBSA26 (0x2c4) +/* CB 27th frame start address for output DMA */ +#define EXYNOS_CIOCBSA27 (0x2c8) +/* CB 28th frame start address for output DMA */ +#define EXYNOS_CIOCBSA28 (0x2cc) +/* CB 29th frame start address for output DMA */ +#define EXYNOS_CIOCBSA29 (0x2d0) +/* CB 30th frame start address for output DMA */ +#define EXYNOS_CIOCBSA30 (0x2d4) +/* CB 31th frame start address for output DMA */ +#define EXYNOS_CIOCBSA31 (0x2d8) +/* CB 32th frame start address for output DMA */ +#define EXYNOS_CIOCBSA32 (0x2dc) + +/* CR 5th frame start address for output DMA */ +#define EXYNOS_CIOCRSA5 (0x2e0) +/* CR 6th frame start address for output DMA */ +#define EXYNOS_CIOCRSA6 (0x2e4) +/* CR 7th frame start address for output DMA */ +#define EXYNOS_CIOCRSA7 (0x2e8) +/* CR 8th frame start address for output DMA */ +#define EXYNOS_CIOCRSA8 (0x2ec) +/* CR 9th frame start address for output DMA */ +#define EXYNOS_CIOCRSA9 (0x2f0) +/* CR 10th frame start address for output DMA */ +#define EXYNOS_CIOCRSA10 (0x2f4) +/* CR 11th frame start address for output DMA */ +#define EXYNOS_CIOCRSA11 (0x2f8) +/* CR 12th frame start address for output DMA */ +#define EXYNOS_CIOCRSA12 (0x2fc) +/* CR 13th frame start address for output DMA */ +#define EXYNOS_CIOCRSA13 (0x300) +/* CR 14th frame start address for output DMA */ +#define EXYNOS_CIOCRSA14 (0x304) +/* CR 15th frame start address for output DMA */ +#define EXYNOS_CIOCRSA15 (0x308) +/* CR 16th frame start address for output DMA */ +#define EXYNOS_CIOCRSA16 (0x30c) +/* CR 17th frame start address for output DMA */ +#define EXYNOS_CIOCRSA17 (0x310) +/* CR 18th frame start address for output DMA */ +#define EXYNOS_CIOCRSA18 (0x314) +/* CR 19th frame start address for output DMA */ +#define EXYNOS_CIOCRSA19 (0x318) +/* CR 20th frame start address for output DMA */ +#define EXYNOS_CIOCRSA20 (0x31c) +/* CR 21th frame start address for output DMA */ +#define EXYNOS_CIOCRSA21 (0x320) +/* CR 22th frame start address for output DMA */ +#define EXYNOS_CIOCRSA22 (0x324) +/* CR 23th frame start address for output DMA */ +#define EXYNOS_CIOCRSA23 (0x328) +/* CR 24th frame start address for output DMA */ +#define EXYNOS_CIOCRSA24 (0x32c) +/* CR 25th frame start address for output DMA */ +#define EXYNOS_CIOCRSA25 (0x330) +/* CR 26th frame start address for output DMA */ +#define EXYNOS_CIOCRSA26 (0x334) +/* CR 27th frame start address for output DMA */ +#define EXYNOS_CIOCRSA27 (0x338) +/* CR 28th frame start address for output DMA */ +#define EXYNOS_CIOCRSA28 (0x33c) +/* CR 29th frame start address for output DMA */ +#define EXYNOS_CIOCRSA29 (0x340) +/* CR 30th frame start address for output DMA */ +#define EXYNOS_CIOCRSA30 (0x344) +/* CR 31th frame start address for output DMA */ +#define EXYNOS_CIOCRSA31 (0x348) +/* CR 32th frame start address for output DMA */ +#define EXYNOS_CIOCRSA32 (0x34c) + +/* + * Macro part +*/ +/* frame start address 1 ~ 4, 5 ~ 32 */ +/* Number of Default PingPong Memory */ +#define DEF_PP 4 +#define EXYNOS_CIOYSA(__x) \ + (((__x) < DEF_PP) ? \ + (EXYNOS_CIOYSA1 + (__x) * 4) : \ + (EXYNOS_CIOYSA5 + ((__x) - DEF_PP) * 4)) +#define EXYNOS_CIOCBSA(__x) \ + (((__x) < DEF_PP) ? \ + (EXYNOS_CIOCBSA1 + (__x) * 4) : \ + (EXYNOS_CIOCBSA5 + ((__x) - DEF_PP) * 4)) +#define EXYNOS_CIOCRSA(__x) \ + (((__x) < DEF_PP) ? \ + (EXYNOS_CIOCRSA1 + (__x) * 4) : \ + (EXYNOS_CIOCRSA5 + ((__x) - DEF_PP) * 4)) +/* Number of Default PingPong Memory */ +#define DEF_IPP 1 +#define EXYNOS_CIIYSA(__x) \ + (((__x) < DEF_IPP) ? \ + (EXYNOS_CIIYSA0) : (EXYNOS_CIIYSA1)) +#define EXYNOS_CIICBSA(__x) \ + (((__x) < DEF_IPP) ? \ + (EXYNOS_CIICBSA0) : (EXYNOS_CIICBSA1)) +#define EXYNOS_CIICRSA(__x) \ + (((__x) < DEF_IPP) ? \ + (EXYNOS_CIICRSA0) : (EXYNOS_CIICRSA1)) + +#define EXYNOS_CISRCFMT_SOURCEHSIZE(x) ((x) << 16) +#define EXYNOS_CISRCFMT_SOURCEVSIZE(x) ((x) << 0) + +#define EXYNOS_CIWDOFST_WINHOROFST(x) ((x) << 16) +#define EXYNOS_CIWDOFST_WINVEROFST(x) ((x) << 0) + +#define EXYNOS_CIWDOFST2_WINHOROFST2(x) ((x) << 16) +#define EXYNOS_CIWDOFST2_WINVEROFST2(x) ((x) << 0) + +#define EXYNOS_CITRGFMT_TARGETHSIZE(x) (((x) & 0x1fff) << 16) +#define EXYNOS_CITRGFMT_TARGETVSIZE(x) (((x) & 0x1fff) << 0) + +#define EXYNOS_CISCPRERATIO_SHFACTOR(x) ((x) << 28) +#define EXYNOS_CISCPRERATIO_PREHORRATIO(x) ((x) << 16) +#define EXYNOS_CISCPRERATIO_PREVERRATIO(x) ((x) << 0) + +#define EXYNOS_CISCPREDST_PREDSTWIDTH(x) ((x) << 16) +#define EXYNOS_CISCPREDST_PREDSTHEIGHT(x) ((x) << 0) + +#define EXYNOS_CISCCTRL_MAINHORRATIO(x) ((x) << 16) +#define EXYNOS_CISCCTRL_MAINVERRATIO(x) ((x) << 0) + +#define EXYNOS_CITAREA_TARGET_AREA(x) ((x) << 0) + +#define EXYNOS_CISTATUS_GET_FRAME_COUNT(x) (((x) >> 26) & 0x3) +#define EXYNOS_CISTATUS_GET_FRAME_END(x) (((x) >> 17) & 0x1) +#define EXYNOS_CISTATUS_GET_LAST_CAPTURE_END(x) (((x) >> 16) & 0x1) +#define EXYNOS_CISTATUS_GET_LCD_STATUS(x) (((x) >> 9) & 0x1) +#define EXYNOS_CISTATUS_GET_ENVID_STATUS(x) (((x) >> 8) & 0x1) + +#define EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(x) (((x) >> 7) & 0x3f) +#define EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(x) ((x) & 0x3f) + +#define EXYNOS_CIIMGEFF_FIN(x) ((x & 0x7) << 26) +#define EXYNOS_CIIMGEFF_PAT_CB(x) ((x) << 13) +#define EXYNOS_CIIMGEFF_PAT_CR(x) ((x) << 0) + +#define EXYNOS_CIILINESKIP(x) (((x) & 0xf) << 24) + +#define EXYNOS_CIREAL_ISIZE_HEIGHT(x) ((x) << 16) +#define EXYNOS_CIREAL_ISIZE_WIDTH(x) ((x) << 0) + +#define EXYNOS_MSCTRL_SUCCESSIVE_COUNT(x) ((x) << 24) +#define EXYNOS_MSCTRL_GET_INDMA_STATUS(x) ((x) & 0x1) + +#define EXYNOS_CIOYOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIOYOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIOCBOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIOCBOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIOCROFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIOCROFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIIYOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIIYOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIICBOFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIICBOFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIICROFF_VERTICAL(x) ((x) << 16) +#define EXYNOS_CIICROFF_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_ORGISIZE_VERTICAL(x) ((x) << 16) +#define EXYNOS_ORGISIZE_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_ORGOSIZE_VERTICAL(x) ((x) << 16) +#define EXYNOS_ORGOSIZE_HORIZONTAL(x) ((x) << 0) + +#define EXYNOS_CIEXTEN_TARGETH_EXT(x) ((((x) & 0x2000) >> 13) << 26) +#define EXYNOS_CIEXTEN_TARGETV_EXT(x) ((((x) & 0x2000) >> 13) << 24) +#define EXYNOS_CIEXTEN_MAINHORRATIO_EXT(x) (((x) & 0x3F) << 10) +#define EXYNOS_CIEXTEN_MAINVERRATIO_EXT(x) ((x) & 0x3F) + +/* + * Bit definition part +*/ +/* Source format register */ +#define EXYNOS_CISRCFMT_ITU601_8BIT (1 << 31) +#define EXYNOS_CISRCFMT_ITU656_8BIT (0 << 31) +#define EXYNOS_CISRCFMT_ITU601_16BIT (1 << 29) +#define EXYNOS_CISRCFMT_ORDER422_YCBYCR (0 << 14) +#define EXYNOS_CISRCFMT_ORDER422_YCRYCB (1 << 14) +#define EXYNOS_CISRCFMT_ORDER422_CBYCRY (2 << 14) +#define EXYNOS_CISRCFMT_ORDER422_CRYCBY (3 << 14) +/* ITU601 16bit only */ +#define EXYNOS_CISRCFMT_ORDER422_Y4CBCRCBCR (0 << 14) +/* ITU601 16bit only */ +#define EXYNOS_CISRCFMT_ORDER422_Y4CRCBCRCB (1 << 14) + +/* Window offset register */ +#define EXYNOS_CIWDOFST_WINOFSEN (1 << 31) +#define EXYNOS_CIWDOFST_CLROVFIY (1 << 30) +#define EXYNOS_CIWDOFST_CLROVRLB (1 << 29) +#define EXYNOS_CIWDOFST_WINHOROFST_MASK (0x7ff << 16) +#define EXYNOS_CIWDOFST_CLROVFICB (1 << 15) +#define EXYNOS_CIWDOFST_CLROVFICR (1 << 14) +#define EXYNOS_CIWDOFST_WINVEROFST_MASK (0xfff << 0) + +/* Global control register */ +#define EXYNOS_CIGCTRL_SWRST (1 << 31) +#define EXYNOS_CIGCTRL_CAMRST_A (1 << 30) +#define EXYNOS_CIGCTRL_SELCAM_ITU_B (0 << 29) +#define EXYNOS_CIGCTRL_SELCAM_ITU_A (1 << 29) +#define EXYNOS_CIGCTRL_SELCAM_ITU_MASK (1 << 29) +#define EXYNOS_CIGCTRL_TESTPATTERN_NORMAL (0 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_COLOR_BAR (1 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_HOR_INC (2 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_VER_INC (3 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_MASK (3 << 27) +#define EXYNOS_CIGCTRL_TESTPATTERN_SHIFT (27) +#define EXYNOS_CIGCTRL_INVPOLPCLK (1 << 26) +#define EXYNOS_CIGCTRL_INVPOLVSYNC (1 << 25) +#define EXYNOS_CIGCTRL_INVPOLHREF (1 << 24) +#define EXYNOS_CIGCTRL_IRQ_OVFEN (1 << 22) +#define EXYNOS_CIGCTRL_HREF_MASK (1 << 21) +#define EXYNOS_CIGCTRL_IRQ_EDGE (0 << 20) +#define EXYNOS_CIGCTRL_IRQ_LEVEL (1 << 20) +#define EXYNOS_CIGCTRL_IRQ_CLR (1 << 19) +#define EXYNOS_CIGCTRL_IRQ_END_DISABLE (1 << 18) +#define EXYNOS_CIGCTRL_IRQ_DISABLE (0 << 16) +#define EXYNOS_CIGCTRL_IRQ_ENABLE (1 << 16) +#define EXYNOS_CIGCTRL_SHADOW_DISABLE (1 << 12) +#define EXYNOS_CIGCTRL_CAM_JPEG (1 << 8) +#define EXYNOS_CIGCTRL_SELCAM_MIPI_B (0 << 7) +#define EXYNOS_CIGCTRL_SELCAM_MIPI_A (1 << 7) +#define EXYNOS_CIGCTRL_SELCAM_MIPI_MASK (1 << 7) +#define EXYNOS_CIGCTRL_SELWB_CAMIF_CAMERA (0 << 6) +#define EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK (1 << 6) +#define EXYNOS_CIGCTRL_SELWRITEBACK_MASK (1 << 10) +#define EXYNOS_CIGCTRL_SELWRITEBACK_A (1 << 10) +#define EXYNOS_CIGCTRL_SELWRITEBACK_B (0 << 10) +#define EXYNOS_CIGCTRL_SELWB_CAMIF_MASK (1 << 6) +#define EXYNOS_CIGCTRL_CSC_ITU601 (0 << 5) +#define EXYNOS_CIGCTRL_CSC_ITU709 (1 << 5) +#define EXYNOS_CIGCTRL_CSC_MASK (1 << 5) +#define EXYNOS_CIGCTRL_INVPOLHSYNC (1 << 4) +#define EXYNOS_CIGCTRL_SELCAM_FIMC_ITU (0 << 3) +#define EXYNOS_CIGCTRL_SELCAM_FIMC_MIPI (1 << 3) +#define EXYNOS_CIGCTRL_SELCAM_FIMC_MASK (1 << 3) +#define EXYNOS_CIGCTRL_PROGRESSIVE (0 << 0) +#define EXYNOS_CIGCTRL_INTERLACE (1 << 0) + +/* Window offset2 register */ +#define EXYNOS_CIWDOFST_WINHOROFST2_MASK (0xfff << 16) +#define EXYNOS_CIWDOFST_WINVEROFST2_MASK (0xfff << 16) + +/* Target format register */ +#define EXYNOS_CITRGFMT_INROT90_CLOCKWISE (1 << 31) +#define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420 (0 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422 (1 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE (2 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_RGB (3 << 29) +#define EXYNOS_CITRGFMT_OUTFORMAT_MASK (3 << 29) +#define EXYNOS_CITRGFMT_FLIP_SHIFT (14) +#define EXYNOS_CITRGFMT_FLIP_NORMAL (0 << 14) +#define EXYNOS_CITRGFMT_FLIP_X_MIRROR (1 << 14) +#define EXYNOS_CITRGFMT_FLIP_Y_MIRROR (2 << 14) +#define EXYNOS_CITRGFMT_FLIP_180 (3 << 14) +#define EXYNOS_CITRGFMT_FLIP_MASK (3 << 14) +#define EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE (1 << 13) +#define EXYNOS_CITRGFMT_TARGETV_MASK (0x1fff << 0) +#define EXYNOS_CITRGFMT_TARGETH_MASK (0x1fff << 16) + +/* Output DMA control register */ +#define EXYNOS_CIOCTRL_WEAVE_OUT (1 << 31) +#define EXYNOS_CIOCTRL_WEAVE_MASK (1 << 31) +#define EXYNOS_CIOCTRL_LASTENDEN (1 << 30) +#define EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR (0 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB (1 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_MSB_CRCB (2 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_MSB_CBCR (3 << 24) +#define EXYNOS_CIOCTRL_ORDER2P_SHIFT (24) +#define EXYNOS_CIOCTRL_ORDER2P_MASK (3 << 24) +#define EXYNOS_CIOCTRL_YCBCR_3PLANE (0 << 3) +#define EXYNOS_CIOCTRL_YCBCR_2PLANE (1 << 3) +#define EXYNOS_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) +#define EXYNOS_CIOCTRL_LASTIRQ_ENABLE (1 << 2) +#define EXYNOS_CIOCTRL_ALPHA_OUT (0xff << 4) +#define EXYNOS_CIOCTRL_ORDER422_YCBYCR (0 << 0) +#define EXYNOS_CIOCTRL_ORDER422_YCRYCB (1 << 0) +#define EXYNOS_CIOCTRL_ORDER422_CBYCRY (2 << 0) +#define EXYNOS_CIOCTRL_ORDER422_CRYCBY (3 << 0) +#define EXYNOS_CIOCTRL_ORDER422_MASK (3 << 0) + +/* Main scaler control register */ +#define EXYNOS_CISCCTRL_SCALERBYPASS (1 << 31) +#define EXYNOS_CISCCTRL_SCALEUP_H (1 << 30) +#define EXYNOS_CISCCTRL_SCALEUP_V (1 << 29) +#define EXYNOS_CISCCTRL_CSCR2Y_NARROW (0 << 28) +#define EXYNOS_CISCCTRL_CSCR2Y_WIDE (1 << 28) +#define EXYNOS_CISCCTRL_CSCY2R_NARROW (0 << 27) +#define EXYNOS_CISCCTRL_CSCY2R_WIDE (1 << 27) +#define EXYNOS_CISCCTRL_LCDPATHEN_FIFO (1 << 26) +#define EXYNOS_CISCCTRL_PROGRESSIVE (0 << 25) +#define EXYNOS_CISCCTRL_INTERLACE (1 << 25) +#define EXYNOS_CISCCTRL_SCAN_MASK (1 << 25) +#define EXYNOS_CISCCTRL_SCALERSTART (1 << 15) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) +#define EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK (3 << 13) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) +#define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK (3 << 11) +#define EXYNOS_CISCCTRL_EXTRGB_NORMAL (0 << 10) +#define EXYNOS_CISCCTRL_EXTRGB_EXTENSION (1 << 10) +#define EXYNOS_CISCCTRL_ONE2ONE (1 << 9) +#define EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK (0x1ff << 0) +#define EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK (0x1ff << 16) + +/* Status register */ +#define EXYNOS_CISTATUS_OVFIY (1 << 31) +#define EXYNOS_CISTATUS_OVFICB (1 << 30) +#define EXYNOS_CISTATUS_OVFICR (1 << 29) +#define EXYNOS_CISTATUS_VSYNC (1 << 28) +#define EXYNOS_CISTATUS_SCALERSTART (1 << 26) +#define EXYNOS_CISTATUS_WINOFSTEN (1 << 25) +#define EXYNOS_CISTATUS_IMGCPTEN (1 << 22) +#define EXYNOS_CISTATUS_IMGCPTENSC (1 << 21) +#define EXYNOS_CISTATUS_VSYNC_A (1 << 20) +#define EXYNOS_CISTATUS_VSYNC_B (1 << 19) +#define EXYNOS_CISTATUS_OVRLB (1 << 18) +#define EXYNOS_CISTATUS_FRAMEEND (1 << 17) +#define EXYNOS_CISTATUS_LASTCAPTUREEND (1 << 16) +#define EXYNOS_CISTATUS_VVALID_A (1 << 15) +#define EXYNOS_CISTATUS_VVALID_B (1 << 14) + +/* Image capture enable register */ +#define EXYNOS_CIIMGCPT_IMGCPTEN (1 << 31) +#define EXYNOS_CIIMGCPT_IMGCPTEN_SC (1 << 30) +#define EXYNOS_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) +#define EXYNOS_CIIMGCPT_CPT_FRMOD_EN (0 << 18) +#define EXYNOS_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) + +/* Image effects register */ +#define EXYNOS_CIIMGEFF_IE_DISABLE (0 << 30) +#define EXYNOS_CIIMGEFF_IE_ENABLE (1 << 30) +#define EXYNOS_CIIMGEFF_IE_SC_BEFORE (0 << 29) +#define EXYNOS_CIIMGEFF_IE_SC_AFTER (1 << 29) +#define EXYNOS_CIIMGEFF_FIN_BYPASS (0 << 26) +#define EXYNOS_CIIMGEFF_FIN_ARBITRARY (1 << 26) +#define EXYNOS_CIIMGEFF_FIN_NEGATIVE (2 << 26) +#define EXYNOS_CIIMGEFF_FIN_ARTFREEZE (3 << 26) +#define EXYNOS_CIIMGEFF_FIN_EMBOSSING (4 << 26) +#define EXYNOS_CIIMGEFF_FIN_SILHOUETTE (5 << 26) +#define EXYNOS_CIIMGEFF_FIN_MASK (7 << 26) +#define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) + +/* Real input DMA size register */ +#define EXYNOS_CIREAL_ISIZE_AUTOLOAD_ENABLE (1 << 31) +#define EXYNOS_CIREAL_ISIZE_ADDR_CH_DISABLE (1 << 30) +#define EXYNOS_CIREAL_ISIZE_HEIGHT_MASK (0x3FFF << 16) +#define EXYNOS_CIREAL_ISIZE_WIDTH_MASK (0x3FFF << 0) + +/* Input DMA control register */ +#define EXYNOS_MSCTRL_FIELD_MASK (1 << 31) +#define EXYNOS_MSCTRL_FIELD_WEAVE (1 << 31) +#define EXYNOS_MSCTRL_FIELD_NORMAL (0 << 31) +#define EXYNOS_MSCTRL_BURST_CNT (24) +#define EXYNOS_MSCTRL_BURST_CNT_MASK (0xf << 24) +#define EXYNOS_MSCTRL_ORDER2P_LSB_CBCR (0 << 16) +#define EXYNOS_MSCTRL_ORDER2P_LSB_CRCB (1 << 16) +#define EXYNOS_MSCTRL_ORDER2P_MSB_CRCB (2 << 16) +#define EXYNOS_MSCTRL_ORDER2P_MSB_CBCR (3 << 16) +#define EXYNOS_MSCTRL_ORDER2P_SHIFT (16) +#define EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK (0x3 << 16) +#define EXYNOS_MSCTRL_C_INT_IN_3PLANE (0 << 15) +#define EXYNOS_MSCTRL_C_INT_IN_2PLANE (1 << 15) +#define EXYNOS_MSCTRL_FLIP_SHIFT (13) +#define EXYNOS_MSCTRL_FLIP_NORMAL (0 << 13) +#define EXYNOS_MSCTRL_FLIP_X_MIRROR (1 << 13) +#define EXYNOS_MSCTRL_FLIP_Y_MIRROR (2 << 13) +#define EXYNOS_MSCTRL_FLIP_180 (3 << 13) +#define EXYNOS_MSCTRL_FLIP_MASK (3 << 13) +#define EXYNOS_MSCTRL_ORDER422_CRYCBY (0 << 4) +#define EXYNOS_MSCTRL_ORDER422_YCRYCB (1 << 4) +#define EXYNOS_MSCTRL_ORDER422_CBYCRY (2 << 4) +#define EXYNOS_MSCTRL_ORDER422_YCBYCR (3 << 4) +#define EXYNOS_MSCTRL_INPUT_EXTCAM (0 << 3) +#define EXYNOS_MSCTRL_INPUT_MEMORY (1 << 3) +#define EXYNOS_MSCTRL_INPUT_MASK (1 << 3) +#define EXYNOS_MSCTRL_INFORMAT_YCBCR420 (0 << 1) +#define EXYNOS_MSCTRL_INFORMAT_YCBCR422 (1 << 1) +#define EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE (2 << 1) +#define EXYNOS_MSCTRL_INFORMAT_RGB (3 << 1) +#define EXYNOS_MSCTRL_ENVID (1 << 0) + +/* DMA parameter register */ +#define EXYNOS_CIDMAPARAM_R_MODE_LINEAR (0 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_CONFTILE (1 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_16X16 (2 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_64X32 (3 << 29) +#define EXYNOS_CIDMAPARAM_R_MODE_MASK (3 << 29) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_64 (0 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_128 (1 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_256 (2 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_512 (3 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_1024 (4 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_2048 (5 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_4096 (6 << 24) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_1 (0 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_2 (1 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_4 (2 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_8 (3 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_16 (4 << 20) +#define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_32 (5 << 20) +#define EXYNOS_CIDMAPARAM_W_MODE_LINEAR (0 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_CONFTILE (1 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_16X16 (2 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_64X32 (3 << 13) +#define EXYNOS_CIDMAPARAM_W_MODE_MASK (3 << 13) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_64 (0 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_128 (1 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_256 (2 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_512 (3 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_1024 (4 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_2048 (5 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_4096 (6 << 8) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_1 (0 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_2 (1 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_4 (2 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_8 (3 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_16 (4 << 4) +#define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_32 (5 << 4) + +/* Gathering Extension register */ +#define EXYNOS_CIEXTEN_TARGETH_EXT_MASK (1 << 26) +#define EXYNOS_CIEXTEN_TARGETV_EXT_MASK (1 << 24) +#define EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK (0x3F << 10) +#define EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK (0x3F) +#define EXYNOS_CIEXTEN_YUV444_OUT (1 << 22) + +/* FIMC Clock Source Select register */ +#define EXYNOS_CLKSRC_HCLK (0 << 1) +#define EXYNOS_CLKSRC_HCLK_MASK (1 << 1) +#define EXYNOS_CLKSRC_SCLK (1 << 1) + +/* SYSREG for FIMC writeback */ +#define SYSREG_CAMERA_BLK (S3C_VA_SYS + 0x0218) +#define SYSREG_ISP_BLK (S3C_VA_SYS + 0x020c) +#define SYSREG_FIMD0WB_DEST_MASK (0x3 << 23) +#define SYSREG_FIMD0WB_DEST_SHIFT 23 + +#endif /* SAMSUNG_REGS_FIMC_H */ diff --git a/drivers/gpu/drm/exynos/regs-gsc.h b/drivers/gpu/drm/exynos/regs-gsc.h new file mode 100644 index 0000000..8ec160b --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-gsc.h @@ -0,0 +1,295 @@ +/* linux/drivers/gpu/drm/exynos/regs-gsc.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Register definition file for Samsung G-Scaler driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef REGS_GSC_H_ +#define REGS_GSC_H_ + +/* SYSCON. GSCBLK_CFG */ +#include <plat/map-base.h> +#include <plat/cpu.h> +#define SYSREG_DISP1BLK_CFG (S3C_VA_SYS + 0x0214) +#define FIFORST_DISP1 (1 << 23) +#define GSC_OUT_MIXER0 (1 << 7) +#define GSC_OUT_MIXER0_GSC3 (3 << 5) +#define SYSREG_GSCBLK_CFG0 (S3C_VA_SYS + 0x0220) +#define GSC_OUT_DST_FIMD_SEL(x) (1 << (8 + 2 * (x))) +#define GSC_OUT_DST_MXR_SEL(x) (2 << (8 + 2 * (x))) +#define GSC_PXLASYNC_RST(x) (1 << (x)) +#define PXLASYNC_LO_MASK_CAMIF_TOP (1 << 20) +#define SYSREG_GSCBLK_CFG1 (S3C_VA_SYS + 0x0224) +#define GSC_BLK_DISP1WB_DEST(x) (x << 10) +#define GSC_BLK_SW_RESET_WB_DEST(x) (1 << (18 + x)) +#define GSC_BLK_PXLASYNC_LO_MASK_WB(x) (0 << (14 + x)) +#define GSC_BLK_GSCL_WB_IN_SRC_SEL(x) (1 << (2 * x)) +#define SYSREG_GSCBLK_CFG2 (S3C_VA_SYS + 0x2000) +#define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x)) + +/* G-Scaler enable */ +#define GSC_ENABLE 0x00 +#define GSC_ENABLE_PP_UPDATE_TIME_MASK (1 << 9) +#define GSC_ENABLE_PP_UPDATE_TIME_CURR (0 << 9) +#define GSC_ENABLE_PP_UPDATE_TIME_EOPAS (1 << 9) +#define GSC_ENABLE_CLK_GATE_MODE_MASK (1 << 8) +#define GSC_ENABLE_CLK_GATE_MODE_FREE (1 << 8) +#define GSC_ENABLE_IPC_MODE_MASK (1 << 7) +#define GSC_ENABLE_NORM_MODE (0 << 7) +#define GSC_ENABLE_IPC_MODE (1 << 7) +#define GSC_ENABLE_PP_UPDATE_MODE_MASK (1 << 6) +#define GSC_ENABLE_PP_UPDATE_FIRE_MODE (1 << 6) +#define GSC_ENABLE_IN_PP_UPDATE (1 << 5) +#define GSC_ENABLE_ON_CLEAR_MASK (1 << 4) +#define GSC_ENABLE_ON_CLEAR_ONESHOT (1 << 4) +#define GSC_ENABLE_QOS_ENABLE (1 << 3) +#define GSC_ENABLE_OP_STATUS (1 << 2) +#define GSC_ENABLE_SFR_UPDATE (1 << 1) +#define GSC_ENABLE_ON (1 << 0) + +/* G-Scaler S/W reset */ +#define GSC_SW_RESET 0x04 +#define GSC_SW_RESET_SRESET (1 << 0) + +/* G-Scaler IRQ */ +#define GSC_IRQ 0x08 +#define GSC_IRQ_STATUS_OR_IRQ (1 << 17) +#define GSC_IRQ_STATUS_OR_FRM_DONE (1 << 16) +#define GSC_IRQ_OR_MASK (1 << 2) +#define GSC_IRQ_FRMDONE_MASK (1 << 1) +#define GSC_IRQ_ENABLE (1 << 0) + +/* G-Scaler input control */ +#define GSC_IN_CON 0x10 +#define GSC_IN_CHROM_STRIDE_SEL_MASK (1 << 20) +#define GSC_IN_CHROM_STRIDE_SEPAR (1 << 20) +#define GSC_IN_RB_SWAP_MASK (1 << 19) +#define GSC_IN_RB_SWAP (1 << 19) +#define GSC_IN_ROT_MASK (7 << 16) +#define GSC_IN_ROT_270 (7 << 16) +#define GSC_IN_ROT_90_YFLIP (6 << 16) +#define GSC_IN_ROT_90_XFLIP (5 << 16) +#define GSC_IN_ROT_90 (4 << 16) +#define GSC_IN_ROT_180 (3 << 16) +#define GSC_IN_ROT_YFLIP (2 << 16) +#define GSC_IN_ROT_XFLIP (1 << 16) +#define GSC_IN_RGB_TYPE_MASK (3 << 14) +#define GSC_IN_RGB_HD_WIDE (3 << 14) +#define GSC_IN_RGB_HD_NARROW (2 << 14) +#define GSC_IN_RGB_SD_WIDE (1 << 14) +#define GSC_IN_RGB_SD_NARROW (0 << 14) +#define GSC_IN_YUV422_1P_ORDER_MASK (1 << 13) +#define GSC_IN_YUV422_1P_ORDER_LSB_Y (0 << 13) +#define GSC_IN_YUV422_1P_OEDER_LSB_C (1 << 13) +#define GSC_IN_CHROMA_ORDER_MASK (1 << 12) +#define GSC_IN_CHROMA_ORDER_CBCR (0 << 12) +#define GSC_IN_CHROMA_ORDER_CRCB (1 << 12) +#define GSC_IN_FORMAT_MASK (7 << 8) +#define GSC_IN_XRGB8888 (0 << 8) +#define GSC_IN_RGB565 (1 << 8) +#define GSC_IN_YUV420_2P (2 << 8) +#define GSC_IN_YUV420_3P (3 << 8) +#define GSC_IN_YUV422_1P (4 << 8) +#define GSC_IN_YUV422_2P (5 << 8) +#define GSC_IN_YUV422_3P (6 << 8) +#define GSC_IN_TILE_TYPE_MASK (1 << 4) +#define GSC_IN_TILE_C_16x8 (0 << 4) +#define GSC_IN_TILE_C_16x16 (1 << 4) +#define GSC_IN_TILE_MODE (1 << 3) +#define GSC_IN_LOCAL_SEL_MASK (3 << 1) +#define GSC_IN_LOCAL_CAM3 (3 << 1) +#define GSC_IN_LOCAL_FIMD_WB (2 << 1) +#define GSC_IN_LOCAL_CAM1 (1 << 1) +#define GSC_IN_LOCAL_CAM0 (0 << 1) +#define GSC_IN_PATH_MASK (1 << 0) +#define GSC_IN_PATH_LOCAL (1 << 0) +#define GSC_IN_PATH_MEMORY (0 << 0) + +/* G-Scaler source image size */ +#define GSC_SRCIMG_SIZE 0x14 +#define GSC_SRCIMG_HEIGHT_MASK (0x1fff << 16) +#define GSC_SRCIMG_HEIGHT(x) ((x) << 16) +#define GSC_SRCIMG_WIDTH_MASK (0x3fff << 0) +#define GSC_SRCIMG_WIDTH(x) ((x) << 0) + +/* G-Scaler source image offset */ +#define GSC_SRCIMG_OFFSET 0x18 +#define GSC_SRCIMG_OFFSET_Y_MASK (0x1fff << 16) +#define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16) +#define GSC_SRCIMG_OFFSET_X_MASK (0x1fff << 0) +#define GSC_SRCIMG_OFFSET_X(x) ((x) << 0) + +/* G-Scaler cropped source image size */ +#define GSC_CROPPED_SIZE 0x1C +#define GSC_CROPPED_HEIGHT_MASK (0x1fff << 16) +#define GSC_CROPPED_HEIGHT(x) ((x) << 16) +#define GSC_CROPPED_WIDTH_MASK (0x1fff << 0) +#define GSC_CROPPED_WIDTH(x) ((x) << 0) + +/* G-Scaler output control */ +#define GSC_OUT_CON 0x20 +#define GSC_OUT_GLOBAL_ALPHA_MASK (0xff << 24) +#define GSC_OUT_GLOBAL_ALPHA(x) ((x) << 24) +#define GSC_OUT_CHROM_STRIDE_SEL_MASK (1 << 13) +#define GSC_OUT_CHROM_STRIDE_SEPAR (1 << 13) +#define GSC_OUT_RB_SWAP_MASK (1 << 12) +#define GSC_OUT_RB_SWAP (1 << 12) +#define GSC_OUT_RGB_TYPE_MASK (3 << 10) +#define GSC_OUT_RGB_HD_NARROW (3 << 10) +#define GSC_OUT_RGB_HD_WIDE (2 << 10) +#define GSC_OUT_RGB_SD_NARROW (1 << 10) +#define GSC_OUT_RGB_SD_WIDE (0 << 10) +#define GSC_OUT_YUV422_1P_ORDER_MASK (1 << 9) +#define GSC_OUT_YUV422_1P_ORDER_LSB_Y (0 << 9) +#define GSC_OUT_YUV422_1P_OEDER_LSB_C (1 << 9) +#define GSC_OUT_CHROMA_ORDER_MASK (1 << 8) +#define GSC_OUT_CHROMA_ORDER_CBCR (0 << 8) +#define GSC_OUT_CHROMA_ORDER_CRCB (1 << 8) +#define GSC_OUT_FORMAT_MASK (7 << 4) +#define GSC_OUT_XRGB8888 (0 << 4) +#define GSC_OUT_RGB565 (1 << 4) +#define GSC_OUT_YUV420_2P (2 << 4) +#define GSC_OUT_YUV420_3P (3 << 4) +#define GSC_OUT_YUV422_1P (4 << 4) +#define GSC_OUT_YUV422_2P (5 << 4) +#define GSC_OUT_YUV444 (7 << 4) +#define GSC_OUT_TILE_TYPE_MASK (1 << 2) +#define GSC_OUT_TILE_C_16x8 (0 << 2) +#define GSC_OUT_TILE_C_16x16 (1 << 2) +#define GSC_OUT_TILE_MODE (1 << 1) +#define GSC_OUT_PATH_MASK (1 << 0) +#define GSC_OUT_PATH_LOCAL (1 << 0) +#define GSC_OUT_PATH_MEMORY (0 << 0) + +/* G-Scaler scaled destination image size */ +#define GSC_SCALED_SIZE 0x24 +#define GSC_SCALED_HEIGHT_MASK (0x1fff << 16) +#define GSC_SCALED_HEIGHT(x) ((x) << 16) +#define GSC_SCALED_WIDTH_MASK (0x1fff << 0) +#define GSC_SCALED_WIDTH(x) ((x) << 0) + +/* G-Scaler pre scale ratio */ +#define GSC_PRE_SCALE_RATIO 0x28 +#define GSC_PRESC_SHFACTOR_MASK (7 << 28) +#define GSC_PRESC_SHFACTOR(x) ((x) << 28) +#define GSC_PRESC_V_RATIO_MASK (7 << 16) +#define GSC_PRESC_V_RATIO(x) ((x) << 16) +#define GSC_PRESC_H_RATIO_MASK (7 << 0) +#define GSC_PRESC_H_RATIO(x) ((x) << 0) + +/* G-Scaler main scale horizontal ratio */ +#define GSC_MAIN_H_RATIO 0x2C +#define GSC_MAIN_H_RATIO_MASK (0xfffff << 0) +#define GSC_MAIN_H_RATIO_VALUE(x) ((x) << 0) + +/* G-Scaler main scale vertical ratio */ +#define GSC_MAIN_V_RATIO 0x30 +#define GSC_MAIN_V_RATIO_MASK (0xfffff << 0) +#define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0) + +/* G-Scaler input chrominance stride */ +#define GSC_IN_CHROM_STRIDE 0x3C +#define GSC_IN_CHROM_STRIDE_MASK (0x3fff << 0) +#define GSC_IN_CHROM_STRIDE_VALUE(x) ((x) << 0) + +/* G-Scaler destination image size */ +#define GSC_DSTIMG_SIZE 0x40 +#define GSC_DSTIMG_HEIGHT_MASK (0x1fff << 16) +#define GSC_DSTIMG_HEIGHT(x) ((x) << 16) +#define GSC_DSTIMG_WIDTH_MASK (0x1fff << 0) +#define GSC_DSTIMG_WIDTH(x) ((x) << 0) + +/* G-Scaler destination image offset */ +#define GSC_DSTIMG_OFFSET 0x44 +#define GSC_DSTIMG_OFFSET_Y_MASK (0x1fff << 16) +#define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16) +#define GSC_DSTIMG_OFFSET_X_MASK (0x1fff << 0) +#define GSC_DSTIMG_OFFSET_X(x) ((x) << 0) + +/* G-Scaler output chrominance stride */ +#define GSC_OUT_CHROM_STRIDE 0x48 +#define GSC_OUT_CHROM_STRIDE_MASK (0x3fff << 0) +#define GSC_OUT_CHROM_STRIDE_VALUE(x) ((x) << 0) + +/* G-Scaler input y address mask */ +#define GSC_IN_BASE_ADDR_Y_MASK 0x4C +/* G-Scaler input y base address */ +#define GSC_IN_BASE_ADDR_Y(n) (0x50 + (n) * 0x4) +/* G-Scaler input y base current address */ +#define GSC_IN_BASE_ADDR_Y_CUR(n) (0x60 + (n) * 0x4) + +/* G-Scaler input cb address mask */ +#define GSC_IN_BASE_ADDR_CB_MASK 0x7C +/* G-Scaler input cb base address */ +#define GSC_IN_BASE_ADDR_CB(n) (0x80 + (n) * 0x4) +/* G-Scaler input cb base current address */ +#define GSC_IN_BASE_ADDR_CB_CUR(n) (0x90 + (n) * 0x4) + +/* G-Scaler input cr address mask */ +#define GSC_IN_BASE_ADDR_CR_MASK 0xAC +/* G-Scaler input cr base address */ +#define GSC_IN_BASE_ADDR_CR(n) (0xB0 + (n) * 0x4) +/* G-Scaler input cr base current address */ +#define GSC_IN_BASE_ADDR_CR_CUR(n) (0xC0 + (n) * 0x4) + +/* G-Scaler input address mask */ +#define GSC_IN_CURR_ADDR_INDEX (0xf << 24) +#define GSC_IN_CURR_GET_INDEX(x) ((x) >> 24) +#define GSC_IN_BASE_ADDR_PINGPONG(x) ((x) << 16) +#define GSC_IN_BASE_ADDR_MASK (0xff << 0) + +/* G-Scaler output y address mask */ +#define GSC_OUT_BASE_ADDR_Y_MASK 0x10C +/* G-Scaler output y base address */ +#define GSC_OUT_BASE_ADDR_Y(n) (0x110 + (n) * 0x4) + +/* G-Scaler output cb address mask */ +#define GSC_OUT_BASE_ADDR_CB_MASK 0x15C +/* G-Scaler output cb base address */ +#define GSC_OUT_BASE_ADDR_CB(n) (0x160 + (n) * 0x4) + +/* G-Scaler output cr address mask */ +#define GSC_OUT_BASE_ADDR_CR_MASK 0x1AC +/* G-Scaler output cr base address */ +#define GSC_OUT_BASE_ADDR_CR(n) (0x1B0 + (n) * 0x4) + +/* G-Scaler output address mask */ +#define GSC_OUT_CURR_ADDR_INDEX (0xf << 24) +#define GSC_OUT_CURR_GET_INDEX(x) ((x) >> 24) +#define GSC_OUT_BASE_ADDR_PINGPONG(x) ((x) << 16) +#define GSC_OUT_BASE_ADDR_MASK (0xffff << 0) + +/* G-Scaler horizontal scaling filter */ +#define GSC_HCOEF(n, s, x) (0x300 + (n) * 0x4 + (s) * 0x30 + (x) * 0x300) + +/* G-Scaler vertical scaling filter */ +#define GSC_VCOEF(n, s, x) (0x200 + (n) * 0x4 + (s) * 0x30 + (x) * 0x300) + +/* G-Scaler BUS control */ +#define GSC_BUSCON 0xA78 +#define GSC_BUSCON_INT_TIME_MASK (1 << 8) +#define GSC_BUSCON_INT_DATA_TRANS (0 << 8) +#define GSC_BUSCON_INT_AXI_RESPONSE (1 << 8) +#define GSC_BUSCON_AWCACHE(x) ((x) << 4) +#define GSC_BUSCON_ARCACHE(x) ((x) << 0) + +/* G-Scaler V position */ +#define GSC_VPOSITION 0xA7C +#define GSC_VPOS_F(x) ((x) << 0) + + +/* G-Scaler clock initial count */ +#define GSC_CLK_INIT_COUNT 0xC00 +#define GSC_CLK_GATE_MODE_INIT_CNT(x) ((x) << 0) + +/* G-Scaler clock snoop count */ +#define GSC_CLK_SNOOP_COUNT 0xC04 +#define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0) + +#endif /* REGS_GSC_H_ */ diff --git a/drivers/gpu/drm/exynos_tmp/Kconfig b/drivers/gpu/drm/exynos_tmp/Kconfig deleted file mode 100644 index 4466490..0000000 --- a/drivers/gpu/drm/exynos_tmp/Kconfig +++ /dev/null @@ -1,46 +0,0 @@ -config DRM_EXYNOS - tristate "DRM Support for Samsung SoC EXYNOS Series" - depends on DRM && PLAT_SAMSUNG - select DRM_KMS_HELPER - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE - -config DRM_EXYNOS_MEMSIZE - int "Memory size in kbytes for DRM" - depends on DRM_EXYNOS - default "32768" - -config DRM_EXYNOS_DMABUF - bool "GEM DMABUF Support" - depends on DRM_EXYNOS - -config DRM_EXYNOS_FIMD - bool "Samsung DRM FIMD" - depends on DRM_EXYNOS - -config DRM_EXYNOS_FIMD_WB - bool "Samsung DRM FIMD WB" - depends on DRM_EXYNOS_FIMD - help - This option enables writeback operations in FIMD for DRM. - It can support fimd streams data to fimc destinations ram. - writeback operations support final blended stream. - when enable this options. - -config DRM_EXYNOS_HDMI - bool "Samsung DRM HDMI" - depends on DRM_EXYNOS - -config DRM_EXYNOS_VIDI - bool "Samsung DRM Virtual Display" - depends on DRM_EXYNOS - -config DRM_EXYNOS_G2D - bool "Samsung DRM G2D" - depends on DRM_EXYNOS - -config DRM_EXYNOS_ROTATOR - bool "Samsung DRM Rotator" - depends on DRM_EXYNOS diff --git a/drivers/gpu/drm/exynos_tmp/Makefile b/drivers/gpu/drm/exynos_tmp/Makefile deleted file mode 100644 index f8a88f3..0000000 --- a/drivers/gpu/drm/exynos_tmp/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# -# Makefile for the drm device driver. This driver provides support for the -# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. - -ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos \ - -Idrivers/media/video/samsung/ump/include -exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ - exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \ - exynos_drm_buf.o exynos_drm_ump.o exynos_drm_gem.o \ - exynos_drm_core.o exynos_drm_plane.o exynos_drm_iommu.o - -exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o -exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o -exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \ - exynos_hdmiphy.o exynos_drm_hdmi.o -exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o -exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o -exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o - -obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos_tmp/exynos_ddc.c b/drivers/gpu/drm/exynos_tmp/exynos_ddc.c deleted file mode 100644 index 7e1051d..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_ddc.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Seung-Woo Kim <sw0312.kim@samsung.com> - * Inki Dae <inki.dae@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include "drmP.h" - -#include <linux/kernel.h> -#include <linux/i2c.h> -#include <linux/module.h> - - -#include "exynos_drm_drv.h" -#include "exynos_hdmi.h" - -static int s5p_ddc_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) -{ - hdmi_attach_ddc_client(client); - - dev_info(&client->adapter->dev, "attached s5p_ddc " - "into i2c adapter successfully\n"); - - return 0; -} - -static int s5p_ddc_remove(struct i2c_client *client) -{ - dev_info(&client->adapter->dev, "detached s5p_ddc " - "from i2c adapter successfully\n"); - - return 0; -} - -static struct i2c_device_id ddc_idtable[] = { - {"s5p_ddc", 0}, - { }, -}; - -struct i2c_driver ddc_driver = { - .driver = { - .name = "s5p_ddc", - .owner = THIS_MODULE, - }, - .id_table = ddc_idtable, - .probe = s5p_ddc_probe, - .remove = __devexit_p(s5p_ddc_remove), - .command = NULL, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_buf.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_buf.c deleted file mode 100644 index f7bb7be..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_buf.c +++ /dev/null @@ -1,241 +0,0 @@ -/* exynos_drm_buf.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm.h" -#include "exynos_drm.h" - -#include <linux/cma.h> -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" -#include "exynos_drm_buf.h" - -static int lowlevel_buffer_allocate(struct drm_device *dev, - unsigned int flags, struct exynos_drm_gem_buf *buf) -{ - dma_addr_t start_addr; - unsigned int npages, i = 0; - struct scatterlist *sgl; - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (IS_NONCONTIG_BUFFER(flags)) { - DRM_DEBUG_KMS("not support allocation type.\n"); - return -EINVAL; - } - - if (buf->dma_addr) { - DRM_DEBUG_KMS("already allocated.\n"); - return 0; - } - - if (buf->size >= SZ_1M) { - npages = buf->size >> SECTION_SHIFT; - buf->page_size = SECTION_SIZE; - } else if (buf->size >= SZ_64K) { - npages = buf->size >> 16; - buf->page_size = SZ_64K; - } else { - npages = buf->size >> PAGE_SHIFT; - buf->page_size = PAGE_SIZE; - } - - buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); - if (!buf->sgt) { - DRM_ERROR("failed to allocate sg table.\n"); - return -ENOMEM; - } - - ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); - if (ret < 0) { - DRM_ERROR("failed to initialize sg table.\n"); - kfree(buf->sgt); - buf->sgt = NULL; - return -ENOMEM; - } - -#ifdef CONFIG_CMA - buf->dma_addr = cma_alloc(dev->dev, "drm", buf->size, - buf->page_size); - if (IS_ERR((void *)buf->dma_addr)) { - DRM_DEBUG_KMS("cma_alloc of size %ld failed\n", - buf->size); - ret = -ENOMEM; - goto err1; - } - - buf->kvaddr = phys_to_virt(buf->dma_addr); -#else - /* align it as page size(page or section) TODO */ - - buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size, - &buf->dma_addr, GFP_KERNEL); - if (!buf->kvaddr) { - DRM_ERROR("failed to allocate buffer.\n"); - ret = -ENOMEM; - goto err1; - } -#endif - buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); - if (!buf->pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; - goto err2; - } - - sgl = buf->sgt->sgl; - start_addr = buf->dma_addr; - - while (i < npages) { - buf->pages[i] = phys_to_page(start_addr); - sg_set_page(sgl, buf->pages[i], buf->page_size, 0); - sg_dma_address(sgl) = start_addr; - start_addr += buf->page_size; - sgl = sg_next(sgl); - i++; - } - - DRM_INFO("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", - (unsigned long)buf->kvaddr, - (unsigned long)buf->dma_addr, - buf->size); - - return ret; -err2: -#ifdef CONFIG_CMA - cma_free(buf->dma_addr); -#else - dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, - (dma_addr_t)buf->dma_addr); -#endif - buf->dma_addr = (dma_addr_t)NULL; -err1: - sg_free_table(buf->sgt); - kfree(buf->sgt); - buf->sgt = NULL; - - return ret; -} - -static void lowlevel_buffer_deallocate(struct drm_device *dev, - unsigned int flags, struct exynos_drm_gem_buf *buf) -{ - DRM_DEBUG_KMS("%s.\n", __FILE__); - - /* - * now buffer is being shared and it would be released - * by original owner so ignor free action. - */ - if (buf->shared || atomic_read(&buf->shared_refcount)) - return; - - /* - * release only physically continuous memory and - * non-continuous memory would be released by exynos - * gem framework. - */ - if (IS_NONCONTIG_BUFFER(flags)) { - DRM_DEBUG_KMS("not support allocation type.\n"); - return; - } - - if (!buf->dma_addr) { - DRM_DEBUG_KMS("dma_addr is invalid.\n"); - return; - } - - sg_free_table(buf->sgt); - - kfree(buf->sgt); - buf->sgt = NULL; - - kfree(buf->pages); - buf->pages = NULL; -#ifdef CONFIG_CMA - cma_free(buf->dma_addr); -#else - dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, - (dma_addr_t)buf->dma_addr); -#endif - buf->dma_addr = (dma_addr_t)NULL; -} - -struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, - unsigned int size) -{ - struct exynos_drm_gem_buf *buffer; - - DRM_DEBUG_KMS("%s.\n", __FILE__); - DRM_DEBUG_KMS("desired size = 0x%x\n", size); - - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) { - DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); - return NULL; - } - - buffer->size = size; - return buffer; -} - -void exynos_drm_fini_buf(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer) -{ - DRM_DEBUG_KMS("%s.\n", __FILE__); - - if (!buffer) { - DRM_DEBUG_KMS("buffer is null.\n"); - return; - } - - kfree(buffer); - buffer = NULL; -} - -int exynos_drm_alloc_buf(struct drm_device *dev, - struct exynos_drm_gem_buf *buf, unsigned int flags) -{ - - /* - * allocate memory region and set the memory information - * to vaddr and dma_addr of a buffer object. - */ - if (lowlevel_buffer_allocate(dev, flags, buf) < 0) - return -ENOMEM; - - return 0; -} - -void exynos_drm_free_buf(struct drm_device *dev, - unsigned int flags, struct exynos_drm_gem_buf *buffer) -{ - - lowlevel_buffer_deallocate(dev, flags, buffer); -} - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_buf.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_buf.h deleted file mode 100644 index 3388e4e..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_buf.h +++ /dev/null @@ -1,47 +0,0 @@ -/* exynos_drm_buf.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_BUF_H_ -#define _EXYNOS_DRM_BUF_H_ - -/* create and initialize buffer object. */ -struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, - unsigned int size); - -/* destroy buffer object. */ -void exynos_drm_fini_buf(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer); - -/* allocate physical memory region and setup sgt and pages. */ -int exynos_drm_alloc_buf(struct drm_device *dev, - struct exynos_drm_gem_buf *buf, - unsigned int flags); - -/* release physical memory region, sgt and pages. */ -void exynos_drm_free_buf(struct drm_device *dev, - unsigned int flags, - struct exynos_drm_gem_buf *buffer); - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_connector.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_connector.c deleted file mode 100644 index 0efb6df..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_connector.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm_crtc_helper.h" - -#include <drm/exynos_drm.h> -#include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h" - -#define MAX_EDID 256 -#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\ - drm_connector) - -struct exynos_drm_connector { - struct drm_connector drm_connector; - uint32_t encoder_id; - struct exynos_drm_manager *manager; -}; - -/* convert exynos_video_timings to drm_display_mode */ -static inline void -convert_to_display_mode(struct drm_display_mode *mode, - struct exynos_drm_panel_info *panel) -{ - struct fb_videomode *timing = &panel->timing; - DRM_DEBUG_KMS("%s\n", __FILE__); - - mode->clock = timing->pixclock / 1000; - mode->vrefresh = timing->refresh; - - mode->hdisplay = timing->xres; - mode->hsync_start = mode->hdisplay + timing->right_margin; - mode->hsync_end = mode->hsync_start + timing->hsync_len; - mode->htotal = mode->hsync_end + timing->left_margin; - - mode->vdisplay = timing->yres; - mode->vsync_start = mode->vdisplay + timing->lower_margin; - mode->vsync_end = mode->vsync_start + timing->vsync_len; - mode->vtotal = mode->vsync_end + timing->upper_margin; - mode->width_mm = panel->width_mm; - mode->height_mm = panel->height_mm; - - if (timing->vmode & FB_VMODE_INTERLACED) - mode->flags |= DRM_MODE_FLAG_INTERLACE; - - if (timing->vmode & FB_VMODE_DOUBLE) - mode->flags |= DRM_MODE_FLAG_DBLSCAN; -} - -/* convert drm_display_mode to exynos_video_timings */ -static inline void -convert_to_video_timing(struct fb_videomode *timing, - struct drm_display_mode *mode) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - memset(timing, 0, sizeof(*timing)); - - timing->pixclock = mode->clock * 1000; - timing->refresh = drm_mode_vrefresh(mode); - - timing->xres = mode->hdisplay; - timing->right_margin = mode->hsync_start - mode->hdisplay; - timing->hsync_len = mode->hsync_end - mode->hsync_start; - timing->left_margin = mode->htotal - mode->hsync_end; - - timing->yres = mode->vdisplay; - timing->lower_margin = mode->vsync_start - mode->vdisplay; - timing->vsync_len = mode->vsync_end - mode->vsync_start; - timing->upper_margin = mode->vtotal - mode->vsync_end; - - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - timing->vmode = FB_VMODE_INTERLACED; - else - timing->vmode = FB_VMODE_NONINTERLACED; - - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - timing->vmode |= FB_VMODE_DOUBLE; -} - -static int exynos_drm_connector_get_modes(struct drm_connector *connector) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_display_ops *display_ops = manager->display_ops; - unsigned int count; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!display_ops) { - DRM_DEBUG_KMS("display_ops is null.\n"); - return 0; - } - - /* - * if get_edid() exists then get_edid() callback of hdmi side - * is called to get edid data through i2c interface else - * get timing from the FIMD driver(display controller). - * - * P.S. in case of lcd panel, count is always 1 if success - * because lcd panel has only one mode. - */ - if (display_ops->get_edid) { - int ret; - void *edid; - - edid = kzalloc(MAX_EDID, GFP_KERNEL); - if (!edid) { - DRM_ERROR("failed to allocate edid\n"); - return 0; - } - - ret = display_ops->get_edid(manager->dev, connector, - edid, MAX_EDID); - if (ret < 0) { - DRM_ERROR("failed to get edid data.\n"); - kfree(edid); - edid = NULL; - return 0; - } - - drm_mode_connector_update_edid_property(connector, edid); - count = drm_add_edid_modes(connector, edid); - - kfree(connector->display_info.raw_edid); - connector->display_info.raw_edid = edid; - } else { - struct drm_display_mode *mode = drm_mode_create(connector->dev); - struct exynos_drm_panel_info *panel; - - if (display_ops->get_panel) - panel = display_ops->get_panel(manager->dev); - else { - drm_mode_destroy(connector->dev, mode); - return 0; - } - - convert_to_display_mode(mode, panel); - connector->display_info.width_mm = mode->width_mm; - connector->display_info.height_mm = mode->height_mm; - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - - count = 1; - } - - return count; -} - -static int exynos_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_display_ops *display_ops = manager->display_ops; - struct fb_videomode timing; - int ret = MODE_BAD; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - convert_to_video_timing(&timing, mode); - - if (display_ops && display_ops->check_timing) - if (!display_ops->check_timing(manager->dev, (void *)&timing)) - ret = MODE_OK; - - return ret; -} - -struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct drm_mode_object *obj; - struct drm_encoder *encoder; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - obj = drm_mode_object_find(dev, exynos_connector->encoder_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) { - DRM_DEBUG_KMS("Unknown ENCODER ID %d\n", - exynos_connector->encoder_id); - return NULL; - } - - encoder = obj_to_encoder(obj); - - return encoder; -} - -static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { - .get_modes = exynos_drm_connector_get_modes, - .mode_valid = exynos_drm_connector_mode_valid, - .best_encoder = exynos_drm_best_encoder, -}; - -static int exynos_drm_connector_fill_modes(struct drm_connector *connector, - unsigned int max_width, unsigned int max_height) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_manager_ops *ops = manager->ops; - unsigned int width, height; - - width = max_width; - height = max_height; - - /* - * if specific driver want to find desired_mode using maxmum - * resolution then get max width and height from that driver. - */ - if (ops && ops->get_max_resol) - ops->get_max_resol(manager->dev, &width, &height); - - return drm_helper_probe_single_connector_modes(connector, width, - height); -} - -/* get detection status of display device. */ -static enum drm_connector_status -exynos_drm_connector_detect(struct drm_connector *connector, bool force) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_display_ops *display_ops = - manager->display_ops; - enum drm_connector_status status = connector_status_disconnected; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (display_ops && display_ops->is_connected) { - if (display_ops->is_connected(manager->dev)) - status = connector_status_connected; - else - status = connector_status_disconnected; - } - - return status; -} - -static void exynos_drm_connector_destroy(struct drm_connector *connector) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(exynos_connector); -} - -static struct drm_connector_funcs exynos_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .fill_modes = exynos_drm_connector_fill_modes, - .detect = exynos_drm_connector_detect, - .destroy = exynos_drm_connector_destroy, -}; - -struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, - struct drm_encoder *encoder) -{ - struct exynos_drm_connector *exynos_connector; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct drm_connector *connector; - int type; - int err; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL); - if (!exynos_connector) { - DRM_ERROR("failed to allocate connector\n"); - return NULL; - } - - connector = &exynos_connector->drm_connector; - - switch (manager->display_ops->type) { - case EXYNOS_DISPLAY_TYPE_HDMI: - type = DRM_MODE_CONNECTOR_HDMIA; - connector->interlace_allowed = true; - connector->polled = DRM_CONNECTOR_POLL_HPD; - break; - case EXYNOS_DISPLAY_TYPE_LCD: - type = DRM_MODE_CONNECTOR_LVDS; - break; - case EXYNOS_DISPLAY_TYPE_VIDI: - type = DRM_MODE_CONNECTOR_VIRTUAL; - connector->polled = DRM_CONNECTOR_POLL_HPD; - break; - default: - type = DRM_MODE_CONNECTOR_Unknown; - break; - } - - drm_connector_init(dev, connector, &exynos_connector_funcs, type); - drm_connector_helper_add(connector, &exynos_connector_helper_funcs); - - err = drm_sysfs_connector_add(connector); - if (err) - goto err_connector; - - exynos_connector->encoder_id = encoder->base.id; - exynos_connector->manager = manager; - connector->encoder = encoder; - - err = drm_mode_connector_attach_encoder(connector, encoder); - if (err) { - DRM_ERROR("failed to attach a connector to a encoder\n"); - goto err_sysfs; - } - - DRM_DEBUG_KMS("connector has been created\n"); - - return connector; - -err_sysfs: - drm_sysfs_connector_remove(connector); -err_connector: - drm_connector_cleanup(connector); - kfree(exynos_connector); - return NULL; -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_core.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_core.c deleted file mode 100644 index eaf630d..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_core.c +++ /dev/null @@ -1,217 +0,0 @@ -/* exynos_drm_core.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h" -#include "exynos_drm_connector.h" -#include "exynos_drm_fbdev.h" - -static LIST_HEAD(exynos_drm_subdrv_list); -static struct drm_device *drm_dev; - -static int exynos_drm_subdrv_probe(struct drm_device *dev, - struct exynos_drm_subdrv *subdrv) -{ - struct drm_encoder *encoder; - struct drm_connector *connector; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (subdrv->probe) { - int ret; - - /* - * this probe callback would be called by sub driver - * after setting of all resources to this sub driver, - * such as clock, irq and register map are done or by load() - * of exynos drm driver. - * - * P.S. note that this driver is considered for modularization. - */ - ret = subdrv->probe(dev, subdrv->dev); - if (ret) - return ret; - } - - if (!subdrv->manager) - return 0; - - subdrv->manager->dev = subdrv->dev; - - /* create and initialize a encoder for this sub driver. */ - encoder = exynos_drm_encoder_create(dev, subdrv->manager, - (1 << MAX_CRTC) - 1); - if (!encoder) { - DRM_ERROR("failed to create encoder\n"); - return -EFAULT; - } - - /* - * create and initialize a connector for this sub driver and - * attach the encoder created above to the connector. - */ - connector = exynos_drm_connector_create(dev, encoder); - if (!connector) { - DRM_ERROR("failed to create connector\n"); - encoder->funcs->destroy(encoder); - return -EFAULT; - } - - subdrv->encoder = encoder; - subdrv->connector = connector; - - return 0; -} - -static void exynos_drm_subdrv_remove(struct drm_device *dev, - struct exynos_drm_subdrv *subdrv) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (subdrv->remove) - subdrv->remove(dev); - - if (subdrv->encoder) { - struct drm_encoder *encoder = subdrv->encoder; - encoder->funcs->destroy(encoder); - subdrv->encoder = NULL; - } - - if (subdrv->connector) { - struct drm_connector *connector = subdrv->connector; - connector->funcs->destroy(connector); - subdrv->connector = NULL; - } -} - -int exynos_drm_device_register(struct drm_device *dev) -{ - struct exynos_drm_subdrv *subdrv, *n; - int err; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (!dev) - return -EINVAL; - - drm_dev = dev; - - list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { - subdrv->drm_dev = dev; - err = exynos_drm_subdrv_probe(dev, subdrv); - if (err) { - DRM_DEBUG("exynos drm subdrv probe failed.\n"); - list_del(&subdrv->list); - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_device_register); - -int exynos_drm_device_unregister(struct drm_device *dev) -{ - struct exynos_drm_subdrv *subdrv; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (!dev) { - WARN(1, "Unexpected drm device unregister!\n"); - return -EINVAL; - } - - list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) - exynos_drm_subdrv_remove(dev, subdrv); - - drm_dev = NULL; - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); - -int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (!subdrv) - return -EINVAL; - - list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); - -int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (!subdrv) - return -EINVAL; - - list_del(&subdrv->list); - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); - -int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) -{ - struct exynos_drm_subdrv *subdrv; - int ret; - - list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { - if (subdrv->open) { - ret = subdrv->open(dev, subdrv->dev, file); - if (ret) - goto err; - } - } - - return 0; - -err: - list_for_each_entry_reverse(subdrv, &subdrv->list, list) { - if (subdrv->close) - subdrv->close(dev, subdrv->dev, file); - } - return ret; -} -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open); - -void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file) -{ - struct exynos_drm_subdrv *subdrv; - - list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { - if (subdrv->close) - subdrv->close(dev, subdrv->dev, file); - } -} -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_crtc.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_crtc.c deleted file mode 100644 index 4afb625..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_crtc.c +++ /dev/null @@ -1,436 +0,0 @@ -/* exynos_drm_crtc.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm_crtc_helper.h" - -#include "exynos_drm_crtc.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_fb.h" -#include "exynos_drm_encoder.h" -#include "exynos_drm_gem.h" - -#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ - drm_crtc) - -/* - * Exynos specific crtc structure. - * - * @drm_crtc: crtc object. - * @overlay: contain information common to display controller and hdmi and - * contents of this overlay object would be copied to sub driver size. - * @pipe: a crtc index created at load() with a new crtc object creation - * and the crtc object would be set to private->crtc array - * to get a crtc object corresponding to this pipe from private->crtc - * array when irq interrupt occured. the reason of using this pipe is that - * drm framework doesn't support multiple irq yet. - * we can refer to the crtc to current hardware interrupt occured through - * this pipe value. - * @dpms: store the crtc dpms value - */ -struct exynos_drm_crtc { - struct drm_crtc drm_crtc; - struct exynos_drm_overlay overlay; - unsigned int pipe; - unsigned int dpms; -}; - -static void exynos_drm_crtc_apply(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; - - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); -} - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos) -{ - struct exynos_drm_gem_buf *buffer; - unsigned int actual_w; - unsigned int actual_h; - int nr = exynos_drm_format_num_buffers(fb->pixel_format); - int i; - - for (i = 0; i < nr; i++) { - buffer = exynos_drm_fb_buffer(fb, i); - if (!buffer) { - DRM_LOG_KMS("buffer is null\n"); - return -EFAULT; - } - - overlay->dma_addr[i] = buffer->dma_addr; - overlay->vaddr[i] = buffer->kvaddr; - - DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", - i, (unsigned long)overlay->vaddr[i], - (unsigned long)overlay->dma_addr[i]); - } - - actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); - actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); - - /* set drm framebuffer data. */ - overlay->fb_x = pos->fb_x; - overlay->fb_y = pos->fb_y; - overlay->fb_width = fb->width; - overlay->fb_height = fb->height; - overlay->src_width = pos->src_w; - overlay->src_height = pos->src_h; - overlay->bpp = fb->bits_per_pixel; - overlay->pitch = fb->pitches[0]; - overlay->pixel_format = fb->pixel_format; - - /* set overlay range to be displayed. */ - overlay->crtc_x = pos->crtc_x; - overlay->crtc_y = pos->crtc_y; - overlay->crtc_width = actual_w; - overlay->crtc_height = actual_h; - - /* set drm mode data. */ - overlay->mode_width = mode->hdisplay; - overlay->mode_height = mode->vdisplay; - overlay->refresh = mode->vrefresh; - overlay->scan_flag = mode->flags; - - DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", - overlay->crtc_x, overlay->crtc_y, - overlay->crtc_width, overlay->crtc_height); - - return 0; -} - -static int exynos_drm_crtc_update(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_overlay *overlay; - struct exynos_drm_crtc_pos pos; - struct drm_display_mode *mode = &crtc->mode; - struct drm_framebuffer *fb = crtc->fb; - - if (!mode || !fb) - return -EINVAL; - - exynos_crtc = to_exynos_crtc(crtc); - overlay = &exynos_crtc->overlay; - - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - - /* it means the offset of framebuffer to be displayed. */ - pos.fb_x = crtc->x; - pos.fb_y = crtc->y; - - /* OSD position to be displayed. */ - pos.crtc_x = 0; - pos.crtc_y = 0; - pos.crtc_w = fb->width - crtc->x; - pos.crtc_h = fb->height - crtc->y; - pos.src_w = pos.crtc_w; - pos.src_h = pos.crtc_h; - - return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); -} - -static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - - DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); - - if (exynos_crtc->dpms == mode) { - DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); - return; - } - - mutex_lock(&dev->struct_mutex); - - switch (mode) { - case DRM_MODE_DPMS_ON: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - default: - DRM_ERROR("unspecified mode %d\n", mode); - break; - } - - mutex_unlock(&dev->struct_mutex); -} - -static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* drm framework doesn't check NULL. */ -} - -static void exynos_drm_crtc_commit(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * when set_crtc is requested from user or at booting time, - * crtc->commit would be called without dpms call so if dpms is - * no power on then crtc->dpms should be called - * with DRM_MODE_DPMS_ON for the hardware power to be on. - */ - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) { - int mode = DRM_MODE_DPMS_ON; - - /* - * enable hardware(power on) to all encoders hdmi connected - * to current crtc. - */ - exynos_drm_crtc_dpms(crtc, mode); - /* - * enable dma to all encoders connected to current crtc and - * lcd panel. - */ - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_dpms_from_crtc); - } - - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); -} - -static bool -exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* drm framework doesn't check NULL */ - return true; -} - -static int -exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * copy the mode data adjusted by mode_fixup() into crtc->mode - * so that hardware can be seet to proper mode. - */ - memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); - - return exynos_drm_crtc_update(crtc); -} - -static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ret = exynos_drm_crtc_update(crtc); - if (ret) - return ret; - - exynos_drm_crtc_apply(crtc); - - return ret; -} - -static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - /* drm framework doesn't check NULL */ -} - -static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { - .dpms = exynos_drm_crtc_dpms, - .prepare = exynos_drm_crtc_prepare, - .commit = exynos_drm_crtc_commit, - .mode_fixup = exynos_drm_crtc_mode_fixup, - .mode_set = exynos_drm_crtc_mode_set, - .mode_set_base = exynos_drm_crtc_mode_set_base, - .load_lut = exynos_drm_crtc_load_lut, -}; - -static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event) -{ - struct drm_device *dev = crtc->dev; - struct exynos_drm_private *dev_priv = dev->dev_private; - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct drm_framebuffer *old_fb = crtc->fb; - int ret = -EINVAL; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - mutex_lock(&dev->struct_mutex); - - if (event) { - /* - * the pipe from user always is 0 so we can set pipe number - * of current owner to event. - */ - event->pipe = exynos_crtc->pipe; - - ret = drm_vblank_get(dev, exynos_crtc->pipe); - if (ret) { - DRM_DEBUG("failed to acquire vblank counter\n"); - list_del(&event->base.link); - - goto out; - } - - list_add_tail(&event->base.link, - &dev_priv->pageflip_event_list); - - crtc->fb = fb; - ret = exynos_drm_crtc_update(crtc); - if (ret) { - crtc->fb = old_fb; - drm_vblank_put(dev, exynos_crtc->pipe); - list_del(&event->base.link); - - goto out; - } - - /* - * the values related to a buffer of the drm framebuffer - * to be applied should be set at here. because these values - * first, are set to shadow registers and then to - * real registers at vsync front porch period. - */ - exynos_drm_crtc_apply(crtc); - } -out: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_private *private = crtc->dev->dev_private; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - private->crtc[exynos_crtc->pipe] = NULL; - - drm_crtc_cleanup(crtc); - kfree(exynos_crtc); -} - -static struct drm_crtc_funcs exynos_crtc_funcs = { - .set_config = drm_crtc_helper_set_config, - .page_flip = exynos_drm_crtc_page_flip, - .destroy = exynos_drm_crtc_destroy, -}; - -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - - return &exynos_crtc->overlay; -} - -int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) -{ - struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_private *private = dev->dev_private; - struct drm_crtc *crtc; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); - if (!exynos_crtc) { - DRM_ERROR("failed to allocate exynos crtc\n"); - return -ENOMEM; - } - - exynos_crtc->pipe = nr; - exynos_crtc->dpms = DRM_MODE_DPMS_OFF; - exynos_crtc->overlay.zpos = DEFAULT_ZPOS; - crtc = &exynos_crtc->drm_crtc; - - private->crtc[nr] = crtc; - - drm_crtc_init(dev, crtc, &exynos_crtc_funcs); - drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); - - return 0; -} - -int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) -{ - struct exynos_drm_private *private = dev->dev_private; - struct exynos_drm_crtc *exynos_crtc = - to_exynos_crtc(private->crtc[crtc]); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) - return -EPERM; - - exynos_drm_fn_encoder(private->crtc[crtc], &crtc, - exynos_drm_enable_vblank); - - return 0; -} - -void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) -{ - struct exynos_drm_private *private = dev->dev_private; - struct exynos_drm_crtc *exynos_crtc = - to_exynos_crtc(private->crtc[crtc]); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) - return; - - exynos_drm_fn_encoder(private->crtc[crtc], &crtc, - exynos_drm_disable_vblank); -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_crtc.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_crtc.h deleted file mode 100644 index 16b8e21..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_crtc.h +++ /dev/null @@ -1,67 +0,0 @@ -/* exynos_drm_crtc.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_CRTC_H_ -#define _EXYNOS_DRM_CRTC_H_ - -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc); -int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr); -int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); -void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); - -/* - * Exynos specific crtc postion structure. - * - * @fb_x: offset x on a framebuffer to be displyed - * - the unit is screen coordinates. - * @fb_y: offset y on a framebuffer to be displayed - * - the unit is screen coordinates. - * @src_w: width of source area to be displayed from a framebuffer. - * @src_h: height of source area to be displayed from a framebuffer. - * @crtc_x: offset x on hardware screen. - * @crtc_y: offset y on hardware screen. - * @crtc_w: width of hardware screen. - * @crtc_h: height of hardware screen. - */ -struct exynos_drm_crtc_pos { - unsigned int fb_x; - unsigned int fb_y; - unsigned int src_w; - unsigned int src_h; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_w; - unsigned int crtc_h; -}; - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos); -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_dmabuf.c deleted file mode 100644 index 9b97057..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_dmabuf.c +++ /dev/null @@ -1,288 +0,0 @@ -/* exynos_drm_dmabuf.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" - -#include <linux/dma-buf.h> - -static struct sg_table *exynos_pages_to_sg(struct page **pages, int nr_pages, - unsigned int page_size) -{ - struct sg_table *sgt = NULL; - struct scatterlist *sgl; - int i, ret; - - sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); - if (!sgt) - goto out; - - ret = sg_alloc_table(sgt, nr_pages, GFP_KERNEL); - if (ret) - goto err_free_sgt; - - if (page_size < PAGE_SIZE) - page_size = PAGE_SIZE; - - for_each_sg(sgt->sgl, sgl, nr_pages, i) - sg_set_page(sgl, pages[i], page_size, 0); - - return sgt; - -err_free_sgt: - kfree(sgt); - sgt = NULL; -out: - return NULL; -} - -static struct sg_table *exynos_gem_map_dma_buf( - struct dma_buf_attachment *attach, - enum dma_data_direction dir) -{ - struct exynos_drm_gem_obj *gem_obj = attach->dmabuf->priv; - struct drm_device *dev = gem_obj->base.dev; - struct exynos_drm_gem_buf *buf; - struct sg_table *sgt = NULL; - unsigned int npages; - int nents; - - DRM_DEBUG_PRIME("%s\n", __FILE__); - - mutex_lock(&dev->struct_mutex); - - buf = gem_obj->buffer; - - /* there should always be pages allocated. */ - if (!buf->pages) { - DRM_ERROR("pages is null.\n"); - goto err_unlock; - } - - npages = buf->size / buf->page_size; - - sgt = exynos_pages_to_sg(buf->pages, npages, buf->page_size); - nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); - - DRM_DEBUG_PRIME("npages = %d buffer size = 0x%lx page_size = 0x%lx\n", - npages, buf->size, buf->page_size); - -err_unlock: - mutex_unlock(&dev->struct_mutex); - return sgt; -} - -static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach, - struct sg_table *sgt, - enum dma_data_direction dir) -{ - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); - sg_free_table(sgt); - kfree(sgt); - sgt = NULL; -} - -static void exynos_dmabuf_release(struct dma_buf *dmabuf) -{ - struct exynos_drm_gem_obj *exynos_gem_obj = dmabuf->priv; - - DRM_DEBUG_PRIME("%s\n", __FILE__); - - /* - * exynos_dmabuf_release() call means that file object's - * f_count is 0 and it calls drm_gem_object_handle_unreference() - * to drop the references that these values had been increased - * at drm_prime_handle_to_fd() - */ - if (exynos_gem_obj->base.export_dma_buf == dmabuf) { - exynos_gem_obj->base.export_dma_buf = NULL; - - /* - * drop this gem object refcount to release allocated buffer - * and resources. - */ - drm_gem_object_unreference_unlocked(&exynos_gem_obj->base); - } -} - -static void *exynos_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, - unsigned long page_num) -{ - return NULL; -} - -static void exynos_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, - unsigned long page_num, - void *addr) -{ - -} - -static void *exynos_gem_dmabuf_kmap(struct dma_buf *dma_buf, - unsigned long page_num) -{ - return NULL; -} - -static void exynos_gem_dmabuf_kunmap(struct dma_buf *dma_buf, - unsigned long page_num, void *addr) -{ - -} - -static struct dma_buf_ops exynos_dmabuf_ops = { - .map_dma_buf = exynos_gem_map_dma_buf, - .unmap_dma_buf = exynos_gem_unmap_dma_buf, - .kmap = exynos_gem_dmabuf_kmap, - .kmap_atomic = exynos_gem_dmabuf_kmap_atomic, - .kunmap = exynos_gem_dmabuf_kunmap, - .kunmap_atomic = exynos_gem_dmabuf_kunmap_atomic, - .release = exynos_dmabuf_release, -}; - -struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, - struct drm_gem_object *obj, int flags) -{ - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); - - return dma_buf_export(exynos_gem_obj, &exynos_dmabuf_ops, - exynos_gem_obj->base.size, 0600); -} - -struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sgt; - struct scatterlist *sgl; - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_gem_buf *buffer; - struct page *page; - int ret; - - DRM_DEBUG_PRIME("%s\n", __FILE__); - - /* is this one of own objects? */ - if (dma_buf->ops == &exynos_dmabuf_ops) { - struct drm_gem_object *obj; - - exynos_gem_obj = dma_buf->priv; - obj = &exynos_gem_obj->base; - - /* is it from our device? */ - if (obj->dev == drm_dev) { - drm_gem_object_reference(obj); - return obj; - } - } - - attach = dma_buf_attach(dma_buf, drm_dev->dev); - if (IS_ERR(attach)) - return ERR_PTR(-EINVAL); - - - sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sgt)) { - ret = PTR_ERR(sgt); - goto err_buf_detach; - } - - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) { - DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); - ret = -ENOMEM; - goto err_unmap_attach; - } - - buffer->pages = kzalloc(sizeof(*page) * sgt->nents, GFP_KERNEL); - if (!buffer->pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; - goto err_free_buffer; - } - - exynos_gem_obj = exynos_drm_gem_init(drm_dev, dma_buf->size); - if (!exynos_gem_obj) { - ret = -ENOMEM; - goto err_free_pages; - } - - sgl = sgt->sgl; - - if (sgt->nents == 1) { - buffer->dma_addr = sg_dma_address(sgt->sgl); - buffer->size = sg_dma_len(sgt->sgl); - } else { - unsigned int i = 0; - - buffer->dma_addr = sg_dma_address(sgl); - while (i < sgt->nents) { - buffer->pages[i] = sg_page(sgl); - buffer->size += sg_dma_len(sgl); - sgl = sg_next(sgl); - i++; - } - } - - exynos_gem_obj->buffer = buffer; - buffer->sgt = sgt; - exynos_gem_obj->base.import_attach = attach; - - /* register buffer information to private buffer manager. */ - ret = register_buf_to_priv_mgr(exynos_gem_obj, - &exynos_gem_obj->priv_handle, - &exynos_gem_obj->priv_id); - if (ret < 0) - goto err_release_gem; - - DRM_DEBUG_PRIME("ump id = %d, dma_addr = 0x%x, size = 0x%lx\n", - exynos_gem_obj->priv_id, - buffer->dma_addr, - buffer->size); - - return &exynos_gem_obj->base; - -err_release_gem: - drm_gem_object_release(&exynos_gem_obj->base); - kfree(exynos_gem_obj); - exynos_gem_obj = NULL; -err_free_pages: - kfree(buffer->pages); - buffer->pages = NULL; -err_free_buffer: - kfree(buffer); - buffer = NULL; -err_unmap_attach: - dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); -err_buf_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); -} - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM DMABUF Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_dmabuf.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_dmabuf.h deleted file mode 100644 index 662a8f9..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_dmabuf.h +++ /dev/null @@ -1,39 +0,0 @@ -/* exynos_drm_dmabuf.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_DMABUF_H_ -#define _EXYNOS_DRM_DMABUF_H_ - -#ifdef CONFIG_DRM_EXYNOS_DMABUF -struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, - struct drm_gem_object *obj, int flags); - -struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, - struct dma_buf *dma_buf); -#else -#define exynos_dmabuf_prime_export NULL -#define exynos_dmabuf_prime_import NULL -#endif -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_drv.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_drv.c deleted file mode 100644 index 340a8d6..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_drv.c +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm.h" -#include "drm_crtc_helper.h" - -#include <drm/exynos_drm.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_crtc.h" -#include "exynos_drm_encoder.h" -#include "exynos_drm_fbdev.h" -#include "exynos_drm_fb.h" -#include "exynos_drm_gem.h" -#include "exynos_drm_g2d.h" -#include "exynos_drm_rotator.h" -#include "exynos_drm_plane.h" -#include "exynos_drm_vidi.h" -#include "exynos_drm_dmabuf.h" - -#define DRIVER_NAME "exynos-drm" -#define DRIVER_DESC "Samsung SoC DRM" -#define DRIVER_DATE "20110530" -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 0 - -#define VBLANK_OFF_DELAY 50000 - -static int exynos_drm_list_gem_info(int id, void *ptr, void *data) -{ - struct drm_gem_object *obj = ptr; - struct drm_file *filp = data; - struct exynos_drm_gem_obj *gem = to_exynos_gem_obj(obj); - struct exynos_drm_gem_buf *buf = gem->buffer; - - DRM_INFO("%3d \t%3d \t%2d \t\t%2d \t0x%lx \t0x%x \t0x%lx "\ - "\t%2d \t\t%2d \t\t%2d\n", - filp->pid, - id, - atomic_read(&obj->refcount.refcount), - atomic_read(&obj->handle_count), - gem->size, - gem->flags, - buf->page_size, - buf->pfnmap, - obj->export_dma_buf ? 1 : 0, - obj->import_attach ? 1 : 0); - - return 0; -} - -static ssize_t exynos_drm_show_gem_info(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct drm_device *drm_dev = dev_get_drvdata(dev); - struct drm_file *filp; - - DRM_INFO("pid \thandle \trefcount \thcount \tsize \t\tflags "\ - "\tpage_size \tpfnmap \texport_to_fd \timport_from_fd\n"); - - list_for_each_entry(filp, &drm_dev->filelist, lhead) - idr_for_each(&filp->object_idr, &exynos_drm_list_gem_info, - filp); - - return strlen(buf); -} - -static const struct device_attribute exynos_device_attrs[] = { - __ATTR(gem_info, S_IRUGO, exynos_drm_show_gem_info, NULL) -}; - -static int exynos_drm_load(struct drm_device *dev, unsigned long flags) -{ - struct exynos_drm_private *private; - int ret; - int nr; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); - if (!private) { - DRM_ERROR("failed to allocate private\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&private->pageflip_event_list); - dev->dev_private = (void *)private; - - drm_mode_config_init(dev); - - /* init kms poll for handling hpd */ - drm_kms_helper_poll_init(dev); - - exynos_drm_mode_config_init(dev); - - /* - * EXYNOS4 is enough to have two CRTCs and each crtc would be used - * without dependency of hardware. - */ - for (nr = 0; nr < MAX_CRTC; nr++) { - ret = exynos_drm_crtc_create(dev, nr); - if (ret) - goto err_crtc; - } - - for (nr = 0; nr < MAX_PLANE; nr++) { - ret = exynos_plane_init(dev, nr); - if (ret) - goto err_crtc; - } - - ret = drm_vblank_init(dev, MAX_CRTC); - if (ret) - goto err_crtc; - - /* - * probe sub drivers such as display controller and hdmi driver, - * that were registered at probe() of platform driver - * to the sub driver and create encoder and connector for them. - */ - ret = exynos_drm_device_register(dev); - if (ret) - goto err_vblank; - - /* setup possible_clones. */ - exynos_drm_encoder_setup(dev); - - /* - * create and configure fb helper and also exynos specific - * fbdev object. - */ - ret = exynos_drm_fbdev_init(dev); - if (ret) { - DRM_ERROR("failed to initialize drm fbdev\n"); - goto err_drm_device; - } - - drm_vblank_offdelay = VBLANK_OFF_DELAY; - - ret = device_create_file(dev->dev, &exynos_device_attrs[0]); - if (ret < 0) - DRM_DEBUG_DRIVER("failed to create sysfs.\n"); - - return 0; - -err_drm_device: - exynos_drm_device_unregister(dev); -err_vblank: - drm_vblank_cleanup(dev); -err_crtc: - drm_mode_config_cleanup(dev); - kfree(private); - - return ret; -} - -static int exynos_drm_unload(struct drm_device *dev) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - exynos_drm_fbdev_fini(dev); - exynos_drm_device_unregister(dev); - drm_vblank_cleanup(dev); - drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); - kfree(dev->dev_private); - - dev->dev_private = NULL; - - return 0; -} - -static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); - if (!file_priv) - return -ENOMEM; - - drm_prime_init_file_private(&file->prime); - file->driver_priv = file_priv; - - return exynos_drm_subdrv_open(dev, file); -} - -static void exynos_drm_preclose(struct drm_device *dev, - struct drm_file *file) -{ - struct exynos_drm_private *private = dev->dev_private; - struct drm_pending_vblank_event *e, *t; - unsigned long flags; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - /* release events of current file */ - spin_lock_irqsave(&dev->event_lock, flags); - list_for_each_entry_safe(e, t, &private->pageflip_event_list, - base.link) { - if (e->base.file_priv == file) { - list_del(&e->base.link); - e->base.destroy(&e->base); - } - } - drm_prime_destroy_file_private(&file->prime); - spin_unlock_irqrestore(&dev->event_lock, flags); - - exynos_drm_subdrv_close(dev, file); -} - -static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - if (!file->driver_priv) - return; - - kfree(file->driver_priv); - file->driver_priv = NULL; -} - -static void exynos_drm_lastclose(struct drm_device *dev) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - exynos_drm_fbdev_restore_mode(dev); -} - -static struct vm_operations_struct exynos_drm_gem_vm_ops = { - .fault = exynos_drm_gem_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - -static struct drm_ioctl_desc exynos_ioctls[] = { - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, - DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET, - exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, - exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_USERPTR, - exynos_drm_gem_userptr_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, - exynos_drm_gem_get_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_EXPORT_UMP, - exynos_drm_gem_export_ump_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CACHE_OP, - exynos_drm_gem_cache_op_ioctl, DRM_UNLOCKED), - /* temporary ioctl commands. */ - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET_PHY, - exynos_drm_gem_get_phy_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_PHY_IMP, - exynos_drm_gem_phy_imp_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, - exynos_plane_set_zpos_ioctl, DRM_UNLOCKED | DRM_AUTH), - DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, - vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), - - DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, - exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), - DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, - exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), - DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, - exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), - - DRM_IOCTL_DEF_DRV(EXYNOS_ROTATOR_EXEC, - exynos_drm_rotator_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), -}; - -static const struct file_operations exynos_drm_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = exynos_drm_gem_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .release = drm_release, -}; - -static struct drm_driver exynos_drm_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM | - DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, - .load = exynos_drm_load, - .unload = exynos_drm_unload, - .open = exynos_drm_open, - .preclose = exynos_drm_preclose, - .lastclose = exynos_drm_lastclose, - .postclose = exynos_drm_postclose, - .get_vblank_counter = drm_vblank_count, - .enable_vblank = exynos_drm_crtc_enable_vblank, - .disable_vblank = exynos_drm_crtc_disable_vblank, - .gem_init_object = exynos_drm_gem_init_object, - .gem_free_object = exynos_drm_gem_free_object, - .gem_vm_ops = &exynos_drm_gem_vm_ops, - .gem_close_object = &exynos_drm_gem_close_object, - .dumb_create = exynos_drm_gem_dumb_create, - .dumb_map_offset = exynos_drm_gem_dumb_map_offset, - .dumb_destroy = exynos_drm_gem_dumb_destroy, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = exynos_dmabuf_prime_export, - .gem_prime_import = exynos_dmabuf_prime_import, - .ioctls = exynos_ioctls, - .fops = &exynos_drm_driver_fops, - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, -}; - -static int exynos_drm_platform_probe(struct platform_device *pdev) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); - - return drm_platform_init(&exynos_drm_driver, pdev); -} - -static int exynos_drm_platform_remove(struct platform_device *pdev) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - drm_platform_exit(&exynos_drm_driver, pdev); - - return 0; -} - -static struct platform_driver exynos_drm_platform_driver = { - .probe = exynos_drm_platform_probe, - .remove = __devexit_p(exynos_drm_platform_remove), - .driver = { - .owner = THIS_MODULE, - .name = "exynos-drm", - }, -}; - -static int __init exynos_drm_init(void) -{ - int ret; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - -#ifdef CONFIG_DRM_EXYNOS_FIMD - ret = platform_driver_register(&fimd_driver); - if (ret < 0) - goto out_fimd; -#endif - -#ifdef CONFIG_DRM_EXYNOS_HDMI - ret = platform_driver_register(&hdmi_driver); - if (ret < 0) - goto out_hdmi; - ret = platform_driver_register(&mixer_driver); - if (ret < 0) - goto out_mixer; - ret = platform_driver_register(&exynos_drm_common_hdmi_driver); - if (ret < 0) - goto out_common_hdmi; -#endif - -#ifdef CONFIG_DRM_EXYNOS_VIDI - ret = platform_driver_register(&vidi_driver); - if (ret < 0) - goto out_vidi; -#endif - -#ifdef CONFIG_DRM_EXYNOS_G2D - ret = platform_driver_register(&g2d_driver); - if (ret < 0) - goto out_g2d; -#endif - -#ifdef CONFIG_DRM_EXYNOS_ROTATOR - ret = platform_driver_register(&rotator_driver); - if (ret < 0) - goto out_rotator; -#endif - - ret = platform_driver_register(&exynos_drm_platform_driver); - if (ret < 0) - goto out; - - return 0; - -out: -#ifdef CONFIG_DRM_EXYNOS_ROTATOR - platform_driver_unregister(&rotator_driver); -out_rotator: -#endif - -#ifdef CONFIG_DRM_EXYNOS_G2D - platform_driver_unregister(&g2d_driver); -out_g2d: -#endif - -#ifdef CONFIG_DRM_EXYNOS_VIDI - platform_driver_unregister(&vidi_driver); -out_vidi: -#endif - -#ifdef CONFIG_DRM_EXYNOS_HDMI - platform_driver_unregister(&exynos_drm_common_hdmi_driver); -out_common_hdmi: - platform_driver_unregister(&mixer_driver); -out_mixer: - platform_driver_unregister(&hdmi_driver); -out_hdmi: -#endif - -#ifdef CONFIG_DRM_EXYNOS_FIMD - platform_driver_unregister(&fimd_driver); -out_fimd: -#endif - return ret; -} - -static void __exit exynos_drm_exit(void) -{ - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - platform_driver_unregister(&exynos_drm_platform_driver); - -#ifdef CONFIG_DRM_EXYNOS_ROTATOR - platform_driver_unregister(&rotator_driver); -#endif - -#ifdef CONFIG_DRM_EXYNOS_G2D - platform_driver_unregister(&g2d_driver); -#endif - -#ifdef CONFIG_DRM_EXYNOS_VIDI - platform_driver_unregister(&vidi_driver); -#endif - -#ifdef CONFIG_DRM_EXYNOS_HDMI - platform_driver_unregister(&exynos_drm_common_hdmi_driver); - platform_driver_unregister(&mixer_driver); - platform_driver_unregister(&hdmi_driver); -#endif - -#ifdef CONFIG_DRM_EXYNOS_FIMD - platform_driver_unregister(&fimd_driver); -#endif -} - -module_init(exynos_drm_init); -module_exit(exynos_drm_exit); - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_drv.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_drv.h deleted file mode 100644 index 463b086..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_drv.h +++ /dev/null @@ -1,335 +0,0 @@ -/* exynos_drm_drv.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_DRV_H_ -#define _EXYNOS_DRM_DRV_H_ - -#include <linux/module.h> -#include "drm.h" - -#define MAX_CRTC 3 -#define MAX_PLANE 5 -#define MAX_FB_BUFFER 4 -#define DEFAULT_ZPOS -1 - -struct drm_device; -struct exynos_drm_overlay; -struct drm_connector; - -extern unsigned int drm_vblank_offdelay; - -/* this enumerates display type. */ -enum exynos_drm_output_type { - EXYNOS_DISPLAY_TYPE_NONE, - /* RGB or CPU Interface. */ - EXYNOS_DISPLAY_TYPE_LCD, - /* HDMI Interface. */ - EXYNOS_DISPLAY_TYPE_HDMI, - /* Virtual Display Interface. */ - EXYNOS_DISPLAY_TYPE_VIDI, -}; - -/* - * Exynos drm overlay ops structure. - * - * @mode_set: copy drm overlay info to hw specific overlay info. - * @commit: apply hardware specific overlay data to registers. - * @disable: disable hardware specific overlay. - */ -struct exynos_drm_overlay_ops { - void (*mode_set)(struct device *subdrv_dev, - struct exynos_drm_overlay *overlay); - void (*commit)(struct device *subdrv_dev, int zpos); - void (*disable)(struct device *subdrv_dev, int zpos); -}; - -/* - * Exynos drm common overlay structure. - * - * @fb_x: offset x on a framebuffer to be displayed. - * - the unit is screen coordinates. - * @fb_y: offset y on a framebuffer to be displayed. - * - the unit is screen coordinates. - * @fb_width: width of a framebuffer. - * @fb_height: height of a framebuffer. - * @src_width: width of a partial image to be displayed from framebuffer. - * @src_height: height of a partial image to be displayed from framebuffer. - * @crtc_x: offset x on hardware screen. - * @crtc_y: offset y on hardware screen. - * @crtc_width: window width to be displayed (hardware screen). - * @crtc_height: window height to be displayed (hardware screen). - * @mode_width: width of screen mode. - * @mode_height: height of screen mode. - * @refresh: refresh rate. - * @scan_flag: interlace or progressive way. - * (it could be DRM_MODE_FLAG_*) - * @bpp: pixel size.(in bit) - * @pixel_format: fourcc pixel format of this overlay - * @dma_addr: array of bus(accessed by dma) address to the memory region - * allocated for a overlay. - * @vaddr: array of virtual memory addresss to this overlay. - * @zpos: order of overlay layer(z position). - * @default_win: a window to be enabled. - * @color_key: color key on or off. - * @index_color: if using color key feature then this value would be used - * as index color. - * @local_path: in case of lcd type, local path mode on or off. - * @transparency: transparency on or off. - * @activated: activated or not. - * - * this structure is common to exynos SoC and its contents would be copied - * to hardware specific overlay info. - */ -struct exynos_drm_overlay { - unsigned int fb_x; - unsigned int fb_y; - unsigned int fb_width; - unsigned int fb_height; - unsigned int src_width; - unsigned int src_height; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_width; - unsigned int crtc_height; - unsigned int mode_width; - unsigned int mode_height; - unsigned int refresh; - unsigned int scan_flag; - unsigned int bpp; - unsigned int pitch; - uint32_t pixel_format; - dma_addr_t dma_addr[MAX_FB_BUFFER]; - void __iomem *vaddr[MAX_FB_BUFFER]; - int zpos; - - bool default_win; - bool color_key; - unsigned int index_color; - bool local_path; - bool transparency; - bool activated; -}; - -/* - * Exynos DRM Display Structure. - * - this structure is common to analog tv, digital tv and lcd panel. - * - * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. - * @is_connected: check for that display is connected or not. - * @get_edid: get edid modes from display driver. - * @get_panel: get panel object from display driver. - * @check_timing: check if timing is valid or not. - * @power_on: display device on or off. - */ -struct exynos_drm_display_ops { - enum exynos_drm_output_type type; - bool (*is_connected)(struct device *dev); - int (*get_edid)(struct device *dev, struct drm_connector *connector, - u8 *edid, int len); - void *(*get_panel)(struct device *dev); - int (*check_timing)(struct device *dev, void *timing); - int (*power_on)(struct device *dev, int mode); -}; - -/* - * Exynos drm manager ops - * - * @dpms: control device power. - * @apply: set timing, vblank and overlay data to registers. - * @mode_fixup: fix mode data comparing to hw specific display mode. - * @mode_set: convert drm_display_mode to hw specific display mode and - * would be called by encoder->mode_set(). - * @get_max_resol: get maximum resolution to specific hardware. - * @commit: set current hw specific display mode to hw. - * @enable_vblank: specific driver callback for enabling vblank interrupt. - * @disable_vblank: specific driver callback for disabling vblank interrupt. - */ -struct exynos_drm_manager_ops { - void (*dpms)(struct device *subdrv_dev, int mode); - void (*apply)(struct device *subdrv_dev); - void (*mode_fixup)(struct device *subdrv_dev, - struct drm_connector *connector, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); - void (*mode_set)(struct device *subdrv_dev, void *mode); - void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width, - unsigned int *height); - void (*commit)(struct device *subdrv_dev); - int (*enable_vblank)(struct device *subdrv_dev); - void (*disable_vblank)(struct device *subdrv_dev); -}; - -/* - * Exynos drm common manager structure. - * - * @dev: pointer to device object for subdrv device driver. - * sub drivers such as display controller or hdmi driver, - * have their own device object. - * @ops: pointer to callbacks for exynos drm specific framebuffer. - * these callbacks should be set by specific drivers such fimd - * or hdmi driver and are used to control hardware global registers. - * @overlay_ops: pointer to callbacks for exynos drm specific framebuffer. - * these callbacks should be set by specific drivers such fimd - * or hdmi driver and are used to control hardware overlay reigsters. - * @display: pointer to callbacks for exynos drm specific framebuffer. - * these callbacks should be set by specific drivers such fimd - * or hdmi driver and are used to control display devices such as - * analog tv, digital tv and lcd panel and also get timing data for them. - */ -struct exynos_drm_manager { - struct device *dev; - int pipe; - struct exynos_drm_manager_ops *ops; - struct exynos_drm_overlay_ops *overlay_ops; - struct exynos_drm_display_ops *display_ops; -}; - -struct iommu_info_node { - struct list_head list; - dma_addr_t dma_addr; - void *gem_obj; - /* TODO */ -}; - -/* - * Exynos drm g2d private structure - * - * @dev: device object to device driver for using iommu. - * @iommu_map_list: list head to iommu map information. - * each device driver using iommu should have its own iommu_map_list - * because device drivers have their own device address space and - * the device address spaces could be duplicated echo other. - */ -struct exynos_drm_g2d_private { - struct device *dev; - struct list_head inuse_cmdlist; - struct list_head event_list; - struct list_head iommu_map_list; -}; - -/* - * Exynos drm rotator private structure - * - * @dev: device object to device driver for using driver data. - * @iommu_list: list head to iommu map information. - */ -struct exynos_drm_rot_private { - struct device *dev; - struct list_head iommu_list; -}; - -struct drm_exynos_file_private { - struct exynos_drm_g2d_private *g2d_priv; - struct exynos_drm_rot_private *rot_priv; -}; - -/* - * Exynos drm private structure. - */ -struct exynos_drm_private { - struct drm_fb_helper *fb_helper; - - /* list head for new event to be added. */ - struct list_head pageflip_event_list; - - /* - * created crtc object would be contained at this array and - * this array is used to be aware of which crtc did it request vblank. - */ - struct drm_crtc *crtc[MAX_CRTC]; -}; - -/* - * Exynos drm sub driver structure. - * - * @list: sub driver has its own list object to register to exynos drm driver. - * @dev: pointer to device object for subdrv device driver. - * @drm_dev: pointer to drm_device and this pointer would be set - * when sub driver calls exynos_drm_subdrv_register(). - * @manager: subdrv has its own manager to control a hardware appropriately - * and we can access a hardware drawing on this manager. - * @probe: this callback would be called by exynos drm driver after - * subdrv is registered to it. - * @remove: this callback is used to release resources created - * by probe callback. - * @open: this would be called with drm device file open. - * @close: this would be called with drm device file close. - * @encoder: encoder object owned by this sub driver. - * @connector: connector object owned by this sub driver. - */ -struct exynos_drm_subdrv { - struct list_head list; - struct device *dev; - struct drm_device *drm_dev; - struct exynos_drm_manager *manager; - - int (*probe)(struct drm_device *drm_dev, struct device *dev); - void (*remove)(struct drm_device *dev); - int (*open)(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file); - void (*close)(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file); - - struct drm_encoder *encoder; - struct drm_connector *connector; -}; - -/* - * this function calls a probe callback registered to sub driver list and - * create its own encoder and connector and then set drm_device object - * to global one. - */ -int exynos_drm_device_register(struct drm_device *dev); -/* - * this function calls a remove callback registered to sub driver list and - * destroy its own encoder and connetor. - */ -int exynos_drm_device_unregister(struct drm_device *dev); - -/* - * this function would be called by sub drivers such as display controller - * or hdmi driver to register this sub driver object to exynos drm driver - * and when a sub driver is registered to exynos drm driver a probe callback - * of the sub driver is called and creates its own encoder and connector. - */ -int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv); - -/* this function removes subdrv list from exynos drm driver */ -int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv); - -int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file); -void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file); - -extern struct platform_driver fimd_driver; -extern struct platform_driver hdmi_driver; -extern struct platform_driver mixer_driver; -extern struct platform_driver exynos_drm_common_hdmi_driver; -extern struct platform_driver vidi_driver; -extern struct platform_driver g2d_driver; -extern struct platform_driver rotator_driver; -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_encoder.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_encoder.c deleted file mode 100644 index 6e9ac7b..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_encoder.c +++ /dev/null @@ -1,445 +0,0 @@ -/* exynos_drm_encoder.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm_crtc_helper.h" - -#include "exynos_drm_drv.h" -#include "exynos_drm_crtc.h" -#include "exynos_drm_encoder.h" - -#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ - drm_encoder) - -/* - * exynos specific encoder structure. - * - * @drm_encoder: encoder object. - * @manager: specific encoder has its own manager to control a hardware - * appropriately and we can access a hardware drawing on this manager. - * @dpms: store the encoder dpms value. - */ -struct exynos_drm_encoder { - struct drm_encoder drm_encoder; - struct exynos_drm_manager *manager; - int dpms; -}; - -static void exynos_drm_display_power(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - struct exynos_drm_display_ops *display_ops = - manager->display_ops; - - DRM_DEBUG_KMS("connector[%d] dpms[%d]\n", - connector->base.id, mode); - if (display_ops && display_ops->power_on) - display_ops->power_on(manager->dev, mode); - } - } -} - -static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - - DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); - - if (exynos_encoder->dpms == mode) { - DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); - return; - } - - mutex_lock(&dev->struct_mutex); - - switch (mode) { - case DRM_MODE_DPMS_ON: - if (manager_ops && manager_ops->apply) - manager_ops->apply(manager->dev); - exynos_drm_display_power(encoder, mode); - exynos_encoder->dpms = mode; - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - exynos_drm_display_power(encoder, mode); - exynos_encoder->dpms = mode; - break; - default: - DRM_ERROR("unspecified mode %d\n", mode); - break; - } - - mutex_unlock(&dev->struct_mutex); -} - -static bool -exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct exynos_drm_manager_ops *manager_ops = manager->ops; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) - if (manager_ops && manager_ops->mode_fixup) - manager_ops->mode_fixup(manager->dev, connector, - mode, adjusted_mode); - } - - return true; -} - -static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev, - encoder->crtc); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - if (manager_ops && manager_ops->mode_set) - manager_ops->mode_set(manager->dev, - adjusted_mode); - - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); - } - } -} - -static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* drm framework doesn't check NULL. */ -} - -static void exynos_drm_encoder_commit(struct drm_encoder *encoder) -{ - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct exynos_drm_manager_ops *manager_ops = manager->ops; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (manager_ops && manager_ops->commit) - manager_ops->commit(manager->dev); -} - -static struct drm_crtc * -exynos_drm_encoder_get_crtc(struct drm_encoder *encoder) -{ - return encoder->crtc; -} - -static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { - .dpms = exynos_drm_encoder_dpms, - .mode_fixup = exynos_drm_encoder_mode_fixup, - .mode_set = exynos_drm_encoder_mode_set, - .prepare = exynos_drm_encoder_prepare, - .commit = exynos_drm_encoder_commit, - .get_crtc = exynos_drm_encoder_get_crtc, -}; - -static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) -{ - struct exynos_drm_encoder *exynos_encoder = - to_exynos_encoder(encoder); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_encoder->manager->pipe = -1; - - drm_encoder_cleanup(encoder); - kfree(exynos_encoder); -} - -static struct drm_encoder_funcs exynos_encoder_funcs = { - .destroy = exynos_drm_encoder_destroy, -}; - -static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) -{ - struct drm_encoder *clone; - struct drm_device *dev = encoder->dev; - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - struct exynos_drm_display_ops *display_ops = - exynos_encoder->manager->display_ops; - unsigned int clone_mask = 0; - int cnt = 0; - - list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { - switch (display_ops->type) { - case EXYNOS_DISPLAY_TYPE_LCD: - case EXYNOS_DISPLAY_TYPE_HDMI: - case EXYNOS_DISPLAY_TYPE_VIDI: - clone_mask |= (1 << (cnt++)); - break; - default: - continue; - } - } - - return clone_mask; -} - -void exynos_drm_encoder_setup(struct drm_device *dev) -{ - struct drm_encoder *encoder; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) - encoder->possible_clones = exynos_drm_encoder_clones(encoder); -} - -struct drm_encoder * -exynos_drm_encoder_create(struct drm_device *dev, - struct exynos_drm_manager *manager, - unsigned int possible_crtcs) -{ - struct drm_encoder *encoder; - struct exynos_drm_encoder *exynos_encoder; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!manager || !possible_crtcs) - return NULL; - - if (!manager->dev) - return NULL; - - exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); - if (!exynos_encoder) { - DRM_ERROR("failed to allocate encoder\n"); - return NULL; - } - - exynos_encoder->dpms = DRM_MODE_DPMS_OFF; - exynos_encoder->manager = manager; - encoder = &exynos_encoder->drm_encoder; - encoder->possible_crtcs = possible_crtcs; - - DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); - - drm_encoder_init(dev, encoder, &exynos_encoder_funcs, - DRM_MODE_ENCODER_TMDS); - - drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); - - DRM_DEBUG_KMS("encoder has been created\n"); - - return encoder; -} - -struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder) -{ - return to_exynos_encoder(encoder)->manager; -} - -void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, - void (*fn)(struct drm_encoder *, void *)) -{ - struct drm_device *dev = crtc->dev; - struct drm_encoder *encoder; - struct exynos_drm_private *private = dev->dev_private; - struct exynos_drm_manager *manager; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - /* - * if crtc is detached from encoder, check pipe, - * otherwise check crtc attached to encoder - */ - if (!encoder->crtc) { - manager = to_exynos_encoder(encoder)->manager; - if (manager->pipe < 0 || - private->crtc[manager->pipe] != crtc) - continue; - } else { - if (encoder->crtc != crtc) - continue; - } - - fn(encoder, data); - } -} - -void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int crtc = *(int *)data; - - if (manager->pipe == -1) - manager->pipe = crtc; - - if (manager_ops->enable_vblank) - manager_ops->enable_vblank(manager->dev); -} - -void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int crtc = *(int *)data; - - if (manager->pipe == -1) - manager->pipe = crtc; - - if (manager_ops->disable_vblank) - manager_ops->disable_vblank(manager->dev); -} - -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - int zpos = DEFAULT_ZPOS; - - if (data) - zpos = *(int *)data; - - if (overlay_ops && overlay_ops->commit) - overlay_ops->commit(manager->dev, zpos); -} - -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - int crtc = *(int *)data; - int zpos = DEFAULT_ZPOS; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * when crtc is detached from encoder, this pipe is used - * to select manager operation - */ - manager->pipe = crtc; - - exynos_drm_encoder_crtc_plane_commit(encoder, &zpos); -} - -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - int mode = *(int *)data; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_drm_encoder_dpms(encoder, mode); - - exynos_encoder->dpms = mode; -} - -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) -{ - struct drm_device *dev = encoder->dev; - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - struct exynos_drm_manager *manager = exynos_encoder->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct drm_connector *connector; - int mode = *(int *)data; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (manager_ops && manager_ops->dpms) - manager_ops->dpms(manager->dev, mode); - - /* - * set current dpms mode to the connector connected to - * current encoder. connector->dpms would be checked - * at drm_helper_connector_dpms() - */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - if (connector->encoder == encoder) - connector->dpms = mode; - - /* - * if this condition is ok then it means that the crtc is already - * detached from encoder and last function for detaching is properly - * done, so clear pipe from manager to prevent repeated call. - */ - if (mode > DRM_MODE_DPMS_ON) { - if (!encoder->crtc) - manager->pipe = -1; - } -} - -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = data; - - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); -} - -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - int zpos = DEFAULT_ZPOS; - - DRM_DEBUG_KMS("\n"); - - if (data) - zpos = *(int *)data; - - if (overlay_ops && overlay_ops->disable) - overlay_ops->disable(manager->dev, zpos); -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_encoder.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_encoder.h deleted file mode 100644 index eb7d231..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_encoder.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_ENCODER_H_ -#define _EXYNOS_DRM_ENCODER_H_ - -struct exynos_drm_manager; - -void exynos_drm_encoder_setup(struct drm_device *dev); -struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, - struct exynos_drm_manager *mgr, - unsigned int possible_crtcs); -struct exynos_drm_manager * -exynos_drm_get_manager(struct drm_encoder *encoder); -void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, - void (*fn)(struct drm_encoder *, void *)); -void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); -void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data); -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, - void *data); -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data); - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_fb.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_fb.c deleted file mode 100644 index 7b47330..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_fb.c +++ /dev/null @@ -1,251 +0,0 @@ -/* exynos_drm_fb.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm_crtc.h" -#include "drm_crtc_helper.h" -#include "drm_fb_helper.h" - -#include "exynos_drm.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_fb.h" -#include "exynos_drm_gem.h" - -#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) - -/* - * exynos specific framebuffer structure. - * - * @fb: drm framebuffer obejct. - * @exynos_gem_obj: array of exynos specific gem object containing a gem object. - */ -struct exynos_drm_fb { - struct drm_framebuffer fb; - struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; -}; - -static int check_fb_gem_memory_type(struct exynos_drm_gem_obj *exynos_gem_obj) -{ - unsigned int flags; - - flags = exynos_gem_obj->flags; - - /* not support physically non-continuous memory for fb yet. TODO */ - if (IS_NONCONTIG_BUFFER(flags)) { - DRM_ERROR("cannot use this gem memory type for fb.\n"); - return -EINVAL; - } - - return 0; -} - -static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) -{ - struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - drm_framebuffer_cleanup(fb); - - kfree(exynos_fb); - exynos_fb = NULL; -} - -static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return drm_gem_handle_create(file_priv, - &exynos_fb->exynos_gem_obj[0]->base, handle); -} - -static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, - struct drm_file *file_priv, unsigned flags, - unsigned color, struct drm_clip_rect *clips, - unsigned num_clips) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO */ - - return 0; -} - -static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { - .destroy = exynos_drm_fb_destroy, - .create_handle = exynos_drm_fb_create_handle, - .dirty = exynos_drm_fb_dirty, -}; - -struct drm_framebuffer * -exynos_drm_framebuffer_init(struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj) -{ - struct exynos_drm_fb *exynos_fb; - struct exynos_drm_gem_obj *exynos_gem_obj; - int ret; - - exynos_gem_obj = to_exynos_gem_obj(obj); - - ret = check_fb_gem_memory_type(exynos_gem_obj); - if (ret < 0) { - DRM_ERROR("cannot use this gem memory type for fb.\n"); - return ERR_PTR(-EINVAL); - } - - exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); - if (!exynos_fb) { - DRM_ERROR("failed to allocate exynos drm framebuffer\n"); - return ERR_PTR(-ENOMEM); - } - - exynos_fb->exynos_gem_obj[0] = exynos_gem_obj; - - ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); - if (ret) { - DRM_ERROR("failed to initialize framebuffer\n"); - return ERR_PTR(ret); - } - - drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); - - return &exynos_fb->fb; -} - -static struct drm_framebuffer * -exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, - struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct drm_gem_object *obj; - struct drm_framebuffer *fb; - struct exynos_drm_fb *exynos_fb; - int nr; - int i; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); - if (!obj) { - DRM_ERROR("failed to lookup gem object\n"); - return ERR_PTR(-ENOENT); - } - - drm_gem_object_unreference_unlocked(obj); - - fb = exynos_drm_framebuffer_init(dev, mode_cmd, obj); - if (IS_ERR(fb)) - return fb; - - exynos_fb = to_exynos_fb(fb); - nr = exynos_drm_format_num_buffers(fb->pixel_format); - - for (i = 1; i < nr; i++) { - struct exynos_drm_gem_obj *exynos_gem_obj; - int ret; - - obj = drm_gem_object_lookup(dev, file_priv, - mode_cmd->handles[i]); - if (!obj) { - DRM_ERROR("failed to lookup gem object\n"); - exynos_drm_fb_destroy(fb); - return ERR_PTR(-ENOENT); - } - - drm_gem_object_unreference_unlocked(obj); - - exynos_gem_obj = to_exynos_gem_obj(obj); - - ret = check_fb_gem_memory_type(exynos_gem_obj); - if (ret < 0) { - DRM_ERROR("cannot use this gem memory type for fb.\n"); - exynos_drm_fb_destroy(fb); - return ERR_PTR(ret); - } - - exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj); - } - - return fb; -} - -struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, - int index) -{ - struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); - struct exynos_drm_gem_buf *buffer; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (index >= MAX_FB_BUFFER) - return NULL; - - buffer = exynos_fb->exynos_gem_obj[index]->buffer; - if (!buffer) - return NULL; - - DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n", - (unsigned long)buffer->kvaddr, - (unsigned long)buffer->dma_addr); - - return buffer; -} - -static void exynos_drm_output_poll_changed(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - struct drm_fb_helper *fb_helper = private->fb_helper; - - if (fb_helper) - drm_fb_helper_hotplug_event(fb_helper); -} - -static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { - .fb_create = exynos_user_fb_create, - .output_poll_changed = exynos_drm_output_poll_changed, -}; - -void exynos_drm_mode_config_init(struct drm_device *dev) -{ - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - - /* - * set max width and height as default value(4096x4096). - * this value would be used to check framebuffer size limitation - * at drm_mode_addfb(). - */ - dev->mode_config.max_width = 4096; - dev->mode_config.max_height = 4096; - - dev->mode_config.funcs = &exynos_drm_mode_config_funcs; -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_fb.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_fb.h deleted file mode 100644 index 3ecb30d..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_fb.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_FB_H_ -#define _EXYNOS_DRM_FB_H - -static inline int exynos_drm_format_num_buffers(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_NV12M: - case DRM_FORMAT_NV12MT: - return 2; - case DRM_FORMAT_YUV420M: - return 3; - default: - return 1; - } -} - -struct drm_framebuffer * -exynos_drm_framebuffer_init(struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj); - -/* get memory information of a drm framebuffer */ -struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, - int index); - -void exynos_drm_mode_config_init(struct drm_device *dev); - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_fbdev.c deleted file mode 100644 index d5586cc..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_fbdev.c +++ /dev/null @@ -1,318 +0,0 @@ -/* exynos_drm_fbdev.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm_crtc.h" -#include "drm_fb_helper.h" -#include "drm_crtc_helper.h" - -#include "exynos_drm_drv.h" -#include "exynos_drm_fb.h" -#include "exynos_drm_gem.h" - -#define MAX_CONNECTOR 4 -#define PREFERRED_BPP 32 - -#define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\ - drm_fb_helper) - -struct exynos_drm_fbdev { - struct drm_fb_helper drm_fb_helper; - struct exynos_drm_gem_obj *exynos_gem_obj; -}; - -static struct fb_ops exynos_drm_fb_ops = { - .owner = THIS_MODULE, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_blank = drm_fb_helper_blank, - .fb_pan_display = drm_fb_helper_pan_display, - .fb_setcmap = drm_fb_helper_setcmap, -}; - -static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, - struct drm_framebuffer *fb) -{ - struct fb_info *fbi = helper->fbdev; - struct drm_device *dev = helper->dev; - struct exynos_drm_gem_buf *buffer; - unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); - unsigned long offset; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); - drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); - - /* RGB formats use only one buffer */ - buffer = exynos_drm_fb_buffer(fb, 0); - if (!buffer) { - DRM_LOG_KMS("buffer is null.\n"); - return -EFAULT; - } - - offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); - offset += fbi->var.yoffset * fb->pitches[0]; - - dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr; - fbi->screen_base = buffer->kvaddr + offset; - fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset); - fbi->screen_size = size; - fbi->fix.smem_len = size; - - return 0; -} - -static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_device *dev = helper->dev; - struct fb_info *fbi; - struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - struct platform_device *pdev = dev->platformdev; - unsigned long size; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", - sizes->surface_width, sizes->surface_height, - sizes->surface_bpp); - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - mutex_lock(&dev->struct_mutex); - - fbi = framebuffer_alloc(0, &pdev->dev); - if (!fbi) { - DRM_ERROR("failed to allocate fb info.\n"); - ret = -ENOMEM; - goto out; - } - - size = mode_cmd.pitches[0] * mode_cmd.height; - - /* 0 means to allocate physically continuous memory */ - exynos_gem_obj = exynos_drm_gem_create(dev, 0, size); - if (IS_ERR(exynos_gem_obj)) { - ret = PTR_ERR(exynos_gem_obj); - goto out; - } - - exynos_fbdev->exynos_gem_obj = exynos_gem_obj; - - helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, - &exynos_gem_obj->base); - if (IS_ERR_OR_NULL(helper->fb)) { - DRM_ERROR("failed to create drm framebuffer.\n"); - ret = PTR_ERR(helper->fb); - goto out; - } - - helper->fbdev = fbi; - - fbi->par = helper; - fbi->flags = FBINFO_FLAG_DEFAULT; - fbi->fbops = &exynos_drm_fb_ops; - - ret = fb_alloc_cmap(&fbi->cmap, 256, 0); - if (ret) { - DRM_ERROR("failed to allocate cmap.\n"); - goto out; - } - - ret = exynos_drm_fbdev_update(helper, helper->fb); - if (ret < 0) { - fb_dealloc_cmap(&fbi->cmap); - goto out; - } - -/* - * if failed, all resources allocated above would be released by - * drm_mode_config_cleanup() when drm_load() had been called prior - * to any specific driver such as fimd or hdmi driver. - */ -out: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * with !helper->fb, it means that this funcion is called first time - * and after that, the helper->fb would be used as clone mode. - */ - if (!helper->fb) { - ret = exynos_drm_fbdev_create(helper, sizes); - if (ret < 0) { - DRM_ERROR("failed to create fbdev.\n"); - return ret; - } - - /* - * fb_helper expects a value more than 1 if succeed - * because register_framebuffer() should be called. - */ - ret = 1; - } - - return ret; -} - -static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { - .fb_probe = exynos_drm_fbdev_probe, -}; - -int exynos_drm_fbdev_init(struct drm_device *dev) -{ - struct exynos_drm_fbdev *fbdev; - struct exynos_drm_private *private = dev->dev_private; - struct drm_fb_helper *helper; - unsigned int num_crtc; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) - return 0; - - fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); - if (!fbdev) { - DRM_ERROR("failed to allocate drm fbdev.\n"); - return -ENOMEM; - } - - private->fb_helper = helper = &fbdev->drm_fb_helper; - helper->funcs = &exynos_drm_fb_helper_funcs; - - num_crtc = dev->mode_config.num_crtc; - - ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); - if (ret < 0) { - DRM_ERROR("failed to initialize drm fb helper.\n"); - goto err_init; - } - - ret = drm_fb_helper_single_add_all_connectors(helper); - if (ret < 0) { - DRM_ERROR("failed to register drm_fb_helper_connector.\n"); - goto err_setup; - - } - - ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); - if (ret < 0) { - DRM_ERROR("failed to set up hw configuration.\n"); - goto err_setup; - } - - return 0; - -err_setup: - drm_fb_helper_fini(helper); - -err_init: - private->fb_helper = NULL; - kfree(fbdev); - - return ret; -} - -static void exynos_drm_fbdev_destroy(struct drm_device *dev, - struct drm_fb_helper *fb_helper) -{ - struct drm_framebuffer *fb; - - /* release drm framebuffer and real buffer */ - if (fb_helper->fb && fb_helper->fb->funcs) { - fb = fb_helper->fb; - if (fb && fb->funcs->destroy) - fb->funcs->destroy(fb); - } - - /* release linux framebuffer */ - if (fb_helper->fbdev) { - struct fb_info *info; - int ret; - - info = fb_helper->fbdev; - ret = unregister_framebuffer(info); - if (ret < 0) - DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); - - if (info->cmap.len) - fb_dealloc_cmap(&info->cmap); - - framebuffer_release(info); - } - - drm_fb_helper_fini(fb_helper); -} - -void exynos_drm_fbdev_fini(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - struct exynos_drm_fbdev *fbdev; - - if (!private || !private->fb_helper) - return; - - fbdev = to_exynos_fbdev(private->fb_helper); - - if (fbdev->exynos_gem_obj) - exynos_drm_gem_destroy(fbdev->exynos_gem_obj); - - exynos_drm_fbdev_destroy(dev, private->fb_helper); - kfree(fbdev); - private->fb_helper = NULL; -} - -void exynos_drm_fbdev_restore_mode(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - - if (!private || !private->fb_helper) - return; - - drm_fb_helper_restore_fbdev_mode(private->fb_helper); -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_fbdev.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_fbdev.h deleted file mode 100644 index ccfce8a..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_fbdev.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_FBDEV_H_ -#define _EXYNOS_DRM_FBDEV_H_ - -int exynos_drm_fbdev_init(struct drm_device *dev); -int exynos_drm_fbdev_reinit(struct drm_device *dev); -void exynos_drm_fbdev_fini(struct drm_device *dev); -void exynos_drm_fbdev_restore_mode(struct drm_device *dev); - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_fimd.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_fimd.c deleted file mode 100644 index 6ec2a61..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_fimd.c +++ /dev/null @@ -1,1700 +0,0 @@ -/* exynos_drm_fimd.c - * - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Joonyoung Shim <jy0922.shim@samsung.com> - * Inki Dae <inki.dae@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ -#include "drmP.h" -#include "drm_backlight.h" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/cma.h> - -#include <drm/exynos_drm.h> -#include <plat/regs-fb-v4.h> - -#include <plat/fimd_lite_ext.h> - -#include <mach/map.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_fbdev.h" -#include "exynos_drm_crtc.h" - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ -#include <linux/devfreq/exynos4_display.h> -#endif - -#ifdef CONFIG_MACH_SLP_NAPLES -#define VIDCON0_DSI_DISABLE (0 << 30) -#define VIDCON0_DSI_ENABLE (1 << 30) - -/* I80/RGB Trigger control */ -#define TRIGCON (0x01A4) -/* I80 interface control 0 for main LDI */ -#define I80IFCONA0 (0x01B0) -#define I80IFCONA1 (0x01B4) -#define I80IFCONB0 (0x01B8) -#define I80IFCONB1 (0x01B8) - -/* I80IFCONA0 and I80IFCONA1 */ -#define LCD_CS_SETUP(x) (((x) & 0xf) << 16) -#define LCD_WR_SETUP(x) (((x) & 0xf) << 12) -#define LCD_WR_ACT(x) (((x) & 0xf) << 8) -#define LCD_WR_HOLD(x) (((x) & 0xf) << 4) -#define RSPOL_LOW (0 << 2) -#define RSPOL_HIGH (1 << 2) -#define I80IFEN_DISABLE (0 << 0) -#define I80IFEN_ENABLE (1 << 0) - -enum s3cfb_cpu_auto_cmd_rate { - DISABLE_AUTO_FRM, - PER_TWO_FRM, - PER_FOUR_FRM, - PER_SIX_FRM, - PER_EIGHT_FRM, - PER_TEN_FRM, - PER_TWELVE_FRM, - PER_FOURTEEN_FRM, - PER_SIXTEEN_FRM, - PER_EIGHTEEN_FRM, - PER_TWENTY_FRM, - PER_TWENTY_TWO_FRM, - PER_TWENTY_FOUR_FRM, - PER_TWENTY_SIX_FRM, - PER_TWENTY_EIGHT_FRM, - PER_THIRTY_FRM, -}; -#endif -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB -#include <plat/fimc.h> -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ -#include <plat/pd.h> -#include <linux/pm_qos_params.h> -#endif -#define FIMD_GET_LCD_WIDTH _IOR('F', 302, int) -#define FIMD_GET_LCD_HEIGHT _IOR('F', 303, int) -#define FIMD_SET_WRITEBACK _IOW('F', 304, u32) -#endif - -/* - * FIMD is stand for Fully Interactive Mobile Display and - * as a display controller, it transfers contents drawn on memory - * to a LCD Panel through Display Interfaces such as RGB or - * CPU Interface. - */ - -/* position control register for hardware window 0, 2 ~ 4.*/ -#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) -#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) -/* size control register for hardware window 0. */ -#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08) -/* alpha control register for hardware window 1 ~ 4. */ -#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16) -/* size control register for hardware window 1 ~ 4. */ -#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) - -#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) -#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) -#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) - -/* color key control register for hardware window 1 ~ 4. */ -#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8)) -/* color key value register for hardware window 1 ~ 4. */ -#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8)) - -/* FIMD has totally five hardware windows. */ -#define WINDOWS_NR 5 - -#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) - -static struct s5p_fimd_ext_device *fimd_lite_dev, *mdnie; -static struct s5p_fimd_dynamic_refresh *fimd_refresh; - -struct fimd_notifier_block { - struct list_head list; - void *data; - int (*client_notifier)(unsigned int val, void *data); -}; - -static LIST_HEAD(fimd_notifier_list); -static DEFINE_MUTEX(fimd_notifier_lock); - -struct fimd_win_data { - unsigned int offset_x; - unsigned int offset_y; - unsigned int ovl_width; - unsigned int ovl_height; - unsigned int fb_width; - unsigned int fb_height; - unsigned int bpp; - dma_addr_t dma_addr; - void __iomem *vaddr; - unsigned int buf_offsize; - unsigned int line_size; /* bytes */ - bool enabled; -}; - -struct fimd_context { - struct exynos_drm_subdrv subdrv; - int irq; - struct drm_crtc *crtc; - struct clk *bus_clk; - struct clk *lcd_clk; - struct resource *regs_res; - void __iomem *regs; - struct fimd_win_data win_data[WINDOWS_NR]; - unsigned int clkdiv; - unsigned int default_win; - unsigned long irq_flags; - u32 vidcon0; - u32 vidcon1; - bool suspended; - struct mutex lock; - - struct exynos_drm_panel_info *panel; - unsigned int high_freq; - unsigned int dynamic_refresh; - struct notifier_block nb_exynos_display; - - struct work_struct work; - bool errata; -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB - struct notifier_block nb_ctrl; -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - struct pm_qos_request_list pm_qos; -#endif -#endif -}; -#ifdef CONFIG_MACH_SLP_NAPLES -struct fimd_context *ctx_global; -#endif - -static bool fimd_display_is_connected(struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ - - return true; -} - -static void *fimd_get_panel(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return ctx->panel; -} - -static int fimd_check_timing(struct device *dev, void *timing) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ - - return 0; -} - -static int fimd_display_power_on(struct device *dev, int mode) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO */ - - drm_bl_dpms(mode); - - return 0; -} - -static struct exynos_drm_display_ops fimd_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_LCD, - .is_connected = fimd_display_is_connected, - .get_panel = fimd_get_panel, - .check_timing = fimd_check_timing, - .power_on = fimd_display_power_on, -}; - -static void exynos_drm_mdnie_mode_stop(struct fimd_context *ctx) -{ - struct s5p_fimd_ext_driver *fimd_lite_drv; - u32 cfg; - - fimd_lite_drv = to_fimd_ext_driver(fimd_lite_dev->dev.driver); - - /* set dualrgb register to mDNIe mode. */ - cfg = readl(ctx->regs + DUALRGB); - cfg &= ~(0x3 << 0); - writel(cfg, ctx->regs + DUALRGB); - msleep(20); - - /* change display path. */ - cfg = readl(S3C_VA_SYS + 0x210); - cfg |= 1 << 1; - writel(cfg, S3C_VA_SYS + 0x210); - - if (fimd_lite_drv->stop) - fimd_lite_drv->stop(fimd_lite_dev); - - if (fimd_lite_drv->setup) - fimd_lite_drv->setup(fimd_lite_dev, 0); - - /* clock off */ - if (fimd_lite_drv->power_off) - fimd_lite_drv->power_off(fimd_lite_dev); - - fimd_lite_dev->enabled = false; -} - -static void exynos_drm_set_mdnie_mode(struct fimd_context *ctx) -{ - u32 cfg; - - /* change display path. */ - cfg = readl(S3C_VA_SYS + 0x210); - /* MIE_LBLK0 is mDNIe. */ - cfg |= 1 << 0; - /* FIMDBYPASS_LBLK0 is MIE/mDNIe. */ - cfg &= ~(1 << 1); - writel(cfg, S3C_VA_SYS + 0x210); - - /* all polarity values should be 0 for mDNIe. */ - cfg = readl(ctx->regs + VIDCON1); - cfg &= ~(VIDCON1_INV_VCLK | VIDCON1_INV_HSYNC | - VIDCON1_INV_VSYNC | VIDCON1_INV_VDEN | - VIDCON1_VCLK_MASK); - - writel(cfg, ctx->regs + VIDCON1); - - /* set dualrgb register to mDNIe mode. */ - cfg = readl(ctx->regs + DUALRGB); - cfg &= ~(0x3 << 0); - cfg |= 0x3 << 0; - writel(cfg, ctx->regs + DUALRGB); -} - -static int exynos_drm_change_to_mdnie(struct fimd_context *ctx) -{ - u32 cfg; - struct s5p_fimd_ext_driver *mdnie_drv, *fimd_lite_drv; - - mdnie_drv = to_fimd_ext_driver(mdnie->dev.driver); - fimd_lite_drv = to_fimd_ext_driver(fimd_lite_dev->dev.driver); - - /** - * path change sequence for mDNIe. - * - * 1. FIMD-LITE DMA stop. - * 2. FIMD DMA stop. - * 3. change DISPLAY_CONTROL and DUALRGB registers to mDNIe mode. - * 4. change FIMD VCLKFREE to freerun mode. - * 5. initialize mDNIe module. - * 6. initialize FIMD-LITE module. - * 7. FIMD-LITE logic start. - * 8. FIMD-LITE DMA start. - * 9. FIMD DMA start. - * - * ps. FIMD polarity values should be 0. - * lcd polarity values should be set to FIMD-LITE. - * FIMD and FIMD-LITE DMA should be started at same time. - */ - /* set fimd to mDNIe mode.(WB/mDNIe) */ - exynos_drm_set_mdnie_mode(ctx); - - /* enable FIMD-LITE. clk */ - if (fimd_lite_drv && fimd_lite_drv->power_on) - fimd_lite_drv->power_on(fimd_lite_dev); - - /* setup mDNIe. */ - if (mdnie_drv) - mdnie_drv->setup(mdnie, 1); - - /* setup FIMD-LITE. */ - if (fimd_lite_drv) - fimd_lite_drv->setup(fimd_lite_dev, 1); - - cfg = readl(ctx->regs + VIDCON0); - cfg |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(cfg, ctx->regs + VIDCON0); - - if (fimd_lite_drv->start) - fimd_lite_drv->start(fimd_lite_dev); - return 0; -} - -static void fimd_dpms(struct device *subdrv_dev, int mode) -{ - struct fimd_context *ctx = get_fimd_context(subdrv_dev); - - DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); - - mutex_lock(&ctx->lock); - - switch (mode) { - case DRM_MODE_DPMS_ON: - /* - * enable fimd hardware only if suspended status. - * - * P.S. fimd_dpms function would be called at booting time so - * clk_enable could be called double time. - */ - if (ctx->suspended) - pm_runtime_get_sync(subdrv_dev); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - if (!ctx->suspended) - pm_runtime_put_sync(subdrv_dev); - break; - default: - DRM_DEBUG_KMS("unspecified mode %d\n", mode); - break; - } - - mutex_unlock(&ctx->lock); -} - -static void fimd_apply(struct device *subdrv_dev) -{ - struct fimd_context *ctx = get_fimd_context(subdrv_dev); - struct exynos_drm_manager *mgr = ctx->subdrv.manager; - struct exynos_drm_manager_ops *mgr_ops = mgr->ops; - struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; - struct fimd_win_data *win_data; - int i; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - for (i = 0; i < WINDOWS_NR; i++) { - win_data = &ctx->win_data[i]; - if (win_data->enabled && (ovl_ops && ovl_ops->commit)) - ovl_ops->commit(subdrv_dev, i); - } - - if (mgr_ops && mgr_ops->commit) - mgr_ops->commit(subdrv_dev); -} - -#ifdef CONFIG_MACH_SLP_NAPLES -void _fimd_set_auto_cmd_rate(struct fimd_context *ctx, unsigned char cmd_rate) -{ - unsigned int cmd_rate_val; - unsigned int i80_if_con_reg_val; - - cmd_rate_val = (cmd_rate == DISABLE_AUTO_FRM) ? (0x0 << 0) : - (cmd_rate == PER_TWO_FRM) ? (0x1 << 0) : - (cmd_rate == PER_FOUR_FRM) ? (0x2 << 0) : - (cmd_rate == PER_SIX_FRM) ? (0x3 << 0) : - (cmd_rate == PER_EIGHT_FRM) ? (0x4 << 0) : - (cmd_rate == PER_TEN_FRM) ? (0x5 << 0) : - (cmd_rate == PER_TWELVE_FRM) ? (0x6 << 0) : - (cmd_rate == PER_FOURTEEN_FRM) ? (0x7 << 0) : - (cmd_rate == PER_SIXTEEN_FRM) ? (0x8 << 0) : - (cmd_rate == PER_EIGHTEEN_FRM) ? (0x9 << 0) : - (cmd_rate == PER_TWENTY_FRM) ? (0xa << 0) : - (cmd_rate == PER_TWENTY_TWO_FRM) ? (0xb << 0) : - (cmd_rate == PER_TWENTY_FOUR_FRM) ? (0xc << 0) : - (cmd_rate == PER_TWENTY_SIX_FRM) ? (0xd << 0) : - (cmd_rate == PER_TWENTY_EIGHT_FRM) ? (0xe << 0) : (0xf << 0); - - i80_if_con_reg_val = readl(ctx->regs + I80IFCONB0); - i80_if_con_reg_val &= ~(0xf << 0); - i80_if_con_reg_val |= cmd_rate_val; - writel(i80_if_con_reg_val, ctx->regs + I80IFCONB0); -} -#endif -static void fimd_commit(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - u32 val; - - if (ctx->suspended) - return; - - DRM_DEBUG_KMS("%s\n", __FILE__); - -#ifdef CONFIG_MACH_SLP_NAPLES - /* setup horizontal and vertical display size. */ - val = VIDTCON2_LINEVAL(timing->yres - 1) | - VIDTCON2_HOZVAL(timing->xres - 1); - writel(val, ctx->regs + VIDTCON2); - - /* cpu_interface_timing */ - val = readl(ctx->regs + I80IFCONA0); - val = LCD_CS_SETUP(0) | - LCD_WR_SETUP(0) | - LCD_WR_ACT(1) | - LCD_WR_HOLD(0) | - RSPOL_LOW | /* in case of LCD MIPI module */ - I80IFEN_ENABLE; - writel(val, ctx->regs + I80IFCONA0); - - _fimd_set_auto_cmd_rate(ctx, DISABLE_AUTO_FRM); - - /* setup clock source, clock divider, enable dma. */ - val = ctx->vidcon0; - val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (ctx->clkdiv > 1) - val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) - | VIDCON0_CLKDIR; - else - val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* - * fields of register with prefix '_F' would be updated - * at vsync(same as dma start) - */ - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(val, ctx->regs + VIDCON0); - - /* - * Workaround: After power domain is turned off - * then when it is turned on, this needs. - */ - val &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); - writel(val, ctx->regs + VIDCON0); - - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(val, ctx->regs + VIDCON0); -#else - /* setup polarity values from machine code. */ - writel(ctx->vidcon1, ctx->regs + VIDCON1); - - /* setup vertical timing values. */ - val = VIDTCON0_VBPD(timing->upper_margin - 1) | - VIDTCON0_VFPD(timing->lower_margin - 1) | - VIDTCON0_VSPW(timing->vsync_len - 1); - writel(val, ctx->regs + VIDTCON0); - - /* setup horizontal timing values. */ - val = VIDTCON1_HBPD(timing->left_margin - 1) | - VIDTCON1_HFPD(timing->right_margin - 1) | - VIDTCON1_HSPW(timing->hsync_len - 1); - writel(val, ctx->regs + VIDTCON1); - - /* setup horizontal and vertical display size. */ - val = VIDTCON2_LINEVAL(timing->yres - 1) | - VIDTCON2_HOZVAL(timing->xres - 1); - writel(val, ctx->regs + VIDTCON2); - - /* setup clock source, clock divider, enable dma. */ - val = ctx->vidcon0; - val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (ctx->clkdiv > 1) - val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; - else - val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* - * fields of register with prefix '_F' would be updated - * at vsync(same as dma start) - */ - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(val, ctx->regs + VIDCON0); - - /* - * fix fimd errata with mDNIe. - * - * this code fixes a issue that mDNIe unfunctions properly - * when fimd power off goes to on. this issue is because dma - * is enabled two times with setcrtc call once a process is - * ternimated(at this thime, fimd goes to on from off for back - * to console fb) so this condition would avoid the situation. - */ - if (!ctx->errata) { - /* - * Workaround: After power domain is turned off then - * when it is turned on, this needs. - */ - val &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); - writel(val, ctx->regs + VIDCON0); - - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; - writel(val, ctx->regs + VIDCON0); - - ctx->errata = true; - } -#endif -} - -static int fimd_enable_vblank(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return -EPERM; - - if (!test_and_set_bit(0, &ctx->irq_flags)) { - val = readl(ctx->regs + VIDINTCON0); - - val |= VIDINTCON0_INT_ENABLE; - val |= VIDINTCON0_INT_FRAME; - - val &= ~VIDINTCON0_FRAMESEL0_MASK; - val |= VIDINTCON0_FRAMESEL0_VSYNC; - val &= ~VIDINTCON0_FRAMESEL1_MASK; - val |= VIDINTCON0_FRAMESEL1_NONE; - - writel(val, ctx->regs + VIDINTCON0); - } - - return 0; -} - -static void fimd_disable_vblank(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; - - if (test_and_clear_bit(0, &ctx->irq_flags)) { - val = readl(ctx->regs + VIDINTCON0); - - val &= ~VIDINTCON0_INT_FRAME; - val &= ~VIDINTCON0_INT_ENABLE; - - writel(val, ctx->regs + VIDINTCON0); - } -} - -static struct exynos_drm_manager_ops fimd_manager_ops = { - .dpms = fimd_dpms, - .apply = fimd_apply, - .commit = fimd_commit, - .enable_vblank = fimd_enable_vblank, - .disable_vblank = fimd_disable_vblank, -}; - -static void fimd_win_mode_set(struct device *dev, - struct exynos_drm_overlay *overlay) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data; - int win; - unsigned long offset; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!overlay) { - dev_err(dev, "overlay is NULL\n"); - return; - } - - win = overlay->zpos; - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - offset = overlay->fb_x * (overlay->bpp >> 3); - offset += overlay->fb_y * overlay->pitch; - - DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); - - win_data = &ctx->win_data[win]; - - win_data->offset_x = overlay->crtc_x; - win_data->offset_y = overlay->crtc_y; - win_data->ovl_width = overlay->crtc_width; - win_data->ovl_height = overlay->crtc_height; - win_data->fb_width = overlay->fb_width; - win_data->fb_height = overlay->fb_height; - win_data->dma_addr = overlay->dma_addr[0] + offset; - win_data->vaddr = overlay->vaddr[0] + offset; - win_data->bpp = overlay->bpp; - win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * - (overlay->bpp >> 3); - win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); - - DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", - win_data->offset_x, win_data->offset_y); - DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - win_data->ovl_width, win_data->ovl_height); - DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", - (unsigned long)win_data->dma_addr, - (unsigned long)win_data->vaddr); - DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", - overlay->fb_width, overlay->crtc_width); -} - -static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data = &ctx->win_data[win]; - unsigned long val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - val = WINCONx_ENWIN; - - switch (win_data->bpp) { - case 1: - val |= WINCON0_BPPMODE_1BPP; - val |= WINCONx_BITSWP; - val |= WINCONx_BURSTLEN_4WORD; - break; - case 2: - val |= WINCON0_BPPMODE_2BPP; - val |= WINCONx_BITSWP; - val |= WINCONx_BURSTLEN_8WORD; - break; - case 4: - val |= WINCON0_BPPMODE_4BPP; - val |= WINCONx_BITSWP; - val |= WINCONx_BURSTLEN_8WORD; - break; - case 8: - val |= WINCON0_BPPMODE_8BPP_PALETTE; - val |= WINCONx_BURSTLEN_8WORD; - val |= WINCONx_BYTSWP; - break; - case 16: - val |= WINCON0_BPPMODE_16BPP_565; - val |= WINCONx_HAWSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - case 24: - val |= WINCON0_BPPMODE_24BPP_888; - val |= WINCONx_WSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - case 32: - val |= WINCON1_BPPMODE_28BPP_A4888 - | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; - val |= WINCONx_WSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - default: - DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); - - val |= WINCON0_BPPMODE_24BPP_888; - val |= WINCONx_WSWP; - val |= WINCONx_BURSTLEN_16WORD; - break; - } - - DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); - - writel(val, ctx->regs + WINCON(win)); -} - -static void fimd_win_set_colkey(struct device *dev, unsigned int win) -{ - struct fimd_context *ctx = get_fimd_context(dev); - unsigned int keycon0 = 0, keycon1 = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | - WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); - - keycon1 = WxKEYCON1_COLVAL(0xffffffff); - - writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); - writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); -} - -static void fimd_win_commit(struct device *dev, int zpos) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data; - int win = zpos; - unsigned long val, alpha, size; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; - - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - win_data = &ctx->win_data[win]; - - /* - * SHADOWCON register is used for enabling timing. - * - * for example, once only width value of a register is set, - * if the dma is started then fimd hardware could malfunction so - * with protect window setting, the register fields with prefix '_F' - * wouldn't be updated at vsync also but updated once unprotect window - * is set. - */ - - /* protect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - /* buffer start address */ - val = (unsigned long)win_data->dma_addr; - writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); - - /* buffer end address */ - size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); - val = (unsigned long)(win_data->dma_addr + size); - writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); - - DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", - (unsigned long)win_data->dma_addr, val, size); - DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - win_data->ovl_width, win_data->ovl_height); - - /* buffer size */ - val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | - VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size); - writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); - - /* OSD position */ - val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | - VIDOSDxA_TOPLEFT_Y(win_data->offset_y); - writel(val, ctx->regs + VIDOSD_A(win)); - - val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x + - win_data->ovl_width - 1) | - VIDOSDxB_BOTRIGHT_Y(win_data->offset_y + - win_data->ovl_height - 1); - writel(val, ctx->regs + VIDOSD_B(win)); - - DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", - win_data->offset_x, win_data->offset_y, - win_data->offset_x + win_data->ovl_width - 1, - win_data->offset_y + win_data->ovl_height - 1); - - /* hardware window 0 doesn't support alpha channel. */ - if (win != 0) { - /* OSD alpha */ - alpha = VIDISD14C_ALPHA1_R(0xf) | - VIDISD14C_ALPHA1_G(0xf) | - VIDISD14C_ALPHA1_B(0xf); - - writel(alpha, ctx->regs + VIDOSD_C(win)); - } - - /* OSD size */ - if (win != 3 && win != 4) { - u32 offset = VIDOSD_D(win); - if (win == 0) - offset = VIDOSD_C_SIZE_W0; - val = win_data->ovl_width * win_data->ovl_height; - writel(val, ctx->regs + offset); - - DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); - } - - fimd_win_set_pixfmt(dev, win); - - /* hardware window 0 doesn't support color key. */ - if (win != 0) - fimd_win_set_colkey(dev, win); - - /* wincon */ - val = readl(ctx->regs + WINCON(win)); - val |= WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); - - /* Enable DMA channel and unprotect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_CHx_ENABLE(win); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - win_data->enabled = true; -} - -static void fimd_win_disable(struct device *dev, int zpos) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct fimd_win_data *win_data; - int win = zpos; - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - win_data = &ctx->win_data[win]; - - /* protect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - /* wincon */ - val = readl(ctx->regs + WINCON(win)); - val &= ~WINCONx_ENWIN; - writel(val, ctx->regs + WINCON(win)); - - /* unprotect windows */ - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_CHx_ENABLE(win); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); - - win_data->enabled = false; -} - -static struct exynos_drm_overlay_ops fimd_overlay_ops = { - .mode_set = fimd_win_mode_set, - .commit = fimd_win_commit, - .disable = fimd_win_disable, -}; - -static struct exynos_drm_manager fimd_manager = { - .pipe = -1, - .ops = &fimd_manager_ops, - .overlay_ops = &fimd_overlay_ops, - .display_ops = &fimd_display_ops, -}; - -static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) -{ - struct exynos_drm_private *dev_priv = drm_dev->dev_private; - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned long flags; - bool is_checked = false; - - spin_lock_irqsave(&drm_dev->event_lock, flags); - - list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, - base.link) { - /* if event's pipe isn't same as crtc then ignore it. */ - if (crtc != e->pipe) - continue; - - is_checked = true; - - do_gettimeofday(&now); - e->event.sequence = 0; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; - - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - } - - if (is_checked) { - /* - * call drm_vblank_put only in case that drm_vblank_get was - * called. - */ - if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) - drm_vblank_put(drm_dev, crtc); - - /* - * don't off vblank if vblank_disable_allowed is 1, - * because vblank would be off by timer handler. - */ - if (!drm_dev->vblank_disable_allowed) - drm_vblank_off(drm_dev, crtc); - } - - spin_unlock_irqrestore(&drm_dev->event_lock, flags); -} - -static void exynos_fimd_schedule_work(struct work_struct *work) -{ - struct fimd_context *ctx = container_of(work, struct fimd_context, - work); - u32 ret; - - /* Change mdnie mode after irq handler */ - if (mdnie && fimd_lite_dev) { - if (!fimd_lite_dev->enabled) { - while (1) { - ret = (__raw_readl(ctx->regs + VIDCON1)) & - VIDCON1_VSTATUS_MASK; - if (ret == VIDCON1_VSTATUS_BACKPORCH) { - exynos_drm_change_to_mdnie(ctx); - fimd_lite_dev->enabled = true; - break; - } - } - } - } - -} - -#ifdef CONFIG_MACH_SLP_NAPLES -void fimd_set_trigger(void) -{ - u32 reg = 0; - struct exynos_drm_subdrv *subdrv = &ctx_global->subdrv; - struct drm_device *drm_dev = subdrv->drm_dev; - struct exynos_drm_manager *manager = subdrv->manager; - u32 val; - - val = readl(ctx_global->regs + VIDINTCON1); - - if (val & VIDINTCON1_INT_FRAME) - /* VSYNC interrupt */ - writel(VIDINTCON1_INT_FRAME, ctx_global->regs + VIDINTCON1); - - /* check the crtc is detached already from encoder */ - if (manager->pipe < 0) - return; - - drm_handle_vblank(drm_dev, manager->pipe); - fimd_finish_pageflip(drm_dev, manager->pipe); - - schedule_work(&ctx_global->work); - - reg = readl(ctx_global->regs + TRIGCON); - - reg |= 1 << 0 | 1 << 1; - - writel(reg, ctx_global->regs + TRIGCON); -} -#endif -static irqreturn_t fimd_irq_handler(int irq, void *dev_id) -{ - struct fimd_context *ctx = (struct fimd_context *)dev_id; - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct drm_device *drm_dev = subdrv->drm_dev; - struct exynos_drm_manager *manager = subdrv->manager; - u32 val; - - val = readl(ctx->regs + VIDINTCON1); - - if (val & VIDINTCON1_INT_FRAME) - /* VSYNC interrupt */ - writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); - - /* check the crtc is detached already from encoder */ - if (manager->pipe < 0) - goto out; - - drm_handle_vblank(drm_dev, manager->pipe); - fimd_finish_pageflip(drm_dev, manager->pipe); - - schedule_work(&ctx->work); - -out: - return IRQ_HANDLED; -} - -static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * enable drm irq mode. - * - with irq_enabled = 1, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - drm_dev->irq_enabled = 1; - - /* - * with vblank_disable_allowed = 1, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) - */ - drm_dev->vblank_disable_allowed = 1; - - return 0; -} - -static void fimd_subdrv_remove(struct drm_device *drm_dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ -} - -static int fimd_calc_clkdiv(struct fimd_context *ctx, - struct fb_videomode *timing) -{ - unsigned long clk = clk_get_rate(ctx->lcd_clk); - u32 retrace; - u32 clkdiv; - u32 best_framerate = 0; - u32 framerate; - - DRM_DEBUG_KMS("%s\n", __FILE__); - -#ifdef CONFIG_MACH_SLP_NAPLES - retrace = timing->xres * timing->yres * 2; -#else - retrace = timing->left_margin + timing->hsync_len + - timing->right_margin + timing->xres; - retrace *= timing->upper_margin + timing->vsync_len + - timing->lower_margin + timing->yres; -#endif - - /* default framerate is 60Hz */ - if (!timing->refresh) - timing->refresh = 60; - - clk /= retrace; - - for (clkdiv = 1; clkdiv < 0x100; clkdiv++) { - int tmp; - - /* get best framerate */ - framerate = clk / clkdiv; - tmp = timing->refresh - framerate; - if (tmp < 0) { - best_framerate = framerate; - continue; - } else { - if (!best_framerate) - best_framerate = framerate; - else if (tmp < (best_framerate - framerate)) - best_framerate = framerate; - break; - } - } - - return clkdiv; -} - -static void fimd_clear_win(struct fimd_context *ctx, int win) -{ - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - writel(0, ctx->regs + WINCON(win)); - writel(0, ctx->regs + VIDOSD_A(win)); - writel(0, ctx->regs + VIDOSD_B(win)); - writel(0, ctx->regs + VIDOSD_C(win)); - - if (win == 1 || win == 2) - writel(0, ctx->regs + VIDOSD_D(win)); - - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); -} - -int fimd_register_client(int (*client_notifier)(unsigned int val, void *data), - void *data) -{ - struct fimd_notifier_block *fimd_block; - - fimd_block = kzalloc(sizeof(*fimd_block), GFP_KERNEL); - if (!fimd_block) { - printk(KERN_ERR "failed to allocate fimd_notifier_block\n"); - return -ENOMEM; - } - - fimd_block->client_notifier = client_notifier; - fimd_block->data = data; - - mutex_lock(&fimd_notifier_lock); - list_add_tail(&fimd_block->list, &fimd_notifier_list); - mutex_unlock(&fimd_notifier_lock); - - return 0; -} -EXPORT_SYMBOL(fimd_register_client); - -void fimd_unregister_client(int (*client_notifier)(unsigned int val, - void *data)) -{ - struct fimd_notifier_block *fimd_block; - - mutex_lock(&fimd_notifier_lock); - list_for_each_entry(fimd_block, &fimd_notifier_list, list) { - if (!fimd_block) - continue; - - if (fimd_block->client_notifier == client_notifier) { - list_del(&fimd_block->list); - kfree(fimd_block); - fimd_block = NULL; - break; - } - } - mutex_unlock(&fimd_notifier_lock); -} -EXPORT_SYMBOL(fimd_unregister_client); - -static int fimd_notifier_call_chain(void) -{ - struct fimd_notifier_block *fimd_block; - - mutex_lock(&fimd_notifier_lock); - list_for_each_entry(fimd_block, &fimd_notifier_list, list) { - if (fimd_block && fimd_block->client_notifier) - fimd_block->client_notifier(0, fimd_block->data); - } - mutex_unlock(&fimd_notifier_lock); - - return 0; -} - -static int fimd_power_on(struct fimd_context *ctx, bool enable) -{ - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->dev; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (enable != false && enable != true) - return -EINVAL; - - if (enable) { - int ret; - - /* fimd power should be off to clear mipi-dsi fifo. */ - fimd_notifier_call_chain(); - - ret = clk_enable(ctx->bus_clk); - if (ret < 0) - return ret; - - ret = clk_enable(ctx->lcd_clk); - if (ret < 0) { - clk_disable(ctx->bus_clk); - return ret; - } - - ctx->suspended = false; - - /* if vblank was enabled status, enable it again. */ - if (test_and_clear_bit(0, &ctx->irq_flags)) - fimd_enable_vblank(dev); - - fimd_apply(dev); - } else { - if (fimd_lite_dev) - exynos_drm_mdnie_mode_stop(ctx); - - clk_disable(ctx->lcd_clk); - clk_disable(ctx->bus_clk); - - ctx->suspended = true; - ctx->errata = false; - } - - return 0; -} - -static void exynos_drm_change_clock(struct fimd_context *ctx) -{ - unsigned int cfg = 0; - struct s5p_fimd_ext_driver *fimd_lite_drv; - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - - fimd_lite_drv = to_fimd_ext_driver(fimd_lite_dev->dev.driver); - - if (!ctx->dynamic_refresh) { - timing->refresh = 60; - ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); -#ifdef CONFIG_LCD_S6E8AA0 - /* workaround: To apply dynamic refresh rate */ - s6e8aa0_panel_cond(1); -#endif - if (fimd_lite_dev && fimd_lite_dev->enabled) { - fimd_refresh->clkdiv = ctx->clkdiv; - fimd_lite_drv->change_clock(fimd_refresh, - fimd_lite_dev); - } else { - cfg = readl(ctx->regs + VIDCON0); - cfg &= ~VIDCON0_CLKVAL_F(0xFF); - cfg |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1); - writel(cfg, ctx->regs + VIDCON0); - } - } else { - ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); -#ifdef CONFIG_LCD_S6E8AA0 - /* workaround: To apply dynamic refresh rate */ - s6e8aa0_panel_cond(ctx->high_freq); -#endif - if (fimd_lite_dev && fimd_lite_dev->enabled) { - fimd_refresh->clkdiv = ctx->clkdiv; - fimd_lite_drv->change_clock(fimd_refresh, - fimd_lite_dev); - } else { - cfg = readl(ctx->regs + VIDCON0); - cfg &= ~VIDCON0_CLKVAL_F(0xFF); - cfg |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1); - writel(cfg, ctx->regs + VIDCON0); - } - } -} - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ -static int exynos_display_notifier_callback(struct notifier_block *this, - unsigned long event, void *_data) -{ - struct fimd_context *ctx - = container_of(this, struct fimd_context, nb_exynos_display); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - - if (ctx->suspended) - return NOTIFY_DONE; - - switch (event) { - case EXYNOS4_DISPLAY_LV_HF: - timing->refresh = EXYNOS4_DISPLAY_LV_HF; - ctx->high_freq = 1; - break; - case EXYNOS4_DISPLAY_LV_LF: - timing->refresh = EXYNOS4_DISPLAY_LV_LF; - ctx->high_freq = 0; - break; - default: - return NOTIFY_BAD; - } - - exynos_drm_change_clock(ctx); - - return NOTIFY_DONE; -} -#endif - -static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - unsigned long refresh; - int ret; - - if (ctx->dynamic_refresh) { - ret = kstrtoul(buf, 0, &refresh); - timing->refresh = refresh; - if (refresh == 60) - ctx->high_freq = 1; - else - ctx->high_freq = 0; - - exynos_drm_change_clock(ctx); - } - - return count; -} - -static ssize_t show_refresh(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - - return snprintf(buf, PAGE_SIZE, "%d\n", timing->refresh); -} - -static struct device_attribute device_attrs[] = { - __ATTR(refresh, S_IRUGO|S_IWUSR, show_refresh, store_refresh), -}; - -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB -static void fimd_set_writeback(struct fimd_context *ctx, int enable) -{ - u32 vidcon0 = readl(ctx->regs + VIDCON0); - u32 vidcon2 = readl(ctx->regs + VIDCON2); - - vidcon0 &= ~VIDCON0_VIDOUT_MASK; - vidcon2 &= ~(VIDCON2_WB_MASK | - VIDCON2_WB_SKIP_MASK | - VIDCON2_TVFORMATSEL_HW_SW_MASK | - VIDCON2_TVFORMATSEL_MASK); - - if (enable) { - vidcon0 |= VIDCON0_VIDOUT_WB; - vidcon2 |= (VIDCON2_WB_ENABLE | - VIDCON2_TVFORMATSEL_SW | - VIDCON2_TVFORMATSEL_YUV444); - } else { - vidcon0 |= VIDCON0_VIDOUT_RGB; - vidcon2 |= VIDCON2_WB_DISABLE; - } - - writel(vidcon0, ctx->regs + VIDCON0); - writel(vidcon2, ctx->regs + VIDCON2); -} - -static int fimd_notifier_ctrl(struct notifier_block *this, - unsigned long event, void *_data) -{ - struct fimd_context *ctx = container_of(this, - struct fimd_context, nb_ctrl); - - switch (event) { - case FIMD_GET_LCD_WIDTH: { - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - int *width = (int *)_data; - - *width = timing->xres; - } - break; - case FIMD_GET_LCD_HEIGHT: { - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; - int *height = (int *)_data; - - *height = timing->yres; - } - break; - case FIMD_SET_WRITEBACK: { - unsigned int refresh; - int *enable = (int *)&_data; - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - if (*enable) - refresh = EXYNOS4_DISPLAY_LV_HF; - else - refresh = EXYNOS4_DISPLAY_LV_LF; - pm_qos_update_request(&ctx->pm_qos, - refresh); -#endif - fimd_set_writeback(ctx, *enable); - } - break; - default: - /* ToDo : for checking use case */ - DRM_INFO("%s:event[0x%x]\n", __func__, (unsigned int)event); - break; - } - - return NOTIFY_DONE; -} -#endif - -static int __devinit fimd_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct fimd_context *ctx; - struct exynos_drm_subdrv *subdrv; - struct exynos_drm_fimd_pdata *pdata; - struct exynos_drm_panel_info *panel; - struct resource *res; - int win; - int i; - int ret = -EINVAL; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - - panel = &pdata->panel; - if (!panel) { - dev_err(dev, "panel is null.\n"); - return -EINVAL; - } - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->bus_clk = clk_get(dev, "lcd"); - if (IS_ERR(ctx->bus_clk)) { - dev_err(dev, "failed to get bus clock\n"); - ret = PTR_ERR(ctx->bus_clk); - goto err_clk_get; - } - - clk_enable(ctx->bus_clk); - - ctx->lcd_clk = clk_get(dev, "sclk_fimd"); - if (IS_ERR(ctx->lcd_clk)) { - dev_err(dev, "failed to get lcd clock\n"); - ret = PTR_ERR(ctx->lcd_clk); - goto err_bus_clk; - } - - clk_enable(ctx->lcd_clk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "failed to find registers\n"); - ret = -ENOENT; - goto err_clk; - } - - ctx->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!ctx->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_clk; - } - - ctx->regs = ioremap(res->start, resource_size(res)); - if (!ctx->regs) { - dev_err(dev, "failed to map registers\n"); - ret = -ENXIO; - goto err_req_region_io; - } -#ifdef CONFIG_MACH_SLP_NAPLES - /* Temporary code for trigger*/ - ctx_global = ctx; -#endif - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "irq request failed.\n"); - goto err_get_resource; - } - - ctx->irq = res->start; - - ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); - if (ret < 0) { - dev_err(dev, "irq request failed.\n"); - goto err_get_resource; - } - - ctx->clkdiv = fimd_calc_clkdiv(ctx, &panel->timing); - ctx->vidcon0 = pdata->vidcon0; - ctx->vidcon1 = pdata->vidcon1; - ctx->default_win = pdata->default_win; - ctx->dynamic_refresh = pdata->dynamic_refresh; - ctx->panel = panel; - - INIT_WORK(&ctx->work, exynos_fimd_schedule_work); - - panel->timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; - - DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", - panel->timing.pixclock, ctx->clkdiv); - - /* mdnie support. */ - mdnie = s5p_fimd_ext_find_device("mdnie"); - fimd_lite_dev = s5p_fimd_ext_find_device("fimd_lite"); - if (mdnie && fimd_lite_dev) { - fimd_refresh = kzalloc(sizeof(*fimd_refresh), GFP_KERNEL); - if (!fimd_refresh) { - dev_err(dev, "failed to allocate fimd_refresh.\n"); - ret = -ENOMEM; - goto err_alloc_fail; - } - - fimd_refresh->dynamic_refresh = pdata->dynamic_refresh; - fimd_refresh->regs = ctx->regs; - fimd_refresh->clkdiv = ctx->clkdiv; - } - - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { - ret = device_create_file(&(pdev->dev), - &device_attrs[i]); - if (ret) - break; - } - - if (ret < 0) - dev_err(&pdev->dev, "failed to add sysfs entries\n"); - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - ctx->nb_exynos_display.notifier_call = exynos_display_notifier_callback; - ret = exynos4_display_register_client(&ctx->nb_exynos_display); - if (ret < 0) - dev_warn(dev, "failed to register exynos-display notifier\n"); -#endif - - dev_info(&pdev->dev, "registered successfully\n"); - - subdrv = &ctx->subdrv; - - subdrv->dev = dev; - subdrv->manager = &fimd_manager; - subdrv->probe = fimd_subdrv_probe; - subdrv->remove = fimd_subdrv_remove; - -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB - ctx->nb_ctrl.notifier_call = fimd_notifier_ctrl; - ret = fimc_register_client(&ctx->nb_ctrl); - if (ret) { - dev_err(dev, "could not register fimd notify callback\n"); - goto err_alloc_fail; - } -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - pm_qos_add_request(&ctx->pm_qos, - PM_QOS_DISPLAY_FREQUENCY, EXYNOS4_DISPLAY_LV_LF); -#endif -#endif - - mutex_init(&ctx->lock); - - platform_set_drvdata(pdev, ctx); - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_get_sync(dev); - - for (win = 0; win < WINDOWS_NR; win++) - if (win != ctx->default_win) - fimd_clear_win(ctx, win); - - exynos_drm_subdrv_register(subdrv); - - return 0; - -err_alloc_fail: - free_irq(ctx->irq, ctx); - -err_get_resource: - iounmap(ctx->regs); - -err_req_region_io: - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - -err_clk: - clk_disable(ctx->lcd_clk); - clk_put(ctx->lcd_clk); - -err_bus_clk: - clk_disable(ctx->bus_clk); - clk_put(ctx->bus_clk); - -err_clk_get: - kfree(ctx); - return ret; -} - -static int __devexit fimd_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct fimd_context *ctx = platform_get_drvdata(pdev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_drm_subdrv_unregister(&ctx->subdrv); -#ifdef CONFIG_DRM_EXYNOS_FIMD_WB - fimc_unregister_client(&ctx->nb_ctrl); -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - pm_qos_remove_request(&ctx->pm_qos); -#endif -#endif - - if (ctx->suspended) - goto out; - - pm_runtime_set_suspended(dev); - pm_runtime_put_sync(dev); - -out: - pm_runtime_disable(dev); - - clk_put(ctx->lcd_clk); - clk_put(ctx->bus_clk); - - -#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ - exynos4_display_unregister_client(&ctx->nb_exynos_display); -#endif - - iounmap(ctx->regs); - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - free_irq(ctx->irq, ctx); - - kfree(ctx); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int fimd_suspend(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - if (pm_runtime_suspended(dev)) - return 0; - - /* - * do not use pm_runtime_suspend(). if pm_runtime_suspend() is - * called here, an error would be returned by that interface - * because the usage_count of pm runtime is more than 1. - */ - return fimd_power_on(ctx, false); -} - -static int fimd_resume(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - /* - * if entered to sleep when lcd panel was on, the usage_count - * of pm runtime would still be 1 so in this case, fimd driver - * should be on directly not drawing on pm runtime interface. - */ - if (!pm_runtime_suspended(dev)) - return fimd_power_on(ctx, true); - - return 0; -} -#endif - -#ifdef CONFIG_PM_RUNTIME -static int fimd_runtime_suspend(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return fimd_power_on(ctx, false); -} - -static int fimd_runtime_resume(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - return fimd_power_on(ctx, true); -} -#endif - -static const struct dev_pm_ops fimd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) - SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) -}; - -struct platform_driver fimd_driver = { - .probe = fimd_probe, - .remove = __devexit_p(fimd_remove), - .driver = { - .name = "s3cfb", - .owner = THIS_MODULE, - .pm = &fimd_pm_ops, - }, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_g2d.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_g2d.c deleted file mode 100644 index a8f201e..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_g2d.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics Co.Ltd - * Authors: Joonyoung Shim <jy0922.shim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundationr - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/pm_qos_params.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/workqueue.h> - -#include "drmP.h" -#include "exynos_drm.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_iommu.h" - -#define G2D_HW_MAJOR_VER 4 -#define G2D_HW_MINOR_VER 1 - -/* vaild register range set from user: 0x0104 ~ 0x0880 */ -#define G2D_VALID_START 0x0104 -#define G2D_VALID_END 0x0880 - -/* general registers */ -#define G2D_SOFT_RESET 0x0000 -#define G2D_INTEN 0x0004 -#define G2D_INTC_PEND 0x000C -#define G2D_DMA_SFR_BASE_ADDR 0x0080 -#define G2D_DMA_COMMAND 0x0084 -#define G2D_DMA_STATUS 0x008C -#define G2D_DMA_HOLD_CMD 0x0090 - -/* command registers */ -#define G2D_BITBLT_START 0x0100 - -/* registers for base address */ -#define G2D_SRC_BASE_ADDR 0x0304 -#define G2D_SRC_PLANE2_BASE_ADDR 0x0318 -#define G2D_DST_BASE_ADDR 0x0404 -#define G2D_DST_PLANE2_BASE_ADDR 0x0418 -#define G2D_PAT_BASE_ADDR 0x0500 -#define G2D_MSK_BASE_ADDR 0x0520 - -/* G2D_SOFT_RESET */ -#define G2D_SFRCLEAR (1 << 1) -#define G2D_R (1 << 0) - -/* G2D_INTEN */ -#define G2D_INTEN_ACF (1 << 3) -#define G2D_INTEN_UCF (1 << 2) -#define G2D_INTEN_GCF (1 << 1) -#define G2D_INTEN_SCF (1 << 0) - -/* G2D_INTC_PEND */ -#define G2D_INTP_ACMD_FIN (1 << 3) -#define G2D_INTP_UCMD_FIN (1 << 2) -#define G2D_INTP_GCMD_FIN (1 << 1) -#define G2D_INTP_SCMD_FIN (1 << 0) - -/* G2D_DMA_COMMAND */ -#define G2D_DMA_HALT (1 << 2) -#define G2D_DMA_CONTINUE (1 << 1) -#define G2D_DMA_START (1 << 0) - -/* G2D_DMA_STATUS */ -#define G2D_DMA_LIST_DONE_COUNT (0xFF << 17) -#define G2D_DMA_BITBLT_DONE_COUNT (0xFFFF << 1) -#define G2D_DMA_DONE (1 << 0) -#define G2D_DMA_LIST_DONE_COUNT_OFFSET 17 - -/* G2D_DMA_HOLD_CMD */ -#define G2D_USET_HOLD (1 << 2) -#define G2D_LIST_HOLD (1 << 1) -#define G2D_BITBLT_HOLD (1 << 0) - -/* G2D_BITBLT_START */ -#define G2D_START_CASESEL (1 << 2) -#define G2D_START_NHOLT (1 << 1) -#define G2D_START_BITBLT (1 << 0) - -#define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) -#define G2D_CMDLIST_NUM 64 -#define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) -#define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) - -#define MAX_BUF_ADDR_NR 6 - -/* cmdlist data structure */ -struct g2d_cmdlist { - u32 head; - u32 data[G2D_CMDLIST_DATA_NUM]; - u32 last; /* last data offset */ -}; - -struct drm_exynos_pending_g2d_event { - struct drm_pending_event base; - struct drm_exynos_g2d_event event; -}; - -struct g2d_cmdlist_node { - struct list_head list; - struct g2d_cmdlist *cmdlist; - unsigned int map_nr; - void *gem_obj[MAX_BUF_ADDR_NR]; - dma_addr_t dma_addr; - - struct drm_exynos_pending_g2d_event *event; -}; - -struct g2d_runqueue_node { - struct list_head list; - struct list_head run_cmdlist; - struct list_head event_list; - struct completion complete; - int async; -}; - -struct g2d_data { - struct device *dev; - struct clk *gate_clk; - struct resource *regs_res; - void __iomem *regs; - int irq; - struct workqueue_struct *g2d_workq; - struct work_struct runqueue_work; - struct exynos_drm_subdrv subdrv; - struct pm_qos_request_list pm_qos; - bool suspended; - - /* cmdlist */ - struct g2d_cmdlist_node *cmdlist_node; - struct list_head free_cmdlist; - struct mutex cmdlist_mutex; - dma_addr_t cmdlist_pool; - dma_addr_t cmdlist_pool_map; - void *cmdlist_pool_virt; - - /* runqueue*/ - struct g2d_runqueue_node *runqueue_node; - struct list_head runqueue; - struct mutex runqueue_mutex; - struct kmem_cache *runqueue_slab; -}; - -static int g2d_init_cmdlist(struct g2d_data *g2d) -{ - struct device *dev = g2d->dev; - struct g2d_cmdlist_node *node = g2d->cmdlist_node; - int nr; - int ret; - - g2d->cmdlist_pool_virt = dma_alloc_coherent(dev, G2D_CMDLIST_POOL_SIZE, - &g2d->cmdlist_pool, GFP_KERNEL); - if (!g2d->cmdlist_pool_virt) { - dev_err(dev, "failed to allocate dma memory\n"); - return -ENOMEM; - } - - /* - * Allocate device address space for command list pool and then map all - * pages contained in sg list to iommu table. Command list pool also is - * accessed by dma through device address with using iommu. - */ - g2d->cmdlist_pool_map = exynos_drm_iommu_map(dev, g2d->cmdlist_pool, - G2D_CMDLIST_POOL_SIZE); - if (!g2d->cmdlist_pool_map) { - dev_err(dev, "failed map to iommu\n"); - ret = -EFAULT; - goto err; - } - - node = kcalloc(G2D_CMDLIST_NUM, G2D_CMDLIST_NUM * sizeof(*node), - GFP_KERNEL); - if (!node) { - dev_err(dev, "failed to allocate memory\n"); - ret = -ENOMEM; - goto err_iommu_unmap; - } - - for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { - node[nr].cmdlist = - g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; - node[nr].dma_addr = - g2d->cmdlist_pool_map + nr * G2D_CMDLIST_SIZE; - - list_add_tail(&node[nr].list, &g2d->free_cmdlist); - } - - return 0; - -err_iommu_unmap: - exynos_drm_iommu_unmap(dev, g2d->cmdlist_pool_map); -err: - dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, - g2d->cmdlist_pool); - return ret; -} - -static void g2d_fini_cmdlist(struct g2d_data *g2d) -{ - struct device *dev = g2d->dev; - - exynos_drm_iommu_unmap(dev, g2d->cmdlist_pool_map); - - kfree(g2d->cmdlist_node); - dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, - g2d->cmdlist_pool); -} - -static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) -{ - struct device *dev = g2d->dev; - struct g2d_cmdlist_node *node; - - mutex_lock(&g2d->cmdlist_mutex); - if (list_empty(&g2d->free_cmdlist)) { - dev_err(dev, "there is no free cmdlist\n"); - mutex_unlock(&g2d->cmdlist_mutex); - return NULL; - } - - node = list_first_entry(&g2d->free_cmdlist, struct g2d_cmdlist_node, - list); - list_del_init(&node->list); - mutex_unlock(&g2d->cmdlist_mutex); - - return node; -} - -static void g2d_put_cmdlist(struct g2d_data *g2d, struct g2d_cmdlist_node *node) -{ - mutex_lock(&g2d->cmdlist_mutex); - list_move_tail(&node->list, &g2d->free_cmdlist); - mutex_unlock(&g2d->cmdlist_mutex); -} - -static void g2d_add_cmdlist_to_inuse(struct exynos_drm_g2d_private *g2d_priv, - struct g2d_cmdlist_node *node) -{ - struct g2d_cmdlist_node *lnode; - - if (list_empty(&g2d_priv->inuse_cmdlist)) - goto add_to_list; - - /* this links to base address of new cmdlist */ - lnode = list_entry(g2d_priv->inuse_cmdlist.prev, - struct g2d_cmdlist_node, list); - lnode->cmdlist->data[lnode->cmdlist->last] = node->dma_addr; - -add_to_list: - list_add_tail(&node->list, &g2d_priv->inuse_cmdlist); - - if (node->event) - list_add_tail(&node->event->base.link, &g2d_priv->event_list); -} - -static int g2d_map_cmdlist_gem(struct g2d_data *g2d, - struct g2d_cmdlist_node *node, - struct drm_device *drm_dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; - struct g2d_cmdlist *cmdlist = node->cmdlist; - struct iommu_gem_map_params params; - unsigned int gem_handle; - dma_addr_t addr; - int offset; - int i; - - params.dev = g2d->dev; - params.drm_dev = drm_dev; - params.file = file; - - for (i = 0; i < node->map_nr; i++) { - offset = cmdlist->last - (i * 2 + 1); - gem_handle = cmdlist->data[offset]; - - addr = exynos_drm_iommu_map_gem(¶ms, - &g2d_priv->iommu_map_list, - gem_handle, - IOMMU_G2D); - if (!addr) { - node->map_nr = i; - return -EFAULT; - } - - cmdlist->data[offset] = addr; - node->gem_obj[i] = params.gem_obj; - } - - return 0; -} - -static void g2d_unmap_cmdlist_gem(struct drm_device *drm_dev, - struct g2d_cmdlist_node *node, - struct drm_file *file, int dec) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; - struct device *dev = g2d_priv->dev; - struct g2d_cmdlist *cmdlist = node->cmdlist; - struct iommu_gem_map_params params; - struct g2d_data *g2d; - dma_addr_t addr; - int offset; - int i; - - g2d = dev_get_drvdata(dev); - if (!g2d) - return; - - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; - - for (i = 0; i < node->map_nr; i++) { - offset = cmdlist->last - (i * 2 + 1) - dec; - addr = cmdlist->data[offset]; - - params.gem_obj = node->gem_obj[i]; - - exynos_drm_iommu_unmap_gem(¶ms, addr, - IOMMU_G2D); - } -} - -static void g2d_dma_start(struct g2d_data *g2d, - struct g2d_runqueue_node *runqueue_node) -{ - struct g2d_cmdlist_node *node = - list_first_entry(&runqueue_node->run_cmdlist, - struct g2d_cmdlist_node, list); - - pm_runtime_get_sync(g2d->dev); - clk_enable(g2d->gate_clk); - pm_qos_update_request(&g2d->pm_qos, 400000); - - /* interrupt enable */ - writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, - g2d->regs + G2D_INTEN); - - writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR); - writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND); -} - -static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d) -{ - struct g2d_runqueue_node *runqueue_node; - - if (list_empty(&g2d->runqueue)) - return NULL; - - runqueue_node = list_first_entry(&g2d->runqueue, - struct g2d_runqueue_node, list); - list_del_init(&runqueue_node->list); - return runqueue_node; -} - -static void g2d_free_runqueue_node(struct g2d_data *g2d, - struct g2d_runqueue_node *runqueue_node) -{ - if (!runqueue_node) - return; - - mutex_lock(&g2d->cmdlist_mutex); - list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist); - mutex_unlock(&g2d->cmdlist_mutex); - - kmem_cache_free(g2d->runqueue_slab, runqueue_node); -} - -static void g2d_exec_runqueue(struct g2d_data *g2d) -{ - g2d->runqueue_node = g2d_get_runqueue_node(g2d); - if (g2d->runqueue_node) - g2d_dma_start(g2d, g2d->runqueue_node); -} - -static void g2d_runqueue_worker(struct work_struct *work) -{ - struct g2d_data *g2d = container_of(work, struct g2d_data, - runqueue_work); - - pm_qos_update_request(&g2d->pm_qos, 0); - - mutex_lock(&g2d->runqueue_mutex); - clk_disable(g2d->gate_clk); - pm_runtime_put_sync(g2d->dev); - - complete(&g2d->runqueue_node->complete); - if (g2d->runqueue_node->async) - g2d_free_runqueue_node(g2d, g2d->runqueue_node); - - if (g2d->suspended) - g2d->runqueue_node = NULL; - else - g2d_exec_runqueue(g2d); - mutex_unlock(&g2d->runqueue_mutex); -} - -static void g2d_finish_event(struct g2d_data *g2d, u32 cmdlist_no) -{ - struct drm_device *drm_dev = g2d->subdrv.drm_dev; - struct g2d_runqueue_node *runqueue_node = g2d->runqueue_node; - struct drm_exynos_pending_g2d_event *e; - struct timeval now; - unsigned long flags; - - if (list_empty(&runqueue_node->event_list)) - return; - - e = list_first_entry(&runqueue_node->event_list, - struct drm_exynos_pending_g2d_event, base.link); - - do_gettimeofday(&now); - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; - e->event.cmdlist_no = cmdlist_no; - - spin_lock_irqsave(&drm_dev->event_lock, flags); - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - spin_unlock_irqrestore(&drm_dev->event_lock, flags); -} - -static irqreturn_t g2d_irq_handler(int irq, void *dev_id) -{ - struct g2d_data *g2d = dev_id; - u32 pending; - - pending = readl_relaxed(g2d->regs + G2D_INTC_PEND); - if (pending) - writel_relaxed(pending, g2d->regs + G2D_INTC_PEND); - - if (pending & G2D_INTP_GCMD_FIN) { - u32 cmdlist_no = readl_relaxed(g2d->regs + G2D_DMA_STATUS); - - cmdlist_no = (cmdlist_no & G2D_DMA_LIST_DONE_COUNT) >> - G2D_DMA_LIST_DONE_COUNT_OFFSET; - - g2d_finish_event(g2d, cmdlist_no); - - writel_relaxed(0, g2d->regs + G2D_DMA_HOLD_CMD); - if (!(pending & G2D_INTP_ACMD_FIN)) { - writel_relaxed(G2D_DMA_CONTINUE, - g2d->regs + G2D_DMA_COMMAND); - } - } - - if (pending & G2D_INTP_ACMD_FIN) - queue_work(g2d->g2d_workq, &g2d->runqueue_work); - - return IRQ_HANDLED; -} - -static int g2d_check_reg_offset(struct device *dev, struct g2d_cmdlist *cmdlist, - int nr, bool for_addr) -{ - int reg_offset; - int index; - int i; - - for (i = 0; i < nr; i++) { - index = cmdlist->last - 2 * (i + 1); - reg_offset = cmdlist->data[index] & ~0xfffff000; - - if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END) - goto err; - if (reg_offset % 4) - goto err; - - switch (reg_offset) { - case G2D_SRC_BASE_ADDR: - case G2D_SRC_PLANE2_BASE_ADDR: - case G2D_DST_BASE_ADDR: - case G2D_DST_PLANE2_BASE_ADDR: - case G2D_PAT_BASE_ADDR: - case G2D_MSK_BASE_ADDR: - if (!for_addr) - goto err; - break; - default: - if (for_addr) - goto err; - break; - } - } - - return 0; - -err: - dev_err(dev, "Bad register offset: 0x%x\n", cmdlist->data[index]); - return -EINVAL; -} - -/* ioctl functions */ -int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file) -{ - struct drm_exynos_g2d_get_ver *ver = data; - - ver->major = G2D_HW_MAJOR_VER; - ver->minor = G2D_HW_MINOR_VER; - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_g2d_get_ver_ioctl); - -int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; - struct device *dev = g2d_priv->dev; - struct g2d_data *g2d; - struct drm_exynos_g2d_set_cmdlist *req = data; - struct drm_exynos_pending_g2d_event *e; - struct g2d_cmdlist_node *node; - struct g2d_cmdlist *cmdlist; - unsigned long flags; - int size; - int ret; - - if (!dev) - return -ENODEV; - - g2d = dev_get_drvdata(dev); - if (!g2d) - return -EFAULT; - - node = g2d_get_cmdlist(g2d); - if (!node) - return -ENOMEM; - - node->event = NULL; - - if (req->event_type != G2D_EVENT_NOT) { - spin_lock_irqsave(&drm_dev->event_lock, flags); - if (file->event_space < sizeof(e->event)) { - spin_unlock_irqrestore(&drm_dev->event_lock, flags); - ret = -ENOMEM; - goto err; - } - file->event_space -= sizeof(e->event); - spin_unlock_irqrestore(&drm_dev->event_lock, flags); - - e = kzalloc(sizeof(*node->event), GFP_KERNEL); - if (!e) { - dev_err(dev, "failed to allocate event\n"); - - spin_lock_irqsave(&drm_dev->event_lock, flags); - file->event_space += sizeof(e->event); - spin_unlock_irqrestore(&drm_dev->event_lock, flags); - - ret = -ENOMEM; - goto err; - } - - e->event.base.type = DRM_EXYNOS_G2D_EVENT; - e->event.base.length = sizeof(e->event); - e->event.user_data = req->user_data; - e->base.event = &e->event.base; - e->base.file_priv = file; - e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; - - node->event = e; - } - - cmdlist = node->cmdlist; - - cmdlist->last = 0; - - /* - * If don't clear SFR registers, the cmdlist is affected by register - * values of previous cmdlist. G2D hw executes SFR clear command and - * a next command at the same time then the next command is ignored and - * is executed rightly from next next command, so needs a dummy command - * to next command of SFR clear command. - */ - cmdlist->data[cmdlist->last++] = G2D_SOFT_RESET; - cmdlist->data[cmdlist->last++] = G2D_SFRCLEAR; - cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR; - cmdlist->data[cmdlist->last++] = 0; - - if (node->event) { - cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD; - cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; - } - - /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ - size = cmdlist->last + req->cmd_nr * 2 + req->cmd_gem_nr * 2 + 2; - if (size > G2D_CMDLIST_DATA_NUM) { - dev_err(dev, "cmdlist size is too big\n"); - ret = -EINVAL; - goto err_free_event; - } - - if (copy_from_user(cmdlist->data + cmdlist->last, - (void __user *)req->cmd, - sizeof(*req->cmd) * req->cmd_nr)) { - ret = -EFAULT; - goto err_free_event; - } - cmdlist->last += req->cmd_nr * 2; - - ret = g2d_check_reg_offset(dev, cmdlist, req->cmd_nr, false); - if (ret < 0) - goto err_free_event; - - node->map_nr = req->cmd_gem_nr; - if (req->cmd_gem_nr) { - struct drm_exynos_g2d_cmd *cmd_gem = req->cmd_gem; - - if (copy_from_user(cmdlist->data + cmdlist->last, - (void __user *)cmd_gem, - sizeof(*cmd_gem) * req->cmd_gem_nr)) { - ret = -EFAULT; - goto err_free_event; - } - cmdlist->last += req->cmd_gem_nr * 2; - - ret = g2d_check_reg_offset(dev, cmdlist, req->cmd_gem_nr, true); - if (ret < 0) - goto err_free_event; - - ret = g2d_map_cmdlist_gem(g2d, node, drm_dev, file); - if (ret < 0) - goto err_unmap; - } - - cmdlist->data[cmdlist->last++] = G2D_BITBLT_START; - cmdlist->data[cmdlist->last++] = G2D_START_BITBLT; - - /* head */ - cmdlist->head = cmdlist->last / 2; - - /* tail */ - cmdlist->data[cmdlist->last] = 0; - - g2d_add_cmdlist_to_inuse(g2d_priv, node); - - return 0; - -err_unmap: - g2d_unmap_cmdlist_gem(drm_dev, node, file, 0); -err_free_event: - if (node->event) { - spin_lock_irqsave(&drm_dev->event_lock, flags); - file->event_space += sizeof(e->event); - spin_unlock_irqrestore(&drm_dev->event_lock, flags); - kfree(node->event); - } -err: - g2d_put_cmdlist(g2d, node); - return ret; -} -EXPORT_SYMBOL_GPL(exynos_g2d_set_cmdlist_ioctl); - -int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; - struct device *dev = g2d_priv->dev; - struct g2d_data *g2d; - struct drm_exynos_g2d_exec *req = data; - struct g2d_runqueue_node *runqueue_node; - struct list_head *run_cmdlist; - struct list_head *event_list; - - if (!dev) - return -ENODEV; - - g2d = dev_get_drvdata(dev); - if (!g2d) - return -EFAULT; - - runqueue_node = kmem_cache_alloc(g2d->runqueue_slab, GFP_KERNEL); - if (!runqueue_node) { - dev_err(dev, "failed to allocate memory\n"); - return -ENOMEM; - } - run_cmdlist = &runqueue_node->run_cmdlist; - event_list = &runqueue_node->event_list; - INIT_LIST_HEAD(run_cmdlist); - INIT_LIST_HEAD(event_list); - init_completion(&runqueue_node->complete); - runqueue_node->async = req->async; - - list_splice_init(&g2d_priv->inuse_cmdlist, run_cmdlist); - list_splice_init(&g2d_priv->event_list, event_list); - - if (list_empty(run_cmdlist)) { - dev_err(dev, "there is no inuse cmdlist\n"); - kmem_cache_free(g2d->runqueue_slab, runqueue_node); - return -EPERM; - } - - mutex_lock(&g2d->runqueue_mutex); - list_add_tail(&runqueue_node->list, &g2d->runqueue); - if (!g2d->runqueue_node) - g2d_exec_runqueue(g2d); - mutex_unlock(&g2d->runqueue_mutex); - - if (runqueue_node->async) - goto out; - - wait_for_completion(&runqueue_node->complete); - g2d_free_runqueue_node(g2d, runqueue_node); - -out: - return 0; -} -EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl); - -static int g2d_open(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv; - - g2d_priv = kzalloc(sizeof(*g2d_priv), GFP_KERNEL); - if (!g2d_priv) { - dev_err(dev, "failed to allocate g2d private data\n"); - return -ENOMEM; - } - - g2d_priv->dev = dev; - file_priv->g2d_priv = g2d_priv; - - INIT_LIST_HEAD(&g2d_priv->inuse_cmdlist); - INIT_LIST_HEAD(&g2d_priv->event_list); - INIT_LIST_HEAD(&g2d_priv->iommu_map_list); - - return 0; -} - -static void g2d_close(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; - struct g2d_data *g2d; - struct g2d_cmdlist_node *node, *n; - struct iommu_info_node *im, *t_im; - struct iommu_gem_map_params params; - - if (!dev) - return; - - g2d = dev_get_drvdata(dev); - if (!g2d) - return; - - mutex_lock(&g2d->cmdlist_mutex); - list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) - list_move_tail(&node->list, &g2d->free_cmdlist); - mutex_unlock(&g2d->cmdlist_mutex); - - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; - - /* - * unmap all device address spaces from iommu table and - * release all lists. - */ - list_for_each_entry_safe(im, t_im, &g2d_priv->iommu_map_list, list) { - params.gem_obj = im->gem_obj; - - exynos_drm_iommu_unmap_gem(¶ms, im->dma_addr, - IOMMU_G2D); - list_del(&im->list); - kfree(im); - im = NULL; - } - - kfree(file_priv->g2d_priv); -} - -static int __devinit g2d_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - struct g2d_data *g2d; - struct exynos_drm_subdrv *subdrv; - int ret; - - g2d = kzalloc(sizeof(*g2d), GFP_KERNEL); - if (!g2d) { - dev_err(dev, "failed to allocate driver data\n"); - return -ENOMEM; - } - - g2d->runqueue_slab = kmem_cache_create("g2d_runqueue_slab", - sizeof(struct g2d_runqueue_node), 0, 0, NULL); - if (!g2d->runqueue_slab) { - ret = -ENOMEM; - goto err_free_mem; - } - - g2d->dev = dev; - - g2d->g2d_workq = create_singlethread_workqueue("g2d"); - if (!g2d->g2d_workq) { - dev_err(dev, "failed to create workqueue\n"); - ret = -EINVAL; - goto err_destroy_slab; - } - - INIT_WORK(&g2d->runqueue_work, g2d_runqueue_worker); - INIT_LIST_HEAD(&g2d->free_cmdlist); - INIT_LIST_HEAD(&g2d->runqueue); - - mutex_init(&g2d->cmdlist_mutex); - mutex_init(&g2d->runqueue_mutex); - - /* setup device address space for g2d iommu */ - ret = exynos_drm_iommu_setup(dev); - if (ret < 0) { - dev_err(dev, "failed to setup iommu\n"); - goto err_destroy_workqueue; - } - - /* enable iommu to g2d hardware */ - ret = exynos_drm_iommu_activate(dev); - if (ret < 0) { - dev_err(dev, "failed to activate iommu\n"); - goto err_iommu_cleanup; - } - - ret = g2d_init_cmdlist(g2d); - if (ret < 0) - goto err_iommu_deactivate; - - g2d->gate_clk = clk_get(dev, "fimg2d"); - if (IS_ERR(g2d->gate_clk)) { - dev_err(dev, "failed to get gate clock\n"); - ret = PTR_ERR(g2d->gate_clk); - goto err_fini_cmdlist; - } - - pm_runtime_enable(dev); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "failed to get I/O memory\n"); - ret = -ENOENT; - goto err_put_clk; - } - - g2d->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!g2d->regs_res) { - dev_err(dev, "failed to request I/O memory\n"); - ret = -ENOENT; - goto err_put_clk; - } - - g2d->regs = ioremap(res->start, resource_size(res)); - if (!g2d->regs) { - dev_err(dev, "failed to remap I/O memory\n"); - ret = -ENXIO; - goto err_release_res; - } - - g2d->irq = platform_get_irq(pdev, 0); - if (g2d->irq < 0) { - dev_err(dev, "failed to get irq\n"); - ret = g2d->irq; - goto err_unmap_base; - } - - ret = request_irq(g2d->irq, g2d_irq_handler, 0, "drm_g2d", g2d); - if (ret < 0) { - dev_err(dev, "irq request failed\n"); - goto err_unmap_base; - } - - platform_set_drvdata(pdev, g2d); - - subdrv = &g2d->subdrv; - subdrv->dev = dev; - subdrv->open = g2d_open; - subdrv->close = g2d_close; - - ret = exynos_drm_subdrv_register(subdrv); - if (ret < 0) { - dev_err(dev, "failed to register drm g2d device\n"); - goto err_free_irq; - } - - pm_qos_add_request(&g2d->pm_qos, PM_QOS_BUS_DMA_THROUGHPUT, 0); - - dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n", - G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER); - - return 0; - -err_free_irq: - free_irq(g2d->irq, g2d); -err_unmap_base: - iounmap(g2d->regs); -err_release_res: - release_resource(g2d->regs_res); - kfree(g2d->regs_res); -err_put_clk: - pm_runtime_disable(dev); - clk_put(g2d->gate_clk); -err_fini_cmdlist: - g2d_fini_cmdlist(g2d); -err_iommu_deactivate: - exynos_drm_iommu_deactivate(dev); -err_iommu_cleanup: - exynos_drm_iommu_cleanup(dev); -err_destroy_workqueue: - destroy_workqueue(g2d->g2d_workq); -err_destroy_slab: - kmem_cache_destroy(g2d->runqueue_slab); -err_free_mem: - kfree(g2d); - return ret; -} - -static int __devexit g2d_remove(struct platform_device *pdev) -{ - struct g2d_data *g2d = platform_get_drvdata(pdev); - - cancel_work_sync(&g2d->runqueue_work); - pm_qos_remove_request(&g2d->pm_qos); - exynos_drm_subdrv_unregister(&g2d->subdrv); - free_irq(g2d->irq, g2d); - - while (g2d->runqueue_node) { - g2d_free_runqueue_node(g2d, g2d->runqueue_node); - g2d->runqueue_node = g2d_get_runqueue_node(g2d); - } - - iounmap(g2d->regs); - release_resource(g2d->regs_res); - kfree(g2d->regs_res); - - pm_runtime_disable(&pdev->dev); - clk_put(g2d->gate_clk); - - g2d_fini_cmdlist(g2d); - exynos_drm_iommu_deactivate(&pdev->dev); - exynos_drm_iommu_cleanup(&pdev->dev); - destroy_workqueue(g2d->g2d_workq); - kmem_cache_destroy(g2d->runqueue_slab); - kfree(g2d); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int g2d_suspend(struct device *dev) -{ - struct g2d_data *g2d = dev_get_drvdata(dev); - - mutex_lock(&g2d->runqueue_mutex); - g2d->suspended = true; - mutex_unlock(&g2d->runqueue_mutex); - - while (g2d->runqueue_node) - /* FIXME: good range? */ - usleep_range(500, 1000); - - flush_work_sync(&g2d->runqueue_work); - - return 0; -} - -static int g2d_resume(struct device *dev) -{ - struct g2d_data *g2d = dev_get_drvdata(dev); - - g2d->suspended = false; - g2d_exec_runqueue(g2d); - - return 0; -} -#endif - -SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume); - -struct platform_driver g2d_driver = { - .probe = g2d_probe, - .remove = __devexit_p(g2d_remove), - .driver = { - /* FIXME */ - .name = "s5p-fimg2d", - .owner = THIS_MODULE, - .pm = &g2d_pm_ops, - }, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_g2d.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_g2d.h deleted file mode 100644 index 1a9c7ca..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_g2d.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics Co.Ltd - * Authors: Joonyoung Shim <jy0922.shim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundationr - */ - -#ifdef CONFIG_DRM_EXYNOS_G2D -extern int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -#else -static inline int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return -ENODEV; -} - -static inline int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, - void *data, - struct drm_file *file_priv) -{ - return -ENODEV; -} - -static inline int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return -ENODEV; -} -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_gem.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_gem.c deleted file mode 100644 index 35d2cd9..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_gem.c +++ /dev/null @@ -1,1437 +0,0 @@ -/* exynos_drm_gem.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm.h" - -#include <drm/exynos_drm.h> -#include <linux/shmem_fs.h> -#include <linux/dma-buf.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" -#include "exynos_drm_buf.h" -#include "exynos_drm_iommu.h" - -static struct exynos_drm_private_cb *private_cb; - -void exynos_drm_priv_cb_register(struct exynos_drm_private_cb *cb) -{ - if (cb) - private_cb = cb; -} - -int register_buf_to_priv_mgr(struct exynos_drm_gem_obj *obj, - unsigned int *priv_handle, unsigned int *priv_id) -{ - if (private_cb && private_cb->add_buffer) - return private_cb->add_buffer(obj, priv_handle, priv_id); - - return 0; -} - -static unsigned int convert_to_vm_err_msg(int msg) -{ - unsigned int out_msg; - - switch (msg) { - case 0: - case -ERESTARTSYS: - case -EINTR: - out_msg = VM_FAULT_NOPAGE; - break; - - case -ENOMEM: - out_msg = VM_FAULT_OOM; - break; - - default: - out_msg = VM_FAULT_SIGBUS; - break; - } - - return out_msg; -} - -static int check_gem_flags(unsigned int flags) -{ - if (flags & ~(EXYNOS_BO_MASK)) { - DRM_ERROR("invalid flags.\n"); - return -EINVAL; - } - - return 0; -} - -static int check_cache_flags(unsigned int flags) -{ - if (flags & ~(EXYNOS_DRM_CACHE_SEL_MASK | EXYNOS_DRM_CACHE_OP_MASK)) { - DRM_ERROR("invalid flags.\n"); - return -EINVAL; - } - - return 0; -} - -static struct vm_area_struct *get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (!vma_copy) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} - -static void put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} - -static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj, - struct vm_area_struct *vma) -{ - DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags); - - /* non-cachable as default. */ - if (obj->flags & EXYNOS_BO_CACHABLE) - vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - else if (obj->flags & EXYNOS_BO_WC) - vma->vm_page_prot = - pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); - else - vma->vm_page_prot = - pgprot_noncached(vm_get_page_prot(vma->vm_flags)); -} - -static unsigned long roundup_gem_size(unsigned long size, unsigned int flags) -{ - if (!IS_NONCONTIG_BUFFER(flags)) { - if (size >= SZ_1M) - return roundup(size, SECTION_SIZE); - else if (size >= SZ_64K) - return roundup(size, SZ_64K); - else - goto out; - } -out: - return roundup(size, PAGE_SIZE); -} - -struct page **exynos_gem_get_pages(struct drm_gem_object *obj, - gfp_t gfpmask) -{ - struct inode *inode; - struct address_space *mapping; - struct page *p, **pages; - int i, npages; - - /* This is the shared memory object that backs the GEM resource */ - inode = obj->filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - - npages = obj->size >> PAGE_SHIFT; - - pages = drm_malloc_ab(npages, sizeof(struct page *)); - if (pages == NULL) - return ERR_PTR(-ENOMEM); - - gfpmask |= mapping_gfp_mask(mapping); - - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); - if (IS_ERR(p)) - goto fail; - pages[i] = p; - } - - return pages; - -fail: - while (i--) - page_cache_release(pages[i]); - - drm_free_large(pages); - return ERR_PTR(PTR_ERR(p)); -} - -static void exynos_gem_put_pages(struct drm_gem_object *obj, - struct page **pages, - bool dirty, bool accessed) -{ - int i, npages; - - npages = obj->size >> PAGE_SHIFT; - - for (i = 0; i < npages; i++) { - if (dirty) - set_page_dirty(pages[i]); - - if (accessed) - mark_page_accessed(pages[i]); - - /* Undo the reference we took when populating the table */ - page_cache_release(pages[i]); - } - - drm_free_large(pages); -} - -static int exynos_drm_gem_map_pages(struct drm_gem_object *obj, - struct vm_area_struct *vma, - unsigned long f_vaddr, - pgoff_t page_offset) -{ - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); - struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; - unsigned long pfn; - - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - if (!buf->pages) - return -EINTR; - - pfn = page_to_pfn(buf->pages[page_offset++]); - } else - pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset; - - return vm_insert_mixed(vma, f_vaddr, pfn); -} - -static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) -{ - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); - struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; - struct scatterlist *sgl; - struct page **pages; - unsigned int npages, i = 0; - int ret; - - if (buf->pages) { - DRM_DEBUG_KMS("already allocated.\n"); - return -EINVAL; - } - - pages = exynos_gem_get_pages(obj, GFP_KERNEL); - if (IS_ERR(pages)) { - DRM_ERROR("failed to get pages.\n"); - return PTR_ERR(pages); - } - - npages = obj->size >> PAGE_SHIFT; - buf->page_size = PAGE_SIZE; - - buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); - if (!buf->sgt) { - DRM_ERROR("failed to allocate sg table.\n"); - ret = -ENOMEM; - goto err; - } - - ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); - if (ret < 0) { - DRM_ERROR("failed to initialize sg table.\n"); - ret = -EFAULT; - goto err1; - } - - sgl = buf->sgt->sgl; - - /* set all pages to sg list. */ - while (i < npages) { - sg_set_page(sgl, pages[i], PAGE_SIZE, 0); - sg_dma_address(sgl) = page_to_phys(pages[i]); - i++; - sgl = sg_next(sgl); - } - - /* add some codes for UNCACHED type here. TODO */ - - buf->pages = pages; - return ret; -err1: - kfree(buf->sgt); - buf->sgt = NULL; -err: - exynos_gem_put_pages(obj, pages, true, false); - return ret; - -} - -static void exynos_drm_gem_put_pages(struct drm_gem_object *obj) -{ - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); - struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; - - /* - * if buffer typs is EXYNOS_BO_NONCONTIG then release all pages - * allocated at gem fault handler. - */ - sg_free_table(buf->sgt); - kfree(buf->sgt); - buf->sgt = NULL; - - exynos_gem_put_pages(obj, buf->pages, true, false); - buf->pages = NULL; - - /* add some codes for UNCACHED type here. TODO */ -} - -static void exynos_drm_put_userptr(struct drm_gem_object *obj) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_gem_buf *buf; - struct vm_area_struct *vma; - int npages; - - exynos_gem_obj = to_exynos_gem_obj(obj); - buf = exynos_gem_obj->buffer; - vma = exynos_gem_obj->vma; - - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - put_vma(exynos_gem_obj->vma); - goto out; - } - - npages = buf->size >> PAGE_SHIFT; - - npages--; - while (npages >= 0) { - if (buf->write) - set_page_dirty_lock(buf->pages[npages]); - - put_page(buf->pages[npages]); - npages--; - } - -out: - kfree(buf->pages); - buf->pages = NULL; - - kfree(buf->sgt); - buf->sgt = NULL; -} - -static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, - struct drm_file *file_priv, - unsigned int *handle) -{ - int ret; - - /* - * allocate a id of idr table where the obj is registered - * and handle has the id what user can see. - */ - ret = drm_gem_handle_create(file_priv, obj, handle); - if (ret) - return ret; - - DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); - - /* drop reference from allocate - handle holds it now. */ - drm_gem_object_unreference_unlocked(obj); - - return 0; -} - -void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) -{ - struct drm_gem_object *obj; - struct exynos_drm_gem_buf *buf; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - obj = &exynos_gem_obj->base; - buf = exynos_gem_obj->buffer; - - DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); - - /* - * release a private buffer from its table. - * - * this callback will release a ump object only if user requested - * ump export otherwise just return. - */ - if (private_cb->release_buffer) - private_cb->release_buffer(exynos_gem_obj->priv_handle); - - if (!buf->pages) - return; - - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) - exynos_drm_gem_put_pages(obj); - else if (exynos_gem_obj->flags & EXYNOS_BO_USERPTR) - exynos_drm_put_userptr(obj); - else - exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf); - - exynos_drm_fini_buf(obj->dev, buf); - exynos_gem_obj->buffer = NULL; - - if (obj->map_list.map) - drm_gem_free_mmap_offset(obj); - - /* release file pointer to gem object. */ - drm_gem_object_release(obj); - - kfree(exynos_gem_obj); - exynos_gem_obj = NULL; -} - -struct exynos_drm_gem_obj *exynos_drm_gem_get_obj(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - - obj = drm_gem_object_lookup(dev, file_priv, gem_handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - return ERR_PTR(-EINVAL); - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - drm_gem_object_unreference_unlocked(obj); - - return exynos_gem_obj; -} - -struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, - unsigned long size) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - int ret; - - exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); - if (!exynos_gem_obj) { - DRM_ERROR("failed to allocate exynos gem object\n"); - return NULL; - } - - exynos_gem_obj->size = size; - obj = &exynos_gem_obj->base; - - ret = drm_gem_object_init(dev, obj, size); - if (ret < 0) { - DRM_ERROR("failed to initialize gem object\n"); - kfree(exynos_gem_obj); - return NULL; - } - - DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); - - return exynos_gem_obj; -} - -struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, - unsigned int flags, - unsigned long size) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_gem_buf *buf; - int ret; - - if (!size) { - DRM_ERROR("invalid size.\n"); - return ERR_PTR(-EINVAL); - } - - size = roundup_gem_size(size, flags); - DRM_DEBUG_KMS("%s\n", __FILE__); - - ret = check_gem_flags(flags); - if (ret) - return ERR_PTR(ret); - - buf = exynos_drm_init_buf(dev, size); - if (!buf) - return ERR_PTR(-ENOMEM); - - exynos_gem_obj = exynos_drm_gem_init(dev, size); - if (!exynos_gem_obj) { - ret = -ENOMEM; - goto err_fini_buf; - } - - exynos_gem_obj->buffer = buf; - - /* set memory type and cache attribute from user side. */ - exynos_gem_obj->flags = flags; - - /* - * allocate all pages as desired size if user wants to allocate - * physically non-continuous memory. - */ - if (flags & EXYNOS_BO_NONCONTIG) { - ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base); - if (ret < 0) { - drm_gem_object_release(&exynos_gem_obj->base); - goto err_fini_buf; - } - } else { - ret = exynos_drm_alloc_buf(dev, buf, flags); - if (ret < 0) { - drm_gem_object_release(&exynos_gem_obj->base); - goto err_fini_buf; - } - } - - return exynos_gem_obj; - -err_fini_buf: - exynos_drm_fini_buf(dev, buf); - return ERR_PTR(ret); -} - -int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_create *args = data; - struct exynos_drm_gem_obj *exynos_gem_obj; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); - if (IS_ERR(exynos_gem_obj)) - return PTR_ERR(exynos_gem_obj); - - ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, - &args->handle); - if (ret) { - exynos_drm_gem_destroy(exynos_gem_obj); - return ret; - } - - return 0; -} - -void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - - obj = drm_gem_object_lookup(dev, file_priv, gem_handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - return ERR_PTR(-EINVAL); - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - DRM_DEBUG_KMS("not support NONCONTIG type.\n"); - drm_gem_object_unreference_unlocked(obj); - - /* TODO */ - return ERR_PTR(-EINVAL); - } - - return &exynos_gem_obj->buffer->dma_addr; -} - -void exynos_drm_gem_put_dma_addr(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - - obj = drm_gem_object_lookup(dev, file_priv, gem_handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - return; - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - DRM_DEBUG_KMS("not support NONCONTIG type.\n"); - drm_gem_object_unreference_unlocked(obj); - - /* TODO */ - return; - } - - drm_gem_object_unreference_unlocked(obj); - - /* - * decrease obj->refcount one more time because we has already - * increased it at exynos_drm_gem_get_dma_addr(). - */ - drm_gem_object_unreference_unlocked(obj); -} - -int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_map_off *args = data; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", - args->handle, (unsigned long)args->offset); - - if (!(dev->driver->driver_features & DRIVER_GEM)) { - DRM_ERROR("does not support GEM.\n"); - return -ENODEV; - } - - return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle, - &args->offset); -} - -static int exynos_drm_gem_mmap_buffer(struct file *filp, - struct vm_area_struct *vma) -{ - struct drm_gem_object *obj = filp->private_data; - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); - struct exynos_drm_gem_buf *buffer; - unsigned long pfn, vm_size, usize, uaddr = vma->vm_start; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - vma->vm_flags |= (VM_IO | VM_RESERVED); - - update_vm_cache_attr(exynos_gem_obj, vma); - - vma->vm_file = filp; - - vm_size = usize = vma->vm_end - vma->vm_start; - - /* - * a buffer contains information to physically continuous memory - * allocated by user request or at framebuffer creation. - */ - buffer = exynos_gem_obj->buffer; - - /* check if user-requested size is valid. */ - if (vm_size > buffer->size) - return -EINVAL; - - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - int i = 0; - - if (!buffer->pages) - return -EINVAL; - - vma->vm_flags |= VM_MIXEDMAP; - - do { - ret = vm_insert_page(vma, uaddr, buffer->pages[i++]); - if (ret) { - DRM_ERROR("failed to remap user space.\n"); - return ret; - } - - uaddr += PAGE_SIZE; - usize -= PAGE_SIZE; - } while (usize > 0); - } else { - /* - * get page frame number to physical memory to be mapped - * to user space. - */ - pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> - PAGE_SHIFT; - - DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); - - if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, - vma->vm_page_prot)) { - DRM_ERROR("failed to remap pfn range.\n"); - return -EAGAIN; - } - } - - return 0; -} - -static const struct file_operations exynos_drm_gem_fops = { - .mmap = exynos_drm_gem_mmap_buffer, -}; - -int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_mmap *args = data; - struct drm_gem_object *obj; - unsigned int addr; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!(dev->driver->driver_features & DRIVER_GEM)) { - DRM_ERROR("does not support GEM.\n"); - return -ENODEV; - } - - obj = drm_gem_object_lookup(dev, file_priv, args->handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - return -EINVAL; - } - - obj->filp->f_op = &exynos_drm_gem_fops; - obj->filp->private_data = obj; - - down_write(¤t->mm->mmap_sem); - addr = do_mmap(obj->filp, 0, args->size, - PROT_READ | PROT_WRITE, MAP_SHARED, 0); - up_write(¤t->mm->mmap_sem); - - drm_gem_object_unreference_unlocked(obj); - - if (IS_ERR((void *)addr)) - return PTR_ERR((void *)addr); - - args->mapped = addr; - - DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); - - return 0; -} - -static int exynos_drm_get_userptr(struct drm_device *dev, - struct exynos_drm_gem_obj *obj, - unsigned long userptr, - unsigned int write) -{ - unsigned int get_npages; - unsigned long npages = 0; - struct vm_area_struct *vma; - struct exynos_drm_gem_buf *buf = obj->buffer; - - vma = find_vma(current->mm, userptr); - - /* the memory region mmaped with VM_PFNMAP. */ - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - unsigned long this_pfn, prev_pfn, pa; - unsigned long start, end, offset; - struct scatterlist *sgl; - int ret; - - start = userptr; - offset = userptr & ~PAGE_MASK; - end = start + buf->size; - sgl = buf->sgt->sgl; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) { - pa = this_pfn << PAGE_SHIFT; - buf->dma_addr = pa + offset; - } else if (this_pfn != prev_pfn + 1) { - ret = -EINVAL; - goto err; - } - - sg_dma_address(sgl) = (pa + offset); - sg_dma_len(sgl) = PAGE_SIZE; - prev_pfn = this_pfn; - pa += PAGE_SIZE; - npages++; - sgl = sg_next(sgl); - } - - obj->vma = get_vma(vma); - if (!obj->vma) { - ret = -ENOMEM; - goto err; - } - - buf->pfnmap = true; - - return npages; -err: - buf->dma_addr = 0; - return ret; - } - - buf->write = write; - npages = buf->size >> PAGE_SHIFT; - - down_read(¤t->mm->mmap_sem); - get_npages = get_user_pages(current, current->mm, userptr, - npages, write, 1, buf->pages, NULL); - up_read(¤t->mm->mmap_sem); - if (get_npages != npages) - DRM_ERROR("failed to get user_pages.\n"); - - buf->pfnmap = false; - - return get_npages; -} - -int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_exynos_gem_userptr *args = data; - struct exynos_drm_gem_buf *buf; - struct scatterlist *sgl; - unsigned long size, userptr; - unsigned int npages; - int ret, get_npages; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!args->size) { - DRM_ERROR("invalid size.\n"); - return -EINVAL; - } - - ret = check_gem_flags(args->flags); - if (ret) - return ret; - - size = roundup_gem_size(args->size, EXYNOS_BO_USERPTR); - userptr = args->userptr; - - buf = exynos_drm_init_buf(dev, size); - if (!buf) - return -ENOMEM; - - exynos_gem_obj = exynos_drm_gem_init(dev, size); - if (!exynos_gem_obj) { - ret = -ENOMEM; - goto err_free_buffer; - } - - buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); - if (!buf->sgt) { - DRM_ERROR("failed to allocate buf->sgt.\n"); - ret = -ENOMEM; - goto err_release_gem; - } - - npages = size >> PAGE_SHIFT; - - ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); - if (ret < 0) { - DRM_ERROR("failed to initailize sg table.\n"); - goto err_free_sgt; - } - - buf->pages = kzalloc(npages * sizeof(struct page *), GFP_KERNEL); - if (!buf->pages) { - DRM_ERROR("failed to allocate buf->pages\n"); - ret = -ENOMEM; - goto err_free_table; - } - - exynos_gem_obj->buffer = buf; - - get_npages = exynos_drm_get_userptr(dev, exynos_gem_obj, userptr, 1); - if (get_npages != npages) { - DRM_ERROR("failed to get user_pages.\n"); - ret = get_npages; - goto err_release_userptr; - } - - ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, - &args->handle); - if (ret < 0) { - DRM_ERROR("failed to create gem handle.\n"); - goto err_release_userptr; - } - - sgl = buf->sgt->sgl; - - /* - * if buf->pfnmap is true then update sgl of sgt with pages but - * if buf->pfnmap is false then it means the sgl was updated already - * so it doesn't need to update the sgl. - */ - if (!buf->pfnmap) { - unsigned int i = 0; - - /* set all pages to sg list. */ - while (i < npages) { - sg_set_page(sgl, buf->pages[i], PAGE_SIZE, 0); - sg_dma_address(sgl) = page_to_phys(buf->pages[i]); - i++; - sgl = sg_next(sgl); - } - } - - /* always use EXYNOS_BO_USERPTR as memory type for userptr. */ - exynos_gem_obj->flags |= EXYNOS_BO_USERPTR; - - return 0; - -err_release_userptr: - get_npages--; - while (get_npages >= 0) - put_page(buf->pages[get_npages--]); - kfree(buf->pages); - buf->pages = NULL; -err_free_table: - sg_free_table(buf->sgt); -err_free_sgt: - kfree(buf->sgt); - buf->sgt = NULL; -err_release_gem: - drm_gem_object_release(&exynos_gem_obj->base); - kfree(exynos_gem_obj); - exynos_gem_obj = NULL; -err_free_buffer: - exynos_drm_free_buf(dev, 0, buf); - return ret; -} - -int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_exynos_gem_info *args = data; - struct drm_gem_object *obj; - - mutex_lock(&dev->struct_mutex); - - obj = drm_gem_object_lookup(dev, file_priv, args->handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - args->flags = exynos_gem_obj->flags; - args->size = exynos_gem_obj->size; - - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - - return 0; -} - -int exynos_drm_gem_export_ump_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - struct drm_exynos_gem_ump *ump = data; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - mutex_lock(&dev->struct_mutex); - - obj = drm_gem_object_lookup(dev, file, ump->gem_handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - /* register gem buffer to private buffer. */ - ret = register_buf_to_priv_mgr(exynos_gem_obj, - (unsigned int *)&exynos_gem_obj->priv_handle, - (unsigned int *)&exynos_gem_obj->priv_id); - if (ret < 0) - goto err_unreference_gem; - - ump->secure_id = exynos_gem_obj->priv_id; - drm_gem_object_unreference(obj); - - mutex_unlock(&dev->struct_mutex); - - DRM_DEBUG_KMS("got secure id = %d\n", ump->secure_id); - - return 0; - -err_unreference_gem: - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - return ret; - -} - -static int exynos_gem_l1_cache_ops(struct drm_device *drm_dev, - struct drm_exynos_gem_cache_op *op) { - if (op->flags & EXYNOS_DRM_CACHE_FSH_ALL) { - /* - * cortex-A9 core has individual l1 cache so flush l1 caches - * for all cores but other cores should be considered later. - * TODO - */ - if (op->flags & EXYNOS_DRM_ALL_CORES) - flush_all_cpu_caches(); - else - __cpuc_flush_user_all(); - - } else if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE) { - struct vm_area_struct *vma; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, op->usr_addr); - up_read(¤t->mm->mmap_sem); - - if (!vma) { - DRM_ERROR("failed to get vma.\n"); - return -EFAULT; - } - - __cpuc_flush_user_range(op->usr_addr, op->usr_addr + op->size, - vma->vm_flags); - } - - return 0; -} - -static int exynos_gem_l2_cache_ops(struct drm_device *drm_dev, - struct drm_exynos_gem_cache_op *op) { - phys_addr_t phy_start, phy_end; - - if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE || - op->flags & EXYNOS_DRM_CACHE_INV_RANGE || - op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) { - struct vm_area_struct *vma; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, op->usr_addr); - up_read(¤t->mm->mmap_sem); - - if (!vma) { - DRM_ERROR("failed to get vma.\n"); - return -EFAULT; - } - - /* - * for range flush to l2 cache, mmaped memory region should - * be physically continuous because l2 cache uses PIPT. - */ - if (vma && (vma->vm_flags & VM_PFNMAP)) { - unsigned long virt_start = op->usr_addr, pfn; - int ret; - - ret = follow_pfn(vma, virt_start, &pfn); - if (ret < 0) { - DRM_ERROR("failed to get pfn from usr_addr.\n"); - return ret; - } - - phy_start = pfn << PAGE_SHIFT; - phy_end = phy_start + op->size; - } else { - DRM_ERROR("not mmaped memory region with PFNMAP.\n"); - return -EINVAL; - } - } - - if (op->flags & EXYNOS_DRM_CACHE_FSH_ALL) - outer_flush_all(); - else if (op->flags & EXYNOS_DRM_CACHE_FSH_RANGE) - outer_flush_range(phy_start, phy_end); - else if (op->flags & EXYNOS_DRM_CACHE_INV_ALL) - outer_inv_all(); - else if (op->flags & EXYNOS_DRM_CACHE_CLN_ALL) - outer_clean_all(); - else if (op->flags & EXYNOS_DRM_CACHE_INV_RANGE) - outer_inv_range(phy_start, phy_end); - else if (op->flags & EXYNOS_DRM_CACHE_CLN_RANGE) - outer_clean_range(phy_start, phy_end); - else { - DRM_ERROR("invalid l2 cache operation.\n"); - return -EINVAL; - } - - return 0; -} - -int exynos_drm_gem_cache_op_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_cache_op *op = data; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ret = check_cache_flags(op->flags); - if (ret) - return -EINVAL; - - if (op->flags & EXYNOS_DRM_L1_CACHE || - op->flags & EXYNOS_DRM_ALL_CACHES) { - ret = exynos_gem_l1_cache_ops(drm_dev, op); - if (ret < 0) - goto err; - } - - if (op->flags & EXYNOS_DRM_L2_CACHE || - op->flags & EXYNOS_DRM_ALL_CACHES) - ret = exynos_gem_l2_cache_ops(drm_dev, op); -err: - return ret; -} - -/* temporary functions. */ -int exynos_drm_gem_get_phy_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_get_phy *get_phy = data; - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - mutex_lock(&drm_dev->struct_mutex); - - obj = drm_gem_object_lookup(drm_dev, file_priv, get_phy->gem_handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - mutex_unlock(&drm_dev->struct_mutex); - return -EINVAL; - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - /* - * we can get physical address only for EXYNOS_DRM_GEM_PC memory type. - */ - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - DRM_DEBUG_KMS("not physically continuous memory type.\n"); - drm_gem_object_unreference(obj); - mutex_unlock(&drm_dev->struct_mutex); - return -EINVAL; - } - - get_phy->phy_addr = exynos_gem_obj->buffer->dma_addr; - get_phy->size = exynos_gem_obj->buffer->size; - - drm_gem_object_unreference(obj); - mutex_unlock(&drm_dev->struct_mutex); - - return 0; -} - -int exynos_drm_gem_phy_imp_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_phy_imp *args = data; - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_gem_buf *buffer; - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_gem_obj = exynos_drm_gem_init(drm_dev, args->size); - if (!exynos_gem_obj) - return -ENOMEM; - - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) { - DRM_DEBUG_KMS("failed to allocate buffer\n"); - ret = -ENOMEM; - goto err; - } - - buffer->dma_addr = (dma_addr_t)args->phy_addr; - buffer->size = args->size; - - /* - * if shared is true, this bufer wouldn't be released. - * this buffer was allocated by other so don't release it. - */ - buffer->shared = true; - - exynos_gem_obj->buffer = buffer; - - ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, - &args->gem_handle); - if (ret) - goto err_kfree_buffer; - - DRM_DEBUG_KMS("got gem handle = 0x%x\n", args->gem_handle); - - return 0; - -err_kfree_buffer: - kfree(buffer); -err: - drm_gem_object_release(&exynos_gem_obj->base); - kfree(exynos_gem_obj); - return ret; -} - -int exynos_drm_gem_init_object(struct drm_gem_object *obj) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - return 0; -} - -void exynos_drm_gem_free_object(struct drm_gem_object *obj) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_gem_buf *buf; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_gem_obj = to_exynos_gem_obj(obj); - buf = exynos_gem_obj->buffer; - - if (obj->import_attach) - drm_prime_gem_destroy(obj, buf->sgt); - - exynos_drm_gem_destroy(to_exynos_gem_obj(obj)); -} - -int exynos_drm_gem_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * alocate memory to be used for framebuffer. - * - this callback would be called by user application - * with DRM_IOCTL_MODE_CREATE_DUMB command. - */ - - args->pitch = args->width * args->bpp >> 3; - args->size = PAGE_ALIGN(args->pitch * args->height); - - exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); - if (IS_ERR(exynos_gem_obj)) - return PTR_ERR(exynos_gem_obj); - - ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, - &args->handle); - if (ret) { - exynos_drm_gem_destroy(exynos_gem_obj); - return ret; - } - - return 0; -} - -int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, - struct drm_device *dev, uint32_t handle, - uint64_t *offset) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - mutex_lock(&dev->struct_mutex); - - /* - * get offset of memory allocated for drm framebuffer. - * - this callback would be called by user application - * with DRM_IOCTL_MODE_MAP_DUMB command. - */ - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - ret = -EINVAL; - goto unlock; - } - - exynos_gem_obj = to_exynos_gem_obj(obj); - - if (!exynos_gem_obj->base.map_list.map) { - ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base); - if (ret) - goto out; - } - - *offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT; - DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); - -out: - drm_gem_object_unreference(obj); -unlock: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - unsigned int handle) -{ - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * obj->refcount and obj->handle_count are decreased and - * if both them are 0 then exynos_drm_gem_free_object() - * would be called by callback to release resources. - */ - ret = drm_gem_handle_delete(file_priv, handle); - if (ret < 0) { - DRM_ERROR("failed to delete drm_gem_handle.\n"); - return ret; - } - - return 0; -} - -void exynos_drm_gem_close_object(struct drm_gem_object *obj, - struct drm_file *file) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct iommu_gem_map_params params; - unsigned int type = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* check this gem object was mapped to iommu at here. TODO */ - - exynos_gem_obj = to_exynos_gem_obj(obj); - - while (type < MAX_IOMMU_NR) { - /* - * unmap device address space already mapped to iommu. - * - this codes would be performed with user gem release - * request but in case of no request, when device driver - * using iommu is released, also same things should be - * performed by each driver. - */ - if (exynos_gem_obj->iommu_info.mapped & (1 << type)) { - dma_addr_t dma_addr; - struct list_head *list; - - params.dev = exynos_gem_obj->iommu_info.devs[type]; - params.drm_dev = obj->dev; - params.file = file; - params.gem_obj = exynos_gem_obj; - dma_addr = exynos_gem_obj->iommu_info.dma_addrs[type]; - - exynos_drm_iommu_unmap_gem(¶ms, - dma_addr, - type); - - exynos_gem_obj->iommu_info.mapped &= ~(1 << type); - exynos_gem_obj->iommu_info.dma_addrs[type] = 0; - - list = exynos_gem_obj->iommu_info.iommu_lists[type]; - - /* - * this gem has been unmapped from iommu so also - * remove a iommu node from current device's own - * iommu list. - */ - exynos_drm_remove_iommu_list(list, exynos_gem_obj); - } - - type++; - } -} - -int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct drm_gem_object *obj = vma->vm_private_data; - struct drm_device *dev = obj->dev; - unsigned long f_vaddr; - pgoff_t page_offset; - int ret; - - page_offset = ((unsigned long)vmf->virtual_address - - vma->vm_start) >> PAGE_SHIFT; - f_vaddr = (unsigned long)vmf->virtual_address; - - mutex_lock(&dev->struct_mutex); - - ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset); - if (ret < 0) - DRM_ERROR("failed to map pages.\n"); - - mutex_unlock(&dev->struct_mutex); - - return convert_to_vm_err_msg(ret); -} - -int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_gem_object *obj; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* set vm_area_struct. */ - ret = drm_gem_mmap(filp, vma); - if (ret < 0) { - DRM_ERROR("failed to mmap.\n"); - return ret; - } - - obj = vma->vm_private_data; - exynos_gem_obj = to_exynos_gem_obj(obj); - - ret = check_gem_flags(exynos_gem_obj->flags); - if (ret) { - drm_gem_vm_close(vma); - drm_gem_free_mmap_offset(obj); - return ret; - } - - vma->vm_flags &= ~VM_PFNMAP; - vma->vm_flags |= VM_MIXEDMAP; - - update_vm_cache_attr(exynos_gem_obj, vma); - - return ret; -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_gem.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_gem.h deleted file mode 100644 index 1227ae3..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_gem.h +++ /dev/null @@ -1,246 +0,0 @@ -/* exynos_drm_gem.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authoer: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_GEM_H_ -#define _EXYNOS_DRM_GEM_H_ - -#define to_exynos_gem_obj(x) container_of(x,\ - struct exynos_drm_gem_obj, base) - -/* FIMD/HDMI/G2D/FIMC/G3D */ -#define MAX_IOMMU_NR 5 - -#define IS_NONCONTIG_BUFFER(f) ((f & EXYNOS_BO_NONCONTIG) ||\ - (f & EXYNOS_BO_USERPTR)) - -struct exynos_drm_private_cb { - unsigned int (*get_handle)(unsigned int id); - int (*add_buffer)(void *obj, unsigned int *handle, unsigned int *id); - void (*release_buffer)(unsigned int handle); -}; - -/* - * exynos drm iommu information structure. - * - * @mapped: flag a bit of indicating whether any driver's device address - * is mapped to its own iommu or not. - * @dma_addrs: contain device address to each device driver using iommu. - * @devs: device objects that requested mapping to iommu. - */ -struct exynos_drm_iommu_info { - unsigned int mapped; - dma_addr_t dma_addrs[MAX_IOMMU_NR]; - struct device *devs[MAX_IOMMU_NR]; - struct list_head *iommu_lists[MAX_IOMMU_NR]; - /* TODO. */ -}; - -/* - * exynos drm gem buffer structure. - * - * @kvaddr: kernel virtual address to allocated memory region. - * @dma_addr: bus address(accessed by dma) to allocated memory region. - * - this address could be physical address without IOMMU and - * device address with IOMMU. - * @write: whether pages will be written to by the caller. - * @sgt: sg table to transfer page data. - * @pages: contain all pages to allocated memory region. - * @page_size: could be 4K, 64K or 1MB. - * @size: size of allocated memory region. - * @shared: indicate shared mfc memory region. - * (temporarily used and it should be removed later.) - * @shared_refcount: a reference count for this buffer being shared with others. - * @pfnmap: indicate whether memory region from userptr is mmaped with - * VM_PFNMAP or not. - */ -struct exynos_drm_gem_buf { - struct device *dev; - void __iomem *kvaddr; - dma_addr_t dma_addr; - unsigned int write; - struct sg_table *sgt; - struct page **pages; - unsigned long page_size; - unsigned long size; - bool shared; - atomic_t shared_refcount; - bool pfnmap; -}; - -/* - * exynos drm buffer structure. - * - * @base: a gem object. - * - a new handle to this gem object would be created - * by drm_gem_handle_create(). - * @buffer: a pointer to exynos_drm_gem_buffer object. - * - contain the information to memory region allocated - * by user request or at framebuffer creation. - * continuous memory region allocated by user request - * or at framebuffer creation. - * @iommu_info: contain iommu mapping information to each device driver - * using its own iommu. - * @size: total memory size to physically non-continuous memory region. - * @flags: indicate memory type to allocated buffer and cache attruibute. - * @priv_handle: handle to specific buffer object. - * @priv_id: unique id to specific buffer object. - * - * P.S. this object would be transfered to user as kms_bo.handle so - * user can access the buffer through kms_bo.handle. - */ -struct exynos_drm_gem_obj { - struct drm_gem_object base; - struct exynos_drm_gem_buf *buffer; - struct exynos_drm_iommu_info iommu_info; - unsigned long size; - struct vm_area_struct *vma; - unsigned int flags; - void *dma_buf_vmapping; - int vmapping_count; - unsigned int priv_handle; - unsigned int priv_id; -}; - -/* register private callback. */ -void exynos_drm_priv_cb_register(struct exynos_drm_private_cb *cb); - -/* register a buffer object to private buffer manager. */ -int register_buf_to_priv_mgr(struct exynos_drm_gem_obj *obj, - unsigned int *priv_handle, unsigned int *priv_id); - -struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); - -/* destroy a buffer with gem object */ -void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj); - -/* create a private gem object and initialize it. */ -struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, - unsigned long size); - -/* create a new buffer with gem object */ -struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, - unsigned int flags, - unsigned long size); - -/* - * request gem object creation and buffer allocation as the size - * that it is calculated with framebuffer information such as width, - * height and bpp. - */ -int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* - * get dma address from gem handle and this function could be used for - * other drivers such as 2d/3d acceleration drivers. - * with this function call, gem object reference count would be increased. - */ -void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv); - -/* - * put dma address from gem handle and this function could be used for - * other drivers such as 2d/3d acceleration drivers. - * with this function call, gem object reference count would be decreased. - */ -void exynos_drm_gem_put_dma_addr(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv); - -/* get buffer offset to map to user space. */ -int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* - * mmap the physically continuous memory that a gem object contains - * to user space. - */ -int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* map user space allocated by malloc to pages. */ -int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* get buffer information to memory region allocated by gem. */ -int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* initialize gem object. */ -int exynos_drm_gem_init_object(struct drm_gem_object *obj); - -/* free gem object. */ -void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj); - -/* create memory region for drm framebuffer. */ -int exynos_drm_gem_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args); - -/* map memory region for drm framebuffer to user space. */ -int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, - struct drm_device *dev, uint32_t handle, - uint64_t *offset); - -/* - * destroy memory region allocated. - * - a gem handle and physical memory region pointed by a gem object - * would be released by drm_gem_handle_delete(). - */ -int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - unsigned int handle); - -/* page fault handler and mmap fault address(virtual) to physical memory. */ -int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); - -/* set vm_flags and we can change the vm attribute to other one at here. */ -int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); - -/* get ump sequre id for UMP. */ -int exynos_drm_gem_export_ump_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); - -/* do user desired cache operation. */ -int exynos_drm_gem_cache_op_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv); - -/* temporary functions. */ -/* get physical address from a gem. */ -int exynos_drm_gem_get_phy_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv); -/* import physical memory to a gem. */ -int exynos_drm_gem_phy_imp_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv); - -void exynos_drm_gem_close_object(struct drm_gem_object *obj, - struct drm_file *file); - -struct exynos_drm_gem_obj *exynos_drm_gem_get_obj(struct drm_device *dev, - unsigned int gem_handle, - struct drm_file *file_priv); - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_hdmi.c deleted file mode 100644 index 5d9d2c2..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_hdmi.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include "drmP.h" - -#include <linux/kernel.h> -#include <linux/wait.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> - -#include <drm/exynos_drm.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_hdmi.h" - -#define to_context(dev) platform_get_drvdata(to_platform_device(dev)) -#define to_subdrv(dev) to_context(dev) -#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\ - struct drm_hdmi_context, subdrv); - -/* these callback points shoud be set by specific drivers. */ -static struct exynos_hdmi_ops *hdmi_ops; -static struct exynos_mixer_ops *mixer_ops; - -struct drm_hdmi_context { - struct exynos_drm_subdrv subdrv; - struct exynos_drm_hdmi_context *hdmi_ctx; - struct exynos_drm_hdmi_context *mixer_ctx; - - bool enabled[MIXER_WIN_NR]; -}; - -void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ops) - hdmi_ops = ops; -} - -void exynos_mixer_ops_register(struct exynos_mixer_ops *ops) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ops) - mixer_ops = ops; -} - -static bool drm_hdmi_is_connected(struct device *dev) -{ - struct drm_hdmi_context *ctx = to_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->is_connected) - return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx); - - return false; -} - -static int drm_hdmi_get_edid(struct device *dev, - struct drm_connector *connector, u8 *edid, int len) -{ - struct drm_hdmi_context *ctx = to_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->get_edid) - return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector, edid, - len); - - return 0; -} - -static int drm_hdmi_check_timing(struct device *dev, void *timing) -{ - struct drm_hdmi_context *ctx = to_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->check_timing) - return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing); - - return 0; -} - -static int drm_hdmi_power_on(struct device *dev, int mode) -{ - struct drm_hdmi_context *ctx = to_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->power_on) - return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode); - - return 0; -} - -static struct exynos_drm_display_ops drm_hdmi_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_HDMI, - .is_connected = drm_hdmi_is_connected, - .get_edid = drm_hdmi_get_edid, - .check_timing = drm_hdmi_check_timing, - .power_on = drm_hdmi_power_on, -}; - -static int drm_hdmi_enable_vblank(struct device *subdrv_dev) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct exynos_drm_manager *manager = subdrv->manager; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (mixer_ops && mixer_ops->enable_vblank) - return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, - manager->pipe); - - return 0; -} - -static void drm_hdmi_disable_vblank(struct device *subdrv_dev) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (mixer_ops && mixer_ops->disable_vblank) - return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); -} - -static void drm_hdmi_mode_fixup(struct device *subdrv_dev, - struct drm_connector *connector, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->mode_fixup) - hdmi_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, mode, - adjusted_mode); -} - -static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->mode_set) - hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode); -} - -static void drm_hdmi_get_max_resol(struct device *subdrv_dev, - unsigned int *width, unsigned int *height) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->get_max_resol) - hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height); -} - -static void drm_hdmi_commit(struct device *subdrv_dev) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (hdmi_ops && hdmi_ops->commit) - hdmi_ops->commit(ctx->hdmi_ctx->ctx); -} - -static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (mixer_ops && mixer_ops->dpms) - mixer_ops->dpms(ctx->mixer_ctx->ctx, mode); - - if (hdmi_ops && hdmi_ops->dpms) - hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode); -} - -static void drm_hdmi_apply(struct device *subdrv_dev) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - int i; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - for (i = 0; i < MIXER_WIN_NR; i++) { - if (!ctx->enabled[i]) - continue; - if (mixer_ops && mixer_ops->win_commit) - mixer_ops->win_commit(ctx->mixer_ctx->ctx, i); - } - - if (hdmi_ops && hdmi_ops->commit) - hdmi_ops->commit(ctx->hdmi_ctx->ctx); -} - -static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { - .dpms = drm_hdmi_dpms, - .apply = drm_hdmi_apply, - .enable_vblank = drm_hdmi_enable_vblank, - .disable_vblank = drm_hdmi_disable_vblank, - .mode_fixup = drm_hdmi_mode_fixup, - .mode_set = drm_hdmi_mode_set, - .get_max_resol = drm_hdmi_get_max_resol, - .commit = drm_hdmi_commit, -}; - -static void drm_mixer_mode_set(struct device *subdrv_dev, - struct exynos_drm_overlay *overlay) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (mixer_ops && mixer_ops->win_mode_set) - mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); -} - -static void drm_mixer_commit(struct device *subdrv_dev, int zpos) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (win < 0 || win > MIXER_WIN_NR) { - DRM_ERROR("mixer window[%d] is wrong\n", win); - return; - } - - if (mixer_ops && mixer_ops->win_commit) - mixer_ops->win_commit(ctx->mixer_ctx->ctx, win); - - ctx->enabled[win] = true; -} - -static void drm_mixer_disable(struct device *subdrv_dev, int zpos) -{ - struct drm_hdmi_context *ctx = to_context(subdrv_dev); - int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (win < 0 || win > MIXER_WIN_NR) { - DRM_ERROR("mixer window[%d] is wrong\n", win); - return; - } - - if (mixer_ops && mixer_ops->win_disable) - mixer_ops->win_disable(ctx->mixer_ctx->ctx, win); - - ctx->enabled[win] = false; -} - -static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { - .mode_set = drm_mixer_mode_set, - .commit = drm_mixer_commit, - .disable = drm_mixer_disable, -}; - -static struct exynos_drm_manager hdmi_manager = { - .pipe = -1, - .ops = &drm_hdmi_manager_ops, - .overlay_ops = &drm_hdmi_overlay_ops, - .display_ops = &drm_hdmi_display_ops, -}; - -static int hdmi_subdrv_probe(struct drm_device *drm_dev, - struct device *dev) -{ - struct exynos_drm_subdrv *subdrv = to_subdrv(dev); - struct drm_hdmi_context *ctx; - struct platform_device *pdev = to_platform_device(dev); - struct exynos_drm_common_hdmi_pd *pd; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - pd = pdev->dev.platform_data; - - if (!pd) { - DRM_DEBUG_KMS("platform data is null.\n"); - return -EFAULT; - } - - if (!pd->hdmi_dev) { - DRM_DEBUG_KMS("hdmi device is null.\n"); - return -EFAULT; - } - - if (!pd->mixer_dev) { - DRM_DEBUG_KMS("mixer device is null.\n"); - return -EFAULT; - } - - ctx = get_ctx_from_subdrv(subdrv); - - ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *) - to_context(pd->hdmi_dev); - if (!ctx->hdmi_ctx) { - DRM_DEBUG_KMS("hdmi context is null.\n"); - return -EFAULT; - } - - ctx->hdmi_ctx->drm_dev = drm_dev; - - ctx->mixer_ctx = (struct exynos_drm_hdmi_context *) - to_context(pd->mixer_dev); - if (!ctx->mixer_ctx) { - DRM_DEBUG_KMS("mixer context is null.\n"); - return -EFAULT; - } - - ctx->mixer_ctx->drm_dev = drm_dev; - - return 0; -} - -static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos_drm_subdrv *subdrv; - struct drm_hdmi_context *ctx; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - DRM_LOG_KMS("failed to alloc common hdmi context.\n"); - return -ENOMEM; - } - - subdrv = &ctx->subdrv; - - subdrv->dev = dev; - subdrv->manager = &hdmi_manager; - subdrv->probe = hdmi_subdrv_probe; - - platform_set_drvdata(pdev, subdrv); - - exynos_drm_subdrv_register(subdrv); - - return 0; -} - -static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) -{ - struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_drm_subdrv_unregister(&ctx->subdrv); - kfree(ctx); - - return 0; -} - -struct platform_driver exynos_drm_common_hdmi_driver = { - .probe = exynos_drm_hdmi_probe, - .remove = __devexit_p(exynos_drm_hdmi_remove), - .driver = { - .name = "exynos-drm-hdmi", - .owner = THIS_MODULE, - }, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_hdmi.h deleted file mode 100644 index bd81269..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_hdmi.h +++ /dev/null @@ -1,77 +0,0 @@ -/* exynos_drm_hdmi.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authoer: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_HDMI_H_ -#define _EXYNOS_DRM_HDMI_H_ - -#define MIXER_WIN_NR 3 -#define MIXER_DEFAULT_WIN 0 - -/* - * exynos hdmi common context structure. - * - * @drm_dev: pointer to drm_device. - * @ctx: pointer to the context of specific device driver. - * this context should be hdmi_context or mixer_context. - */ -struct exynos_drm_hdmi_context { - struct drm_device *drm_dev; - void *ctx; -}; - -struct exynos_hdmi_ops { - /* display */ - bool (*is_connected)(void *ctx); - int (*get_edid)(void *ctx, struct drm_connector *connector, - u8 *edid, int len); - int (*check_timing)(void *ctx, void *timing); - int (*power_on)(void *ctx, int mode); - - /* manager */ - void (*mode_fixup)(void *ctx, struct drm_connector *connector, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); - void (*mode_set)(void *ctx, void *mode); - void (*get_max_resol)(void *ctx, unsigned int *width, - unsigned int *height); - void (*commit)(void *ctx); - void (*dpms)(void *ctx, int mode); -}; - -struct exynos_mixer_ops { - /* manager */ - int (*enable_vblank)(void *ctx, int pipe); - void (*disable_vblank)(void *ctx); - void (*dpms)(void *ctx, int mode); - - /* overlay */ - void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); - void (*win_commit)(void *ctx, int zpos); - void (*win_disable)(void *ctx, int zpos); -}; - -void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops); -void exynos_mixer_ops_register(struct exynos_mixer_ops *ops); -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_iommu.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_iommu.c deleted file mode 100644 index f2ffa68..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_iommu.c +++ /dev/null @@ -1,329 +0,0 @@ -/* exynos_drm_iommu.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm.h" - -#include <drm/exynos_drm.h> - -#include <plat/s5p-iovmm.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" -#include "exynos_drm_iommu.h" - -static DEFINE_MUTEX(iommu_mutex); - -struct exynos_iommu_ops { - int (*setup)(struct device *dev); - void (*cleanup)(struct device *dev); - int (*activate)(struct device *dev); - void (*deactivate)(struct device *dev); - dma_addr_t (*map)(struct device *dev, struct scatterlist *sg, - off_t offset, size_t size); - void (*unmap)(struct device *dev, dma_addr_t iova); -}; - -static const struct exynos_iommu_ops iommu_ops = { - .setup = iovmm_setup, - .cleanup = iovmm_cleanup, - .activate = iovmm_activate, - .deactivate = iovmm_deactivate, - .map = iovmm_map, - .unmap = iovmm_unmap -}; - -static bool check_iommu_map_params(struct iommu_gem_map_params *params) -{ - if (!params) { - DRM_ERROR("params is null.\n"); - return false; - } - - if (!params->dev || !params->drm_dev || !params->file) { - DRM_ERROR("invalid params.\n"); - return false; - } - - return true; -} - -void exynos_drm_remove_iommu_list(struct list_head *iommu_list, - void *gem_obj) -{ - struct iommu_info_node *im, *t_im; - - list_for_each_entry_safe(im, t_im, iommu_list, list) { - if (im->gem_obj == gem_obj) { - list_del(&im->list); - kfree(im); - im = NULL; - break; - } - } - -} - -dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, - struct list_head *iommu_list, - unsigned int gem_handle, - enum iommu_types type) -{ - struct sg_table *sgt; - struct iommu_info_node *node; - struct exynos_drm_gem_obj *obj; - dma_addr_t dma_addr; - - if (!is_iommu_type_valid(type)) { - DRM_ERROR("invalid iommu type.\n"); - return 0; - } - - if (!check_iommu_map_params(params)) - return 0; - - /* get gem object from specific gem framework. */ - obj = exynos_drm_gem_get_obj(params->drm_dev, gem_handle, - params->file); - if (IS_ERR(obj)) - return 0; - - mutex_lock(&iommu_mutex); - - /* - * if this gem object had already been mapped to iommu then - * return dma address mapped before this time. - */ - if (obj->iommu_info.mapped & (1 << type)) { - DRM_DEBUG_KMS("already mapped to iommu"); - mutex_unlock(&iommu_mutex); - return obj->iommu_info.dma_addrs[type]; - } - - sgt = obj->buffer->sgt; - - /* - * if not using iommu, just return base address to physical - * memory region of the gem. - */ - if (!iommu_ops.map) { - mutex_unlock(&iommu_mutex); - return sg_dma_address(&sgt->sgl[0]); - } - mutex_unlock(&iommu_mutex); - - /* - * allocate device address space for this driver and then - * map all pages contained in sg list to iommu table. - */ - dma_addr = iommu_ops.map(params->dev, sgt->sgl, (off_t)0, - (size_t)obj->size); - if (!dma_addr) { - mutex_unlock(&iommu_mutex); - return dma_addr; - } - - mutex_lock(&iommu_mutex); - - /* - * check map flag bit and device address mapped to iommu. - * this data would be used to avoid duplicated mapping. - */ - obj->iommu_info.mapped |= (1 << type); - obj->iommu_info.dma_addrs[type] = dma_addr; - obj->iommu_info.devs[type] = params->dev; - obj->iommu_info.iommu_lists[type] = iommu_list; - - params->gem_obj = obj; - - /* - * this gem object is referenced by this driver so - * the object refcount should be increased. - */ - drm_gem_object_reference(&obj->base); - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - DRM_ERROR("failed to allocate iommu node.\n"); - dma_addr = 0; - goto err; - } - - node->gem_obj = obj; - node->dma_addr = dma_addr; - mutex_unlock(&iommu_mutex); - - list_add_tail(&node->list, iommu_list); - - return dma_addr; -err: - mutex_unlock(&iommu_mutex); - iommu_ops.unmap(params->dev, dma_addr); - return dma_addr; -} - -void exynos_drm_iommu_unmap_gem(struct iommu_gem_map_params *params, - dma_addr_t dma_addr, - enum iommu_types type) -{ - struct exynos_drm_gem_obj *obj; - - if (!iommu_ops.unmap) - return; - - if (!is_iommu_type_valid(type)) { - DRM_ERROR("invalid iommu type.\n"); - return; - } - - if (!check_iommu_map_params(params)) - return; - - if (!params->gem_obj) - return; - - obj = (struct exynos_drm_gem_obj *)params->gem_obj; - - mutex_lock(&iommu_mutex); - if (!(obj->iommu_info.mapped & (1 << type))) { - DRM_DEBUG_KMS("not already mapped to iommu so just return\n"); - mutex_unlock(&iommu_mutex); - return; - } - - /* uncheck map flag bit. */ - obj->iommu_info.mapped &= ~(1 << type); - obj->iommu_info.dma_addrs[type] = 0; - mutex_unlock(&iommu_mutex); - - iommu_ops.unmap(params->dev, dma_addr); - - /* - * drop this gem object refcount to release allocated buffer - * and resources. - */ - drm_gem_object_unreference_unlocked(&obj->base); -} - -dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, - size_t size) -{ - struct sg_table *sgt; - struct scatterlist *sgl; - dma_addr_t dma_addr = 0, tmp_addr; - unsigned int npages, i = 0; - int ret; - - /* if not using iommu, just return paddr. */ - if (!iommu_ops.map) - return paddr; - - npages = size >> PAGE_SHIFT; - - sgt = kzalloc(sizeof(struct sg_table) * npages, GFP_KERNEL); - if (!sgt) { - dev_err(dev, "failed to allocate sg table.\n"); - return dma_addr; - } - - ret = sg_alloc_table(sgt, npages, GFP_KERNEL); - if (ret < 0) { - dev_err(dev, "failed to initialize sg table.\n"); - goto err; - } - - sgl = sgt->sgl; - tmp_addr = paddr; - - while (i < npages) { - struct page *page = phys_to_page(tmp_addr); - sg_set_page(sgl, page, PAGE_SIZE, 0); - sg_dma_len(sgl) = PAGE_SIZE; - tmp_addr += PAGE_SIZE; - i++; - sgl = sg_next(sgl); - } - - /* - * allocate device address space for this driver and then - * map all pages contained in sg list to iommu table. - */ - dma_addr = iommu_ops.map(dev, sgt->sgl, (off_t)0, (size_t)size); - if (!dma_addr) - dev_err(dev, "failed to map cmdlist pool.\n"); - - sg_free_table(sgt); -err: - kfree(sgt); - sgt = NULL; - - return dma_addr; -} - - -void exynos_drm_iommu_unmap(struct device *dev, dma_addr_t dma_addr) -{ - if (iommu_ops.unmap) - iommu_ops.unmap(dev, dma_addr); -} - -int exynos_drm_iommu_setup(struct device *dev) -{ - /* - * allocate device address space to this driver and add vmm object - * to s5p_iovmm_list. please know that each iommu will use - * 1GB as its own device address apace. - * - * the device address space : 0x80000000 ~ 0xA0000000 - */ - if (iommu_ops.setup) - return iommu_ops.setup(dev); - - return 0; -} - -int exynos_drm_iommu_activate(struct device *dev) -{ - if (iommu_ops.activate) - return iovmm_activate(dev); - - return 0; -} - -void exynos_drm_iommu_deactivate(struct device *dev) -{ - if (iommu_ops.deactivate) - iommu_ops.deactivate(dev); -} - -void exynos_drm_iommu_cleanup(struct device *dev) -{ - if (iommu_ops.cleanup) - iommu_ops.cleanup(dev); -} - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM IOMMU Framework"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_iommu.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_iommu.h deleted file mode 100644 index aa267ba..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_iommu.h +++ /dev/null @@ -1,79 +0,0 @@ -/* exynos_drm_iommu.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Authoer: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_DRM_IOMMU_H_ -#define _EXYNOS_DRM_IOMMU_H_ - -enum iommu_types { - IOMMU_FIMD = 0, - IOMMU_HDMI, - IOMMU_G2D, - IOMMU_FIMC, - IOMMU_G3D, - IOMMU_ROTATOR, - IOMMU_MASK = 0x3f -}; - -struct iommu_gem_map_params { - struct device *dev; - struct drm_device *drm_dev; - struct drm_file *file; - void *gem_obj; -}; - -#define is_iommu_type_valid(t) (((1 << (t)) & ~(IOMMU_MASK)) ? false : true) - -void exynos_drm_remove_iommu_list(struct list_head *iommu_list, - void *gem_obj); - -/* get all pages to gem object and map them to iommu table. */ -dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, - struct list_head *iommu_list, - unsigned int gem_handle, - enum iommu_types type); - -/* unmap device address space to gem object from iommu table. */ -void exynos_drm_iommu_unmap_gem(struct iommu_gem_map_params *params, - dma_addr_t dma_addr, - enum iommu_types type); - -/* map physical memory region pointed by paddr to iommu table. */ -dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, - size_t size); - -/* unmap device address space pointed by dma_addr from iommu table. */ -void exynos_drm_iommu_unmap(struct device *dev, dma_addr_t dma_addr); - -/* setup device address space for device iommu. */ -int exynos_drm_iommu_setup(struct device *dev); - -int exynos_drm_iommu_activate(struct device *dev); - -void exynos_drm_iommu_deactivate(struct device *dev); - -/* clean up allocated device address space for device iommu. */ -void exynos_drm_iommu_cleanup(struct device *dev); - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_plane.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_plane.c deleted file mode 100644 index c4c6525..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_plane.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: Joonyoung Shim <jy0922.shim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include "drmP.h" - -#include "exynos_drm.h" -#include "exynos_drm_crtc.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h" - -struct exynos_plane { - struct drm_plane base; - struct exynos_drm_overlay overlay; - bool enabled; -}; - -static const uint32_t formats[] = { - DRM_FORMAT_XRGB8888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_NV12, - DRM_FORMAT_NV12M, - DRM_FORMAT_NV12MT, -}; - -static int -exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - struct exynos_drm_crtc_pos pos; - int ret; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - pos.crtc_x = crtc_x; - pos.crtc_y = crtc_y; - pos.crtc_w = crtc_w; - pos.crtc_h = crtc_h; - - /* considering 16.16 fixed point of source values */ - pos.fb_x = src_x >> 16; - pos.fb_y = src_y >> 16; - pos.src_w = src_w >> 16; - pos.src_h = src_h >> 16; - - ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos); - if (ret < 0) - return ret; - - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &overlay->zpos, - exynos_drm_encoder_crtc_plane_commit); - - exynos_plane->enabled = true; - - return 0; -} - -static int exynos_disable_plane(struct drm_plane *plane) -{ - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - if (!exynos_plane->enabled) - return 0; - - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_crtc_disable); - - exynos_plane->enabled = false; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; - - return 0; -} - -static void exynos_plane_destroy(struct drm_plane *plane) -{ - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - exynos_disable_plane(plane); - drm_plane_cleanup(plane); - kfree(exynos_plane); -} - -static struct drm_plane_funcs exynos_plane_funcs = { - .update_plane = exynos_update_plane, - .disable_plane = exynos_disable_plane, - .destroy = exynos_plane_destroy, -}; - -int exynos_plane_init(struct drm_device *dev, unsigned int nr) -{ - struct exynos_plane *exynos_plane; - uint32_t possible_crtcs; - - exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); - if (!exynos_plane) - return -ENOMEM; - - /* all CRTCs are available */ - possible_crtcs = (1 << MAX_CRTC) - 1; - - exynos_plane->overlay.zpos = DEFAULT_ZPOS; - - return drm_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, formats, ARRAY_SIZE(formats), - false); -} - -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_plane_set_zpos *zpos_req = data; - struct drm_mode_object *obj; - struct drm_plane *plane; - struct exynos_plane *exynos_plane; - int ret = 0; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) { - if (zpos_req->zpos != DEFAULT_ZPOS) { - DRM_ERROR("zpos not within limits\n"); - return -EINVAL; - } - } - - mutex_lock(&dev->mode_config.mutex); - - obj = drm_mode_object_find(dev, zpos_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - zpos_req->plane_id); - ret = -EINVAL; - goto out; - } - - plane = obj_to_plane(obj); - exynos_plane = container_of(plane, struct exynos_plane, base); - - exynos_plane->overlay.zpos = zpos_req->zpos; - -out: - mutex_unlock(&dev->mode_config.mutex); - return ret; -} diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_plane.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_plane.h deleted file mode 100644 index 16b71f8..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_plane.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: Joonyoung Shim <jy0922.shim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -int exynos_plane_init(struct drm_device *dev, unsigned int nr); -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_rotator.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_rotator.c deleted file mode 100644 index 5bf1d6e..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_rotator.c +++ /dev/null @@ -1,904 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics Co.Ltd - * Authors: YoungJun Cho <yj44.cho@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundationr - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/pm_qos_params.h> - -#include "drmP.h" -#include "exynos_drm.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" -#include "exynos_drm_iommu.h" - -/* Configuration */ -#define ROT_CONFIG 0x00 -#define ROT_CONFIG_IRQ (3 << 8) - -/* Image Control */ -#define ROT_CONTROL 0x10 -#define ROT_CONTROL_PATTERN_WRITE (1 << 16) -#define ROT_CONTROL_FMT_YCBCR420_2P (1 << 8) -#define ROT_CONTROL_FMT_RGB888 (6 << 8) -#define ROT_CONTROL_FMT_MASK (7 << 8) -#define ROT_CONTROL_FLIP_VERTICAL (2 << 6) -#define ROT_CONTROL_FLIP_HORIZONTAL (3 << 6) -#define ROT_CONTROL_FLIP_MASK (3 << 6) -#define ROT_CONTROL_ROT_90 (1 << 4) -#define ROT_CONTROL_ROT_180 (2 << 4) -#define ROT_CONTROL_ROT_270 (3 << 4) -#define ROT_CONTROL_ROT_MASK (3 << 4) -#define ROT_CONTROL_START (1 << 0) - -/* Status */ -#define ROT_STATUS 0x20 -#define ROT_STATUS_IRQ_PENDING(x) (1 << (x)) -#define ROT_STATUS_IRQ(x) (((x) >> 8) & 0x3) -#define ROT_STATUS_IRQ_VAL_COMPLETE 1 -#define ROT_STATUS_IRQ_VAL_ILLEGAL 2 - -/* Sourc Buffer Address */ -#define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2)) - -/* Source Buffer Size */ -#define ROT_SRC_BUF_SIZE 0x3c -#define ROT_SRC_BUF_SIZE_H(x) ((x) << 16) -#define ROT_SRC_BUF_SIZE_W(x) ((x) << 0) - -/* Source Crop Position */ -#define ROT_SRC_CROP_POS 0x40 -#define ROT_SRC_CROP_POS_Y(x) ((x) << 16) -#define ROT_SRC_CROP_POS_X(x) ((x) << 0) - -/* Source Crop Size */ -#define ROT_SRC_CROP_SIZE 0x44 -#define ROT_SRC_CROP_SIZE_H(x) ((x) << 16) -#define ROT_SRC_CROP_SIZE_W(x) ((x) << 0) - -/* Destination Buffer Address */ -#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) - -/* Destination Buffer Size */ -#define ROT_DST_BUF_SIZE 0x5c -#define ROT_DST_BUF_SIZE_H(x) ((x) << 16) -#define ROT_DST_BUF_SIZE_W(x) ((x) << 0) - -/* Destination Crop Position */ -#define ROT_DST_CROP_POS 0x60 -#define ROT_DST_CROP_POS_Y(x) ((x) << 16) -#define ROT_DST_CROP_POS_X(x) ((x) << 0) - -/* Round to nearest aligned value */ -#define ROT_ALIGN(x, align, mask) ((*(x) + (1 << ((align) - 1))) & (mask)) -/* Minimum limit value */ -#define ROT_MIN(min, mask) (((min) + ~(mask)) & (mask)) -/* Maximum limit value */ -#define ROT_MAX(max, mask) ((max) & (mask)) - -enum rot_irq_status { - ROT_IRQ_STATUS_COMPLETE = 8, - ROT_IRQ_STATUS_ILLEGAL = 9, -}; - -struct rot_limit { - u32 min_w; - u32 min_h; - u32 max_w; - u32 max_h; - u32 align; -}; - -struct rot_limit_table { - struct rot_limit ycbcr420_2p; - struct rot_limit rgb888; -}; - -struct rot_context { - struct rot_limit_table *limit_tbl; - struct clk *clock; - struct resource *regs_res; - void __iomem *regs; - int irq; - int exec_ret; - struct exynos_drm_subdrv subdrv; - struct completion complete; - struct mutex exec_mutex; - spinlock_t irq_lock; - struct pm_qos_request_list pm_qos; - bool suspended; -}; - -struct rot_buffer { - dma_addr_t src_addr[DRM_EXYNOS_ROT_MAX_BUF]; - dma_addr_t dst_addr[DRM_EXYNOS_ROT_MAX_BUF]; - void *src_gem_obj[DRM_EXYNOS_ROT_MAX_BUF]; - void *dst_gem_obj[DRM_EXYNOS_ROT_MAX_BUF]; - u32 src_cnt; - u32 dst_cnt; - u32 src_w; - u32 src_h; - u32 dst_w; - u32 dst_h; -}; - -static void rotator_reg_set_irq(struct rot_context *rot, bool enable) -{ - u32 value = readl(rot->regs + ROT_CONFIG); - - if (enable == true) - value |= ROT_CONFIG_IRQ; - else - value &= ~ROT_CONFIG_IRQ; - - writel(value, rot->regs + ROT_CONFIG); -} - -static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) -{ - u32 value = readl(rot->regs + ROT_CONTROL); - value &= ~ROT_CONTROL_FMT_MASK; - - switch (img_fmt) { - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV12M: - value |= ROT_CONTROL_FMT_YCBCR420_2P; - break; - case DRM_FORMAT_RGB888: - value |= ROT_CONTROL_FMT_RGB888; - break; - default: - DRM_ERROR("invalid image format\n"); - return; - } - - writel(value, rot->regs + ROT_CONTROL); -} - -static void rotator_reg_set_flip(struct rot_context *rot, - enum drm_exynos_rot_flip flip) -{ - u32 value = readl(rot->regs + ROT_CONTROL); - value &= ~ROT_CONTROL_FLIP_MASK; - - switch (flip) { - case ROT_FLIP_VERTICAL: - value |= ROT_CONTROL_FLIP_VERTICAL; - break; - case ROT_FLIP_HORIZONTAL: - value |= ROT_CONTROL_FLIP_HORIZONTAL; - break; - default: - /* Flip None */ - break; - } - - writel(value, rot->regs + ROT_CONTROL); -} - -static void rotator_reg_set_rotation(struct rot_context *rot, - enum drm_exynos_rot_degree degree) -{ - u32 value = readl(rot->regs + ROT_CONTROL); - value &= ~ROT_CONTROL_ROT_MASK; - - switch (degree) { - case ROT_DEGREE_90: - value |= ROT_CONTROL_ROT_90; - break; - case ROT_DEGREE_180: - value |= ROT_CONTROL_ROT_180; - break; - case ROT_DEGREE_270: - value |= ROT_CONTROL_ROT_270; - break; - default: - /* Rotation 0 Degree */ - break; - } - - writel(value, rot->regs + ROT_CONTROL); -} - -static void rotator_reg_set_start(struct rot_context *rot) -{ - u32 value = readl(rot->regs + ROT_CONTROL); - - value |= ROT_CONTROL_START; - - writel(value, rot->regs + ROT_CONTROL); -} - -static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) -{ - u32 value = readl(rot->regs + ROT_STATUS); - value = ROT_STATUS_IRQ(value); - - if (value == ROT_STATUS_IRQ_VAL_COMPLETE) - return ROT_IRQ_STATUS_COMPLETE; - else - return ROT_IRQ_STATUS_ILLEGAL; -} - -static void rotator_reg_set_irq_status_clear(struct rot_context *rot, - enum rot_irq_status status) -{ - u32 value = readl(rot->regs + ROT_STATUS); - - value |= ROT_STATUS_IRQ_PENDING((u32)status); - - writel(value, rot->regs + ROT_STATUS); -} - -static void rotator_reg_set_src_buf_addr(struct rot_context *rot, - dma_addr_t addr, int i) -{ - writel(addr, rot->regs + ROT_SRC_BUF_ADDR(i)); -} - -static void rotator_reg_set_src_buf_size(struct rot_context *rot, u32 w, u32 h) -{ - u32 value = ROT_SRC_BUF_SIZE_H(h) | ROT_SRC_BUF_SIZE_W(w); - - writel(value, rot->regs + ROT_SRC_BUF_SIZE); -} - -static void rotator_reg_set_src_crop_pos(struct rot_context *rot, u32 x, u32 y) -{ - u32 value = ROT_SRC_CROP_POS_Y(y) | ROT_SRC_CROP_POS_X(x); - - writel(value, rot->regs + ROT_SRC_CROP_POS); -} - -static void rotator_reg_set_src_crop_size(struct rot_context *rot, u32 w, u32 h) -{ - u32 value = ROT_SRC_CROP_SIZE_H(h) | ROT_SRC_CROP_SIZE_W(w); - - writel(value, rot->regs + ROT_SRC_CROP_SIZE); -} - -static void rotator_reg_set_dst_buf_addr(struct rot_context *rot, - dma_addr_t addr, int i) -{ - writel(addr, rot->regs + ROT_DST_BUF_ADDR(i)); -} - -static void rotator_reg_set_dst_buf_size(struct rot_context *rot, u32 w, u32 h) -{ - u32 value = ROT_DST_BUF_SIZE_H(h) | ROT_DST_BUF_SIZE_W(w); - - writel(value, rot->regs + ROT_DST_BUF_SIZE); -} - -static void rotator_reg_set_dst_crop_pos(struct rot_context *rot, u32 x, u32 y) -{ - u32 value = ROT_DST_CROP_POS_Y(y) | ROT_DST_CROP_POS_X(x); - - writel(value, rot->regs + ROT_DST_CROP_POS); -} - -static void rotator_reg_get_dump(struct rot_context *rot) -{ - u32 value, i; - - for (i = 0; i <= ROT_DST_CROP_POS; i += 0x4) { - value = readl(rot->regs + i); - DRM_INFO("+0x%x: 0x%x", i, value); - } -} - -static bool rotator_check_format_n_handle_valid(u32 img_fmt, - u32 src_buf_handle_cnt, - u32 dst_buf_handle_cnt) -{ - bool ret = false; - - if ((src_buf_handle_cnt != dst_buf_handle_cnt) - || (src_buf_handle_cnt == 0)) - return ret; - - switch (img_fmt) { - case DRM_FORMAT_NV12M: - if (src_buf_handle_cnt == 2) - ret = true; - break; - case DRM_FORMAT_NV12: - case DRM_FORMAT_RGB888: - if (src_buf_handle_cnt == 1) - ret = true; - break; - default: - DRM_ERROR("invalid image format\n"); - break; - } - - return ret; -} - -static void rotator_align_size(struct rot_limit *limit, u32 mask, u32 *w, - u32 *h) -{ - u32 value; - - value = ROT_ALIGN(w, limit->align, mask); - if (value < limit->min_w) - *w = ROT_MIN(limit->min_w, mask); - else if (value > limit->max_w) - *w = ROT_MAX(limit->max_w, mask); - else - *w = value; - - value = ROT_ALIGN(h, limit->align, mask); - if (value < limit->min_h) - *h = ROT_MIN(limit->min_h, mask); - else if (value > limit->max_h) - *h = ROT_MAX(limit->max_h, mask); - else - *h = value; -} - -static void rotator_align_buffer(struct rot_context *rot, - struct rot_buffer *buf, - struct drm_exynos_rot_buffer *req_buf, - struct drm_exynos_rot_control *control) -{ - struct rot_limit_table *limit_tbl = rot->limit_tbl; - struct rot_limit *limit; - u32 mask; - - /* Get size limit */ - if (control->img_fmt == DRM_FORMAT_RGB888) - limit = &limit_tbl->rgb888; - else - limit = &limit_tbl->ycbcr420_2p; - - /* Get mask for rounding to nearest aligned value */ - mask = ~((1 << limit->align) - 1); - - /* For source buffer */ - buf->src_w = req_buf->src_w; - buf->src_h = req_buf->src_h; - rotator_align_size(limit, mask, &buf->src_w, &buf->src_h); - - /* For destination buffer */ - buf->dst_w = req_buf->dst_w; - buf->dst_h = req_buf->dst_h; - rotator_align_size(limit, mask, &buf->dst_w, &buf->dst_h); -} - -static bool rotator_check_crop_boundary(struct rot_buffer *buf, - struct drm_exynos_rot_control *control, - struct drm_exynos_rot_crop *crop) -{ - bool ret = true; - - /* Check source crop position */ - if ((crop->src_x + crop->src_w > buf->src_w) - || (crop->src_y + crop->src_h > buf->src_h)) - return false; - - /* Check destination crop position */ - switch (control->degree) { - case ROT_DEGREE_90: - case ROT_DEGREE_270: - if ((crop->dst_x + crop->src_h > buf->dst_w) - || (crop->dst_y + crop->src_w > buf->dst_h)) - ret = false; - break; - default: - if ((crop->dst_x + crop->src_w > buf->dst_w) - || (crop->dst_y + crop->src_h > buf->dst_h)) - ret = false; - break; - } - - return ret; -} - -static int rotator_iommu_map(struct rot_buffer *buf, - struct drm_exynos_rot_buffer *req_buf, - struct iommu_gem_map_params *params, - struct list_head *iommu_list) -{ - /* For source buffer */ - buf->src_cnt = 0; - while (buf->src_cnt < req_buf->src_cnt) { - buf->src_addr[buf->src_cnt] = exynos_drm_iommu_map_gem(params, - iommu_list, - req_buf->src_handle[buf->src_cnt], - IOMMU_ROTATOR); - if (!buf->src_addr[buf->src_cnt]) { - DRM_ERROR("failed to map src handle[%u]\n", - buf->src_cnt); - return -EINVAL; - } - buf->src_gem_obj[(buf->src_cnt)++] = params->gem_obj; - } - - /* For destination buffer */ - buf->dst_cnt = 0; - while (buf->dst_cnt < req_buf->dst_cnt) { - buf->dst_addr[buf->dst_cnt] = exynos_drm_iommu_map_gem(params, - iommu_list, - req_buf->dst_handle[buf->dst_cnt], - IOMMU_ROTATOR); - if (!buf->dst_addr[buf->dst_cnt]) { - DRM_ERROR("failed to map dst handle[%u]\n", - buf->dst_cnt); - return -EINVAL; - } - buf->dst_gem_obj[(buf->dst_cnt)++] = params->gem_obj; - } - - return 0; -} - -static void rotator_iommu_unmap(struct rot_buffer *buf, - struct iommu_gem_map_params *params) -{ - /* For destination buffer */ - while (buf->dst_cnt > 0) { - params->gem_obj = buf->dst_gem_obj[--(buf->dst_cnt)]; - exynos_drm_iommu_unmap_gem(params, - buf->dst_addr[buf->dst_cnt], - IOMMU_ROTATOR); - } - - /* For source buffer */ - while (buf->src_cnt > 0) { - params->gem_obj = buf->src_gem_obj[--(buf->src_cnt)]; - exynos_drm_iommu_unmap_gem(params, - buf->src_addr[buf->src_cnt], - IOMMU_ROTATOR); - } -} - -static void rotator_execute(struct rot_context *rot, - struct rot_buffer *buf, - struct drm_exynos_rot_control *control, - struct drm_exynos_rot_crop *crop) -{ - int i; - - pm_runtime_get_sync(rot->subdrv.dev); - - /* Set interrupt enable */ - rotator_reg_set_irq(rot, true); - - /* Set control registers */ - rotator_reg_set_format(rot, control->img_fmt); - rotator_reg_set_flip(rot, control->flip); - rotator_reg_set_rotation(rot, control->degree); - - /* Set source buffer address */ - for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++) - rotator_reg_set_src_buf_addr(rot, buf->src_addr[i], i); - - /* Set source buffer size */ - rotator_reg_set_src_buf_size(rot, buf->src_w, buf->src_h); - - /* Set destination buffer address */ - for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++) - rotator_reg_set_dst_buf_addr(rot, buf->dst_addr[i], i); - - /* Set destination buffer size */ - rotator_reg_set_dst_buf_size(rot, buf->dst_w, buf->dst_h); - - /* Set source crop image position */ - rotator_reg_set_src_crop_pos(rot, crop->src_x, crop->src_y); - - /* Set source crop image size */ - rotator_reg_set_src_crop_size(rot, crop->src_w, crop->src_h); - - /* Set destination crop image position */ - rotator_reg_set_dst_crop_pos(rot, crop->dst_x, crop->dst_y); - - /* Start rotator operation */ - rotator_reg_set_start(rot); -} - -int exynos_drm_rotator_exec_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv = file_priv->rot_priv; - struct device *dev = priv->dev; - struct rot_context *rot; - struct drm_exynos_rot_exec_data *req = data; - struct drm_exynos_rot_buffer *req_buf = &req->buf; - struct drm_exynos_rot_control *control = &req->control; - struct drm_exynos_rot_crop *crop = &req->crop; - struct rot_buffer buf; - struct iommu_gem_map_params params; - - if (!dev) { - DRM_ERROR("failed to get dev\n"); - return -ENODEV; - } - - rot = dev_get_drvdata(dev); - if (!rot) { - DRM_ERROR("failed to get drvdata\n"); - return -EFAULT; - } - - if (rot->suspended) { - DRM_ERROR("suspended state\n"); - return -EPERM; - } - - if (!rotator_check_format_n_handle_valid(control->img_fmt, - req_buf->src_cnt, - req_buf->dst_cnt)) { - DRM_ERROR("format or handles are invalid\n"); - return -EINVAL; - } - - init_completion(&rot->complete); - - /* Align buffer */ - rotator_align_buffer(rot, &buf, req_buf, control); - - /* Check crop boundary */ - if (!rotator_check_crop_boundary(&buf, control, crop)) { - DRM_ERROR("boundary errror\n"); - return -EINVAL; - } - - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; - - /* Map IOMMU */ - rot->exec_ret = rotator_iommu_map(&buf, req_buf, ¶ms, - &priv->iommu_list); - if (rot->exec_ret < 0) - goto err_iommu_map; - - /* Assign another src/dst_addr for NV12 image format */ - if (control->img_fmt == DRM_FORMAT_NV12) { - u32 size = crop->src_w * crop->src_h; - - buf.src_addr[buf.src_cnt + 1] = - buf.src_addr[buf.src_cnt] + size; - buf.dst_addr[buf.dst_cnt + 1] = - buf.dst_addr[buf.dst_cnt] + size; - } - - /* Execute */ - mutex_lock(&rot->exec_mutex); - rotator_execute(rot, &buf, control, crop); - if (!wait_for_completion_timeout(&rot->complete, 2 * HZ)) { - DRM_ERROR("timeout error\n"); - rot->exec_ret = -ETIMEDOUT; - mutex_unlock(&rot->exec_mutex); - goto err_iommu_map; - } - mutex_unlock(&rot->exec_mutex); - - /* Unmap IOMMU */ - rotator_iommu_unmap(&buf, ¶ms); - - return rot->exec_ret; - -err_iommu_map: - rotator_iommu_unmap(&buf, ¶ms); - return rot->exec_ret; -} -EXPORT_SYMBOL_GPL(exynos_drm_rotator_exec_ioctl); - -static irqreturn_t rotator_irq_thread(int irq, void *arg) -{ - struct rot_context *rot = (struct rot_context *)arg; - enum rot_irq_status irq_status; - unsigned long flags; - - pm_qos_update_request(&rot->pm_qos, 0); - - /* Get execution result */ - spin_lock_irqsave(&rot->irq_lock, flags); - irq_status = rotator_reg_get_irq_status(rot); - rotator_reg_set_irq_status_clear(rot, irq_status); - spin_unlock_irqrestore(&rot->irq_lock, flags); - - rot->exec_ret = 0; - if (irq_status != ROT_IRQ_STATUS_COMPLETE) { - DRM_ERROR("the SFR is set illegally\n"); - rot->exec_ret = -EINVAL; - rotator_reg_get_dump(rot); - } - - pm_runtime_put(rot->subdrv.dev); - - complete(&rot->complete); - - return IRQ_HANDLED; -} - -static int rotator_subdrv_open(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev, "failed to allocate priv\n"); - return -ENOMEM; - } - - priv->dev = dev; - INIT_LIST_HEAD(&priv->iommu_list); - - file_priv->rot_priv = priv; - - return 0; -} - -static void rotator_subdrv_close(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv = file_priv->rot_priv; - struct iommu_gem_map_params params; - struct iommu_info_node *node, *n; - - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; - - list_for_each_entry_safe(node, n, &priv->iommu_list, list) { - params.gem_obj = node->gem_obj; - exynos_drm_iommu_unmap_gem(¶ms, node->dma_addr, - IOMMU_ROTATOR); - list_del(&node->list); - kfree(node); - node = NULL; - } - - kfree(priv); - - return; -} - -static int __devinit rotator_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rot_context *rot; - struct resource *res; - struct exynos_drm_subdrv *subdrv; - int ret; - - rot = kzalloc(sizeof(*rot), GFP_KERNEL); - if (!rot) { - dev_err(dev, "failed to allocate rot\n"); - return -ENOMEM; - } - - rot->limit_tbl = (struct rot_limit_table *) - platform_get_device_id(pdev)->driver_data; - - mutex_init(&rot->exec_mutex); - spin_lock_init(&rot->irq_lock); - - ret = exynos_drm_iommu_setup(dev); - if (ret < 0) { - dev_err(dev, "failed to setup iommu\n"); - goto err_iommu_setup; - } - - ret = exynos_drm_iommu_activate(dev); - if (ret < 0) { - dev_err(dev, "failed to activate iommu\n"); - goto err_iommu_activate; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "failed to find registers\n"); - ret = -ENOENT; - goto err_get_resource; - } - - rot->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!rot->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_get_resource; - } - - rot->regs = ioremap(res->start, resource_size(res)); - if (!rot->regs) { - dev_err(dev, "failed to map register\n"); - ret = -ENXIO; - goto err_ioremap; - } - - rot->irq = platform_get_irq(pdev, 0); - if (rot->irq < 0) { - dev_err(dev, "faild to get irq\n"); - ret = rot->irq; - goto err_get_irq; - } - - ret = request_threaded_irq(rot->irq, NULL, rotator_irq_thread, - IRQF_ONESHOT, "drm_rotator", rot); - if (ret < 0) { - dev_err(dev, "failed to request irq\n"); - goto err_get_irq; - } - - rot->clock = clk_get(dev, "rotator"); - if (IS_ERR_OR_NULL(rot->clock)) { - dev_err(dev, "faild to get clock\n"); - ret = PTR_ERR(rot->clock); - goto err_clk_get; - } - - pm_runtime_enable(dev); - pm_qos_add_request(&rot->pm_qos, PM_QOS_BUS_DMA_THROUGHPUT, 0); - - subdrv = &rot->subdrv; - subdrv->dev = dev; - subdrv->open = rotator_subdrv_open; - subdrv->close = rotator_subdrv_close; - - platform_set_drvdata(pdev, rot); - - ret = exynos_drm_subdrv_register(subdrv); - if (ret < 0) { - dev_err(dev, "failed to register drm rotator device\n"); - goto err_subdrv_register; - } - - dev_info(dev, "The exynos rotator is probed successfully\n"); - - return 0; - -err_subdrv_register: - pm_runtime_disable(dev); - clk_put(rot->clock); -err_clk_get: - free_irq(rot->irq, rot); -err_get_irq: - iounmap(rot->regs); -err_ioremap: - release_resource(rot->regs_res); - kfree(rot->regs_res); -err_get_resource: - exynos_drm_iommu_deactivate(dev); -err_iommu_activate: - exynos_drm_iommu_cleanup(dev); -err_iommu_setup: - kfree(rot); - return ret; -} - -static int __devexit rotator_remove(struct platform_device *pdev) -{ - struct rot_context *rot = platform_get_drvdata(pdev); - - pm_qos_remove_request(&rot->pm_qos); - - exynos_drm_subdrv_unregister(&rot->subdrv); - - pm_runtime_disable(&pdev->dev); - clk_put(rot->clock); - - free_irq(rot->irq, rot); - - iounmap(rot->regs); - - release_resource(rot->regs_res); - kfree(rot->regs_res); - - exynos_drm_iommu_deactivate(&pdev->dev); - exynos_drm_iommu_cleanup(&pdev->dev); - - kfree(rot); - - return 0; -} - -struct rot_limit_table rot_limit_tbl = { - .ycbcr420_2p = { - .min_w = 32, - .min_h = 32, - .max_w = SZ_32K, - .max_h = SZ_32K, - .align = 3, - }, - .rgb888 = { - .min_w = 8, - .min_h = 8, - .max_w = SZ_8K, - .max_h = SZ_8K, - .align = 2, - }, -}; - -struct platform_device_id rotator_driver_ids[] = { - { - .name = "exynos-rot", - .driver_data = (unsigned long)&rot_limit_tbl, - }, - {}, -}; - -#ifdef CONFIG_PM_SLEEP -static int rotator_suspend(struct device *dev) -{ - struct rot_context *rot = dev_get_drvdata(dev); - - /* Check & wait for running state */ - mutex_lock(&rot->exec_mutex); - mutex_unlock(&rot->exec_mutex); - - rot->suspended = true; - - exynos_drm_iommu_deactivate(dev); - - return 0; -} - -static int rotator_resume(struct device *dev) -{ - struct rot_context *rot = dev_get_drvdata(dev); - - rot->suspended = false; - - exynos_drm_iommu_activate(dev); - - return 0; -} -#endif - -#ifdef CONFIG_PM_RUNTIME -static int rotator_runtime_suspend(struct device *dev) -{ - struct rot_context *rot = dev_get_drvdata(dev); - - clk_disable(rot->clock); - - return 0; -} - -static int rotator_runtime_resume(struct device *dev) -{ - struct rot_context *rot = dev_get_drvdata(dev); - - clk_enable(rot->clock); - pm_qos_update_request(&rot->pm_qos, 400000); - - return 0; -} -#endif - -static const struct dev_pm_ops rotator_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume) - SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume, - NULL) -}; - -struct platform_driver rotator_driver = { - .probe = rotator_probe, - .remove = __devexit_p(rotator_remove), - .id_table = rotator_driver_ids, - .driver = { - .name = "exynos-rot", - .owner = THIS_MODULE, - .pm = &rotator_pm_ops, - }, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_rotator.h b/drivers/gpu/drm/exynos_tmp/exynos_drm_rotator.h deleted file mode 100644 index 5f383d5..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_rotator.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics Co.Ltd - * Authors: YoungJun Cho <yj44.cho@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundationr - */ - -#ifndef _EXYNOS_DRM_ROTATOR_H_ -#define _EXYNOS_DRM_ROTATOR_H_ - -#ifdef CONFIG_DRM_EXYNOS_ROTATOR -extern int exynos_drm_rotator_exec_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -#else -static inline int exynos_drm_rotator_exec_ioctl(struct drm_device *dev, - void *data, - struct drm_file *file_priv) -{ - return -ENOTTY; -} -#endif - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_ump.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_ump.c deleted file mode 100644 index fd9ba2a..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_ump.c +++ /dev/null @@ -1,151 +0,0 @@ -/* exynos_drm_ump.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: Inki Dae <inki.dae@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "drmP.h" -#include "drm.h" - -#include <drm/exynos_drm.h> - -#include "exynos_drm_gem.h" -#include "ump_kernel_interface_ref_drv.h" - -static unsigned int exynos_drm_ump_get_handle(unsigned int id) -{ - return (unsigned int)ump_dd_handle_get((ump_secure_id)id); -} - -static int exynos_drm_ump_add_buffer(void *obj, - unsigned int *handle, unsigned int *id) -{ - struct exynos_drm_gem_obj *gem_obj = obj; - struct exynos_drm_gem_buf *buf = gem_obj->buffer; - ump_dd_physical_block *ump_mem_desc; - unsigned int nblocks; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (IS_NONCONTIG_BUFFER(gem_obj->flags)) { - unsigned int i = 0; - - if (!buf->pages) - return -EFAULT; - - nblocks = gem_obj->size >> PAGE_SHIFT; - ump_mem_desc = kzalloc(sizeof(*ump_mem_desc) * nblocks, - GFP_KERNEL); - if (!ump_mem_desc) { - DRM_ERROR("failed to alloc ump_mem_desc.\n"); - return -ENOMEM; - } - - /* - * if EXYNOS_BO_NONCONTIG type, gem object would already - * have pages allocated by gem creation so contain page - * frame numbers of all pages into ump descriptors. - */ - while (i < nblocks) { - ump_mem_desc[i].addr = - page_to_pfn(buf->pages[i]) << PAGE_SHIFT; - ump_mem_desc[i].size = PAGE_SIZE; - i++; - } - } else { - nblocks = 1; - - ump_mem_desc = kzalloc(sizeof(*ump_mem_desc), GFP_KERNEL); - if (!ump_mem_desc) { - DRM_ERROR("failed to alloc ump_mem_desc.\n"); - return -ENOMEM; - } - - /* - * it EXYNOS_DRM_GEM_PC type, gem would have just one - * physically continuous buffer so let a ump descriptor - * have one buffer address. - */ - ump_mem_desc[0].addr = (unsigned long)buf->dma_addr; - ump_mem_desc[0].size = buf->size; - } - - /* - * register memory information to ump descriptor table through - * the ump descriptor data and then return ump handle to it so that - * user can access the memory region through it. - */ - *handle = (unsigned int) - ump_dd_handle_create_from_phys_blocks(ump_mem_desc, nblocks); - if (!(*handle)) { - DRM_ERROR("failed to create ump handle.\n"); - kfree(ump_mem_desc); - return -EINVAL; - } - - *id = ump_dd_secure_id_get((ump_dd_handle)*handle); - - kfree(ump_mem_desc); - - DRM_DEBUG_KMS("ump handle : 0x%x, secure id = %d\n", *handle, *id); - DRM_INFO("ump handle : 0x%x, secure id = %d\n", *handle, *id); - - return 0; -} - -static void exynos_drm_ump_release_buffer(unsigned int handle) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!handle) { - DRM_DEBUG_KMS("invalid ump handle.\n"); - return; - } - - ump_dd_reference_release((ump_dd_handle)handle); -} - -static struct exynos_drm_private_cb ump_callback = { - .get_handle = exynos_drm_ump_get_handle, - .add_buffer = exynos_drm_ump_add_buffer, - .release_buffer = exynos_drm_ump_release_buffer, -}; - -static int exynos_drm_ump_init(void) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_drm_priv_cb_register(&ump_callback); - - return 0; -} - -static void exynos_drm_ump_exit(void) -{ -} - -subsys_initcall(exynos_drm_ump_init); -module_exit(exynos_drm_ump_exit); - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM UMP Backend Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_drm_vidi.c b/drivers/gpu/drm/exynos_tmp/exynos_drm_vidi.c deleted file mode 100644 index 876e460..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_drm_vidi.c +++ /dev/null @@ -1,680 +0,0 @@ -/* exynos_drm_vidi.c - * - * Copyright (C) 2012 Samsung Electronics Co.Ltd - * Authors: - * Inki Dae <inki.dae@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ -#include "drmP.h" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> - -#include <drm/exynos_drm.h> - -#include "drm_edid.h" -#include "drm_crtc_helper.h" - -#include "exynos_drm_drv.h" -#include "exynos_drm_crtc.h" -#include "exynos_drm_encoder.h" - -/* vidi has totally three virtual windows. */ -#define WINDOWS_NR 3 - -#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev)) - -struct vidi_win_data { - unsigned int offset_x; - unsigned int offset_y; - unsigned int ovl_width; - unsigned int ovl_height; - unsigned int fb_width; - unsigned int fb_height; - unsigned int bpp; - dma_addr_t dma_addr; - void __iomem *vaddr; - unsigned int buf_offsize; - unsigned int line_size; /* bytes */ - bool enabled; -}; - -struct vidi_context { - struct exynos_drm_subdrv subdrv; - struct drm_crtc *crtc; - struct vidi_win_data win_data[WINDOWS_NR]; - struct edid *raw_edid; - unsigned int clkdiv; - unsigned int default_win; - unsigned long irq_flags; - unsigned int connected; - bool vblank_on; - bool suspended; - struct work_struct work; - struct mutex lock; -}; - -static const char fake_edid_info[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, - 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd, - 0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00, - 0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, - 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, - 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, - 0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1, - 0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83, - 0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00, - 0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c, - 0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a, - 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00, - 0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06 -}; - -static void vidi_fake_vblank_handler(struct work_struct *work); - -static bool vidi_display_is_connected(struct device *dev) -{ - struct vidi_context *ctx = get_vidi_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * connection request would come from user side - * to do hotplug through specific ioctl. - */ - return ctx->connected ? true : false; -} - -static int vidi_get_edid(struct device *dev, struct drm_connector *connector, - u8 *edid, int len) -{ - struct vidi_context *ctx = get_vidi_context(dev); - struct edid *raw_edid; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * the edid data comes from user side and it would be set - * to ctx->raw_edid through specific ioctl. - */ - if (!ctx->raw_edid) { - DRM_DEBUG_KMS("raw_edid is null.\n"); - return -EFAULT; - } - - raw_edid = kzalloc(len, GFP_KERNEL); - if (!raw_edid) { - DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); - return -ENOMEM; - } - - memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions) - * EDID_LENGTH, len)); - - /* attach the edid data to connector. */ - connector->display_info.raw_edid = (char *)raw_edid; - - memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions) - * EDID_LENGTH, len)); - - return 0; -} - -static void *vidi_get_panel(struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ - - return NULL; -} - -static int vidi_check_timing(struct device *dev, void *timing) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ - - return 0; -} - -static int vidi_display_power_on(struct device *dev, int mode) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO */ - - return 0; -} - -static struct exynos_drm_display_ops vidi_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_VIDI, - .is_connected = vidi_display_is_connected, - .get_edid = vidi_get_edid, - .get_panel = vidi_get_panel, - .check_timing = vidi_check_timing, - .power_on = vidi_display_power_on, -}; - -static void vidi_dpms(struct device *subdrv_dev, int mode) -{ - struct vidi_context *ctx = get_vidi_context(subdrv_dev); - - DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); - - mutex_lock(&ctx->lock); - - switch (mode) { - case DRM_MODE_DPMS_ON: - /* TODO. */ - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - /* TODO. */ - break; - default: - DRM_DEBUG_KMS("unspecified mode %d\n", mode); - break; - } - - mutex_unlock(&ctx->lock); -} - -static void vidi_apply(struct device *subdrv_dev) -{ - struct vidi_context *ctx = get_vidi_context(subdrv_dev); - struct exynos_drm_manager *mgr = ctx->subdrv.manager; - struct exynos_drm_manager_ops *mgr_ops = mgr->ops; - struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; - struct vidi_win_data *win_data; - int i; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - for (i = 0; i < WINDOWS_NR; i++) { - win_data = &ctx->win_data[i]; - if (win_data->enabled && (ovl_ops && ovl_ops->commit)) - ovl_ops->commit(subdrv_dev, i); - } - - if (mgr_ops && mgr_ops->commit) - mgr_ops->commit(subdrv_dev); -} - -static void vidi_commit(struct device *dev) -{ - struct vidi_context *ctx = get_vidi_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; -} - -static int vidi_enable_vblank(struct device *dev) -{ - struct vidi_context *ctx = get_vidi_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return -EPERM; - - if (!test_and_set_bit(0, &ctx->irq_flags)) - ctx->vblank_on = true; - - return 0; -} - -static void vidi_disable_vblank(struct device *dev) -{ - struct vidi_context *ctx = get_vidi_context(dev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; - - if (test_and_clear_bit(0, &ctx->irq_flags)) - ctx->vblank_on = false; -} - -static struct exynos_drm_manager_ops vidi_manager_ops = { - .dpms = vidi_dpms, - .apply = vidi_apply, - .commit = vidi_commit, - .enable_vblank = vidi_enable_vblank, - .disable_vblank = vidi_disable_vblank, -}; - -static void vidi_win_mode_set(struct device *dev, - struct exynos_drm_overlay *overlay) -{ - struct vidi_context *ctx = get_vidi_context(dev); - struct vidi_win_data *win_data; - int win; - unsigned long offset; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!overlay) { - dev_err(dev, "overlay is NULL\n"); - return; - } - - win = overlay->zpos; - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - offset = overlay->fb_x * (overlay->bpp >> 3); - offset += overlay->fb_y * overlay->pitch; - - DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); - - win_data = &ctx->win_data[win]; - - win_data->offset_x = overlay->crtc_x; - win_data->offset_y = overlay->crtc_y; - win_data->ovl_width = overlay->crtc_width; - win_data->ovl_height = overlay->crtc_height; - win_data->fb_width = overlay->fb_width; - win_data->fb_height = overlay->fb_height; - win_data->dma_addr = overlay->dma_addr[0] + offset; - win_data->vaddr = overlay->vaddr[0] + offset; - win_data->bpp = overlay->bpp; - win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * - (overlay->bpp >> 3); - win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); - - /* - * some parts of win_data should be transferred to user side - * through specific ioctl. - */ - - DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", - win_data->offset_x, win_data->offset_y); - DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - win_data->ovl_width, win_data->ovl_height); - DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", - (unsigned long)win_data->dma_addr, - (unsigned long)win_data->vaddr); - DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", - overlay->fb_width, overlay->crtc_width); -} - -static void vidi_win_commit(struct device *dev, int zpos) -{ - struct vidi_context *ctx = get_vidi_context(dev); - struct vidi_win_data *win_data; - int win = zpos; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (ctx->suspended) - return; - - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - win_data = &ctx->win_data[win]; - - win_data->enabled = true; - - DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr); - - if (ctx->vblank_on) - schedule_work(&ctx->work); -} - -static void vidi_win_disable(struct device *dev, int zpos) -{ - struct vidi_context *ctx = get_vidi_context(dev); - struct vidi_win_data *win_data; - int win = zpos; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (win == DEFAULT_ZPOS) - win = ctx->default_win; - - if (win < 0 || win > WINDOWS_NR) - return; - - win_data = &ctx->win_data[win]; - win_data->enabled = false; - - /* TODO. */ -} - -static struct exynos_drm_overlay_ops vidi_overlay_ops = { - .mode_set = vidi_win_mode_set, - .commit = vidi_win_commit, - .disable = vidi_win_disable, -}; - -static struct exynos_drm_manager vidi_manager = { - .pipe = -1, - .ops = &vidi_manager_ops, - .overlay_ops = &vidi_overlay_ops, - .display_ops = &vidi_display_ops, -}; - -static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc) -{ - struct exynos_drm_private *dev_priv = drm_dev->dev_private; - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned long flags; - bool is_checked = false; - - spin_lock_irqsave(&drm_dev->event_lock, flags); - - list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, - base.link) { - /* if event's pipe isn't same as crtc then ignore it. */ - if (crtc != e->pipe) - continue; - - is_checked = true; - - do_gettimeofday(&now); - e->event.sequence = 0; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; - - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - } - - if (is_checked) { - /* - * call drm_vblank_put only in case that drm_vblank_get was - * called. - */ - if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) - drm_vblank_put(drm_dev, crtc); - - /* - * don't off vblank if vblank_disable_allowed is 1, - * because vblank would be off by timer handler. - */ - if (!drm_dev->vblank_disable_allowed) - drm_vblank_off(drm_dev, crtc); - } - - spin_unlock_irqrestore(&drm_dev->event_lock, flags); -} - -static void vidi_fake_vblank_handler(struct work_struct *work) -{ - struct vidi_context *ctx = container_of(work, struct vidi_context, - work); - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct exynos_drm_manager *manager = subdrv->manager; - - if (manager->pipe < 0) - return; - - /* FIXME!!! */ - msleep(20); - - drm_handle_vblank(subdrv->drm_dev, manager->pipe); - vidi_finish_pageflip(subdrv->drm_dev, manager->pipe); -} - -static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * enable drm irq mode. - * - with irq_enabled = 1, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - drm_dev->irq_enabled = 1; - - /* - * with vblank_disable_allowed = 1, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) - */ - drm_dev->vblank_disable_allowed = 1; - - return 0; -} - -static void vidi_subdrv_remove(struct drm_device *drm_dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* TODO. */ -} - -static int vidi_power_on(struct vidi_context *ctx, bool enable) -{ - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->dev; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (enable != false && enable != true) - return -EINVAL; - - if (enable) { - ctx->suspended = false; - - /* if vblank was enabled status, enable it again. */ - if (test_and_clear_bit(0, &ctx->irq_flags)) - vidi_enable_vblank(dev); - - vidi_apply(dev); - } else { - ctx->suspended = true; - } - - return 0; -} - -static int vidi_show_connection(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int rc; - struct vidi_context *ctx = get_vidi_context(dev); - - mutex_lock(&ctx->lock); - - rc = sprintf(buf, "%d\n", ctx->connected); - - mutex_unlock(&ctx->lock); - - return rc; -} - -static int vidi_store_connection(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct vidi_context *ctx = get_vidi_context(dev); - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ret = kstrtoint(buf, 0, &ctx->connected); - if (ret) - return ret; - - if (ctx->connected > 1) - return -EINVAL; - - DRM_DEBUG_KMS("requested connection.\n"); - - drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); - - return len; -} - -static DEVICE_ATTR(connection, 0644, vidi_show_connection, - vidi_store_connection); - -int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file_priv) -{ - struct vidi_context *ctx = NULL; - struct drm_encoder *encoder; - struct exynos_drm_manager *manager; - struct exynos_drm_display_ops *display_ops; - struct drm_exynos_vidi_connection *vidi = data; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (!vidi) { - DRM_DEBUG_KMS("user data for vidi is null.\n"); - return -EINVAL; - } - - if (!vidi->edid) { - DRM_DEBUG_KMS("edid data is null.\n"); - return -EINVAL; - } - - if (vidi->connection > 1) { - DRM_DEBUG_KMS("connection should be 0 or 1.\n"); - return -EINVAL; - } - - list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, - head) { - manager = exynos_drm_get_manager(encoder); - display_ops = manager->display_ops; - - if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) { - ctx = get_vidi_context(manager->dev); - break; - } - } - - if (!ctx) { - DRM_DEBUG_KMS("not found virtual device type encoder.\n"); - return -EINVAL; - } - - if (ctx->connected == vidi->connection) { - DRM_DEBUG_KMS("same connection request.\n"); - return -EINVAL; - } - - if (vidi->connection) - ctx->raw_edid = (struct edid *)vidi->edid; - - ctx->connected = vidi->connection; - drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); - - return 0; -} - -static int __devinit vidi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct vidi_context *ctx; - struct exynos_drm_subdrv *subdrv; - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->default_win = 0; - - INIT_WORK(&ctx->work, vidi_fake_vblank_handler); - - /* for test */ - ctx->raw_edid = (struct edid *)fake_edid_info; - - subdrv = &ctx->subdrv; - subdrv->dev = dev; - subdrv->manager = &vidi_manager; - subdrv->probe = vidi_subdrv_probe; - subdrv->remove = vidi_subdrv_remove; - - mutex_init(&ctx->lock); - - platform_set_drvdata(pdev, ctx); - - ret = device_create_file(&pdev->dev, &dev_attr_connection); - if (ret < 0) - DRM_INFO("failed to create connection sysfs.\n"); - - exynos_drm_subdrv_register(subdrv); - - return 0; -} - -static int __devexit vidi_remove(struct platform_device *pdev) -{ - struct vidi_context *ctx = platform_get_drvdata(pdev); - - DRM_DEBUG_KMS("%s\n", __FILE__); - - exynos_drm_subdrv_unregister(&ctx->subdrv); - - kfree(ctx); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int vidi_suspend(struct device *dev) -{ - struct vidi_context *ctx = get_vidi_context(dev); - - return vidi_power_on(ctx, false); -} - -static int vidi_resume(struct device *dev) -{ - struct vidi_context *ctx = get_vidi_context(dev); - - return vidi_power_on(ctx, true); -} -#endif - -static const struct dev_pm_ops vidi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume) -}; - -struct platform_driver vidi_driver = { - .probe = vidi_probe, - .remove = __devexit_p(vidi_remove), - .driver = { - .name = "exynos-drm-vidi", - .owner = THIS_MODULE, - .pm = &vidi_pm_ops, - }, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_hdmi.c b/drivers/gpu/drm/exynos_tmp/exynos_hdmi.c deleted file mode 100644 index 0c44cb7..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_hdmi.c +++ /dev/null @@ -1,2481 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Seung-Woo Kim <sw0312.kim@samsung.com> - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * - * Based on drivers/media/video/s5p-tv/hdmi_drv.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include "drmP.h" -#include "drm_edid.h" -#include "drm_crtc_helper.h" - -#include "regs-hdmi.h" - -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <linux/i2c.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/delay.h> -#include <linux/pm_runtime.h> -#include <linux/clk.h> -#include <linux/regulator/consumer.h> - -#include <drm/exynos_drm.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_hdmi.h" - -#include "exynos_hdmi.h" - -#define MAX_WIDTH 1920 -#define MAX_HEIGHT 1080 -#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) - -struct hdmi_resources { - struct clk *hdmi; - struct clk *sclk_hdmi; - struct clk *sclk_pixel; - struct clk *sclk_hdmiphy; - struct clk *hdmiphy; - struct regulator_bulk_data *regul_bulk; - int regul_count; -}; - -struct hdmi_context { - struct device *dev; - struct drm_device *drm_dev; - bool hpd; - bool powered; - bool is_v13; - bool dvi_mode; - struct mutex hdmi_mutex; - - struct resource *regs_res; - void __iomem *regs; - unsigned int external_irq; - unsigned int internal_irq; - - struct i2c_client *ddc_port; - struct i2c_client *hdmiphy_port; - - /* current hdmiphy conf index */ - int cur_conf; - - struct hdmi_resources res; - void *parent_ctx; - - void (*cfg_hpd)(bool external); - int (*get_hpd)(void); -}; - -/* HDMI Version 1.3 */ -static const u8 hdmiphy_v13_conf27[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, -}; - -static const u8 hdmiphy_v13_conf27_027[32] = { - 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, - 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, -}; - -static const u8 hdmiphy_v13_conf74_175[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, - 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, -}; - -static const u8 hdmiphy_v13_conf74_25[32] = { - 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, - 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0, - 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, -}; - -static const u8 hdmiphy_v13_conf148_5[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, - 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, -}; - -struct hdmi_v13_tg_regs { - u8 cmd; - u8 h_fsz_l; - u8 h_fsz_h; - u8 hact_st_l; - u8 hact_st_h; - u8 hact_sz_l; - u8 hact_sz_h; - u8 v_fsz_l; - u8 v_fsz_h; - u8 vsync_l; - u8 vsync_h; - u8 vsync2_l; - u8 vsync2_h; - u8 vact_st_l; - u8 vact_st_h; - u8 vact_sz_l; - u8 vact_sz_h; - u8 field_chg_l; - u8 field_chg_h; - u8 vact_st2_l; - u8 vact_st2_h; - u8 vsync_top_hdmi_l; - u8 vsync_top_hdmi_h; - u8 vsync_bot_hdmi_l; - u8 vsync_bot_hdmi_h; - u8 field_top_hdmi_l; - u8 field_top_hdmi_h; - u8 field_bot_hdmi_l; - u8 field_bot_hdmi_h; -}; - -struct hdmi_v13_core_regs { - u8 h_blank[2]; - u8 v_blank[3]; - u8 h_v_line[3]; - u8 vsync_pol[1]; - u8 int_pro_mode[1]; - u8 v_blank_f[3]; - u8 h_sync_gen[3]; - u8 v_sync_gen1[3]; - u8 v_sync_gen2[3]; - u8 v_sync_gen3[3]; -}; - -struct hdmi_v13_preset_conf { - struct hdmi_v13_core_regs core; - struct hdmi_v13_tg_regs tg; -}; - -struct hdmi_v13_conf { - int width; - int height; - int vrefresh; - bool interlace; - const u8 *hdmiphy_data; - const struct hdmi_v13_preset_conf *conf; -}; - -static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = { - .core = { - .h_blank = {0x8a, 0x00}, - .v_blank = {0x0d, 0x6a, 0x01}, - .h_v_line = {0x0d, 0xa2, 0x35}, - .vsync_pol = {0x01}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, - .h_sync_gen = {0x0e, 0x30, 0x11}, - .v_sync_gen1 = {0x0f, 0x90, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x5a, 0x03, /* h_fsz */ - 0x8a, 0x00, 0xd0, 0x02, /* hact */ - 0x0d, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0xe0, 0x01, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, -}; - -static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = { - .core = { - .h_blank = {0x72, 0x01}, - .v_blank = {0xee, 0xf2, 0x00}, - .h_v_line = {0xee, 0x22, 0x67}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x6c, 0x50, 0x02}, - .v_sync_gen1 = {0x0a, 0x50, 0x00}, - .v_sync_gen2 = {0x01, 0x10, 0x00}, - .v_sync_gen3 = {0x01, 0x10, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x72, 0x06, /* h_fsz */ - 0x71, 0x01, 0x01, 0x05, /* hact */ - 0xee, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x1e, 0x00, 0xd0, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, -}; - -static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = { - .core = { - .h_blank = {0xd0, 0x02}, - .v_blank = {0x32, 0xB2, 0x00}, - .h_v_line = {0x65, 0x04, 0xa5}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x01}, - .v_blank_f = {0x49, 0x2A, 0x23}, - .h_sync_gen = {0x0E, 0xEA, 0x08}, - .v_sync_gen1 = {0x07, 0x20, 0x00}, - .v_sync_gen2 = {0x39, 0x42, 0x23}, - .v_sync_gen3 = {0x38, 0x87, 0x73}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x50, 0x0A, /* h_fsz */ - 0xCF, 0x02, 0x81, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x16, 0x00, 0x1c, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, -}; - -static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = { - .core = { - .h_blank = {0xd0, 0x02}, - .v_blank = {0x65, 0x6c, 0x01}, - .h_v_line = {0x65, 0x04, 0xa5}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x0e, 0xea, 0x08}, - .v_sync_gen1 = {0x09, 0x40, 0x00}, - .v_sync_gen2 = {0x01, 0x10, 0x00}, - .v_sync_gen3 = {0x01, 0x10, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x50, 0x0A, /* h_fsz */ - 0xCF, 0x02, 0x81, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, -}; - -static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = { - .core = { - .h_blank = {0x18, 0x01}, - .v_blank = {0x32, 0xB2, 0x00}, - .h_v_line = {0x65, 0x84, 0x89}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x01}, - .v_blank_f = {0x49, 0x2A, 0x23}, - .h_sync_gen = {0x56, 0x08, 0x02}, - .v_sync_gen1 = {0x07, 0x20, 0x00}, - .v_sync_gen2 = {0x39, 0x42, 0x23}, - .v_sync_gen3 = {0xa4, 0x44, 0x4a}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x17, 0x01, 0x81, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x16, 0x00, 0x1c, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, -}; - -static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = { - .core = { - .h_blank = {0x18, 0x01}, - .v_blank = {0x65, 0x6c, 0x01}, - .h_v_line = {0x65, 0x84, 0x89}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x56, 0x08, 0x02}, - .v_sync_gen1 = {0x09, 0x40, 0x00}, - .v_sync_gen2 = {0x01, 0x10, 0x00}, - .v_sync_gen3 = {0x01, 0x10, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x17, 0x01, 0x81, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, -}; - -static const struct hdmi_v13_conf hdmi_v13_confs[] = { - { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, - { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, - { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p }, - { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 }, - { 1920, 1080, 50, false, hdmiphy_v13_conf148_5, - &hdmi_v13_conf_1080p50 }, - { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 }, - { 1920, 1080, 60, false, hdmiphy_v13_conf148_5, - &hdmi_v13_conf_1080p60 }, -}; - -/* HDMI Version 1.4 */ -static const u8 hdmiphy_conf27_027[32] = { - 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08, - 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80, - 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, -}; - -static const u8 hdmiphy_conf74_176[32] = { - 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x5b, 0xef, 0x08, - 0x81, 0xa0, 0xb9, 0xd8, 0x45, 0xa0, 0xac, 0x80, - 0x5a, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, -}; - -static const u8 hdmiphy_conf74_25[32] = { - 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08, - 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, - 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, -}; - -static const u8 hdmiphy_conf148_5[32] = { - 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08, - 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, - 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, - 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, -}; - -struct hdmi_tg_regs { - u8 cmd; - u8 h_fsz_l; - u8 h_fsz_h; - u8 hact_st_l; - u8 hact_st_h; - u8 hact_sz_l; - u8 hact_sz_h; - u8 v_fsz_l; - u8 v_fsz_h; - u8 vsync_l; - u8 vsync_h; - u8 vsync2_l; - u8 vsync2_h; - u8 vact_st_l; - u8 vact_st_h; - u8 vact_sz_l; - u8 vact_sz_h; - u8 field_chg_l; - u8 field_chg_h; - u8 vact_st2_l; - u8 vact_st2_h; - u8 vact_st3_l; - u8 vact_st3_h; - u8 vact_st4_l; - u8 vact_st4_h; - u8 vsync_top_hdmi_l; - u8 vsync_top_hdmi_h; - u8 vsync_bot_hdmi_l; - u8 vsync_bot_hdmi_h; - u8 field_top_hdmi_l; - u8 field_top_hdmi_h; - u8 field_bot_hdmi_l; - u8 field_bot_hdmi_h; - u8 tg_3d; -}; - -struct hdmi_core_regs { - u8 h_blank[2]; - u8 v2_blank[2]; - u8 v1_blank[2]; - u8 v_line[2]; - u8 h_line[2]; - u8 hsync_pol[1]; - u8 vsync_pol[1]; - u8 int_pro_mode[1]; - u8 v_blank_f0[2]; - u8 v_blank_f1[2]; - u8 h_sync_start[2]; - u8 h_sync_end[2]; - u8 v_sync_line_bef_2[2]; - u8 v_sync_line_bef_1[2]; - u8 v_sync_line_aft_2[2]; - u8 v_sync_line_aft_1[2]; - u8 v_sync_line_aft_pxl_2[2]; - u8 v_sync_line_aft_pxl_1[2]; - u8 v_blank_f2[2]; /* for 3D mode */ - u8 v_blank_f3[2]; /* for 3D mode */ - u8 v_blank_f4[2]; /* for 3D mode */ - u8 v_blank_f5[2]; /* for 3D mode */ - u8 v_sync_line_aft_3[2]; - u8 v_sync_line_aft_4[2]; - u8 v_sync_line_aft_5[2]; - u8 v_sync_line_aft_6[2]; - u8 v_sync_line_aft_pxl_3[2]; - u8 v_sync_line_aft_pxl_4[2]; - u8 v_sync_line_aft_pxl_5[2]; - u8 v_sync_line_aft_pxl_6[2]; - u8 vact_space_1[2]; - u8 vact_space_2[2]; - u8 vact_space_3[2]; - u8 vact_space_4[2]; - u8 vact_space_5[2]; - u8 vact_space_6[2]; -}; - -struct hdmi_preset_conf { - struct hdmi_core_regs core; - struct hdmi_tg_regs tg; -}; - -struct hdmi_conf { - int width; - int height; - int vrefresh; - bool interlace; - const u8 *hdmiphy_data; - const struct hdmi_preset_conf *conf; -}; - -static const struct hdmi_preset_conf hdmi_conf_480p60 = { - .core = { - .h_blank = {0x8a, 0x00}, - .v2_blank = {0x0d, 0x02}, - .v1_blank = {0x2d, 0x00}, - .v_line = {0x0d, 0x02}, - .h_line = {0x5a, 0x03}, - .hsync_pol = {0x01}, - .vsync_pol = {0x01}, - .int_pro_mode = {0x00}, - .v_blank_f0 = {0xff, 0xff}, - .v_blank_f1 = {0xff, 0xff}, - .h_sync_start = {0x0e, 0x00}, - .h_sync_end = {0x4c, 0x00}, - .v_sync_line_bef_2 = {0x0f, 0x00}, - .v_sync_line_bef_1 = {0x09, 0x00}, - .v_sync_line_aft_2 = {0xff, 0xff}, - .v_sync_line_aft_1 = {0xff, 0xff}, - .v_sync_line_aft_pxl_2 = {0xff, 0xff}, - .v_sync_line_aft_pxl_1 = {0xff, 0xff}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x5a, 0x03, /* h_fsz */ - 0x8a, 0x00, 0xd0, 0x02, /* hact */ - 0x0d, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0xe0, 0x01, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_720p50 = { - .core = { - .h_blank = {0xbc, 0x02}, - .v2_blank = {0xee, 0x02}, - .v1_blank = {0x1e, 0x00}, - .v_line = {0xee, 0x02}, - .h_line = {0xbc, 0x07}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f0 = {0xff, 0xff}, - .v_blank_f1 = {0xff, 0xff}, - .h_sync_start = {0xb6, 0x01}, - .h_sync_end = {0xde, 0x01}, - .v_sync_line_bef_2 = {0x0a, 0x00}, - .v_sync_line_bef_1 = {0x05, 0x00}, - .v_sync_line_aft_2 = {0xff, 0xff}, - .v_sync_line_aft_1 = {0xff, 0xff}, - .v_sync_line_aft_pxl_2 = {0xff, 0xff}, - .v_sync_line_aft_pxl_1 = {0xff, 0xff}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0xbc, 0x07, /* h_fsz */ - 0xbc, 0x02, 0x00, 0x05, /* hact */ - 0xee, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x1e, 0x00, 0xd0, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_720p60 = { - .core = { - .h_blank = {0x72, 0x01}, - .v2_blank = {0xee, 0x02}, - .v1_blank = {0x1e, 0x00}, - .v_line = {0xee, 0x02}, - .h_line = {0x72, 0x06}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f0 = {0xff, 0xff}, - .v_blank_f1 = {0xff, 0xff}, - .h_sync_start = {0x6c, 0x00}, - .h_sync_end = {0x94, 0x00}, - .v_sync_line_bef_2 = {0x0a, 0x00}, - .v_sync_line_bef_1 = {0x05, 0x00}, - .v_sync_line_aft_2 = {0xff, 0xff}, - .v_sync_line_aft_1 = {0xff, 0xff}, - .v_sync_line_aft_pxl_2 = {0xff, 0xff}, - .v_sync_line_aft_pxl_1 = {0xff, 0xff}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x72, 0x06, /* h_fsz */ - 0x72, 0x01, 0x00, 0x05, /* hact */ - 0xee, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x1e, 0x00, 0xd0, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_1080i50 = { - .core = { - .h_blank = {0xd0, 0x02}, - .v2_blank = {0x32, 0x02}, - .v1_blank = {0x16, 0x00}, - .v_line = {0x65, 0x04}, - .h_line = {0x50, 0x0a}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x01}, - .v_blank_f0 = {0x49, 0x02}, - .v_blank_f1 = {0x65, 0x04}, - .h_sync_start = {0x0e, 0x02}, - .h_sync_end = {0x3a, 0x02}, - .v_sync_line_bef_2 = {0x07, 0x00}, - .v_sync_line_bef_1 = {0x02, 0x00}, - .v_sync_line_aft_2 = {0x39, 0x02}, - .v_sync_line_aft_1 = {0x34, 0x02}, - .v_sync_line_aft_pxl_2 = {0x38, 0x07}, - .v_sync_line_aft_pxl_1 = {0x38, 0x07}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x50, 0x0a, /* h_fsz */ - 0xd0, 0x02, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x16, 0x00, 0x1c, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_1080i60 = { - .core = { - .h_blank = {0x18, 0x01}, - .v2_blank = {0x32, 0x02}, - .v1_blank = {0x16, 0x00}, - .v_line = {0x65, 0x04}, - .h_line = {0x98, 0x08}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x01}, - .v_blank_f0 = {0x49, 0x02}, - .v_blank_f1 = {0x65, 0x04}, - .h_sync_start = {0x56, 0x00}, - .h_sync_end = {0x82, 0x00}, - .v_sync_line_bef_2 = {0x07, 0x00}, - .v_sync_line_bef_1 = {0x02, 0x00}, - .v_sync_line_aft_2 = {0x39, 0x02}, - .v_sync_line_aft_1 = {0x34, 0x02}, - .v_sync_line_aft_pxl_2 = {0xa4, 0x04}, - .v_sync_line_aft_pxl_1 = {0xa4, 0x04}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x16, 0x00, 0x1c, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_1080p30 = { - .core = { - .h_blank = {0x18, 0x01}, - .v2_blank = {0x65, 0x04}, - .v1_blank = {0x2d, 0x00}, - .v_line = {0x65, 0x04}, - .h_line = {0x98, 0x08}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f0 = {0xff, 0xff}, - .v_blank_f1 = {0xff, 0xff}, - .h_sync_start = {0x56, 0x00}, - .h_sync_end = {0x82, 0x00}, - .v_sync_line_bef_2 = {0x09, 0x00}, - .v_sync_line_bef_1 = {0x04, 0x00}, - .v_sync_line_aft_2 = {0xff, 0xff}, - .v_sync_line_aft_1 = {0xff, 0xff}, - .v_sync_line_aft_pxl_2 = {0xff, 0xff}, - .v_sync_line_aft_pxl_1 = {0xff, 0xff}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_1080p50 = { - .core = { - .h_blank = {0xd0, 0x02}, - .v2_blank = {0x65, 0x04}, - .v1_blank = {0x2d, 0x00}, - .v_line = {0x65, 0x04}, - .h_line = {0x50, 0x0a}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f0 = {0xff, 0xff}, - .v_blank_f1 = {0xff, 0xff}, - .h_sync_start = {0x0e, 0x02}, - .h_sync_end = {0x3a, 0x02}, - .v_sync_line_bef_2 = {0x09, 0x00}, - .v_sync_line_bef_1 = {0x04, 0x00}, - .v_sync_line_aft_2 = {0xff, 0xff}, - .v_sync_line_aft_1 = {0xff, 0xff}, - .v_sync_line_aft_pxl_2 = {0xff, 0xff}, - .v_sync_line_aft_pxl_1 = {0xff, 0xff}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - .vact_space_1 = {0xff, 0xff}, - .vact_space_2 = {0xff, 0xff}, - .vact_space_3 = {0xff, 0xff}, - .vact_space_4 = {0xff, 0xff}, - .vact_space_5 = {0xff, 0xff}, - .vact_space_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x50, 0x0a, /* h_fsz */ - 0xd0, 0x02, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_preset_conf hdmi_conf_1080p60 = { - .core = { - .h_blank = {0x18, 0x01}, - .v2_blank = {0x65, 0x04}, - .v1_blank = {0x2d, 0x00}, - .v_line = {0x65, 0x04}, - .h_line = {0x98, 0x08}, - .hsync_pol = {0x00}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f0 = {0xff, 0xff}, - .v_blank_f1 = {0xff, 0xff}, - .h_sync_start = {0x56, 0x00}, - .h_sync_end = {0x82, 0x00}, - .v_sync_line_bef_2 = {0x09, 0x00}, - .v_sync_line_bef_1 = {0x04, 0x00}, - .v_sync_line_aft_2 = {0xff, 0xff}, - .v_sync_line_aft_1 = {0xff, 0xff}, - .v_sync_line_aft_pxl_2 = {0xff, 0xff}, - .v_sync_line_aft_pxl_1 = {0xff, 0xff}, - .v_blank_f2 = {0xff, 0xff}, - .v_blank_f3 = {0xff, 0xff}, - .v_blank_f4 = {0xff, 0xff}, - .v_blank_f5 = {0xff, 0xff}, - .v_sync_line_aft_3 = {0xff, 0xff}, - .v_sync_line_aft_4 = {0xff, 0xff}, - .v_sync_line_aft_5 = {0xff, 0xff}, - .v_sync_line_aft_6 = {0xff, 0xff}, - .v_sync_line_aft_pxl_3 = {0xff, 0xff}, - .v_sync_line_aft_pxl_4 = {0xff, 0xff}, - .v_sync_line_aft_pxl_5 = {0xff, 0xff}, - .v_sync_line_aft_pxl_6 = {0xff, 0xff}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x00, 0x00, /* vact_st3 */ - 0x00, 0x00, /* vact_st4 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - 0x00, /* 3d FP */ - }, -}; - -static const struct hdmi_conf hdmi_confs[] = { - { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 }, - { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 }, - { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, - { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, - { 1920, 1080, 30, false, hdmiphy_conf74_176, &hdmi_conf_1080p30 }, -}; - - -static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id) -{ - return readl(hdata->regs + reg_id); -} - -static inline void hdmi_reg_writeb(struct hdmi_context *hdata, - u32 reg_id, u8 value) -{ - writeb(value, hdata->regs + reg_id); -} - -static inline void hdmi_reg_writemask(struct hdmi_context *hdata, - u32 reg_id, u32 value, u32 mask) -{ - u32 old = readl(hdata->regs + reg_id); - value = (value & mask) | (old & ~mask); - writel(value, hdata->regs + reg_id); -} - -static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) -{ -#define DUMPREG(reg_id) \ - DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ - readl(hdata->regs + reg_id)) - DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix); - DUMPREG(HDMI_INTC_FLAG); - DUMPREG(HDMI_INTC_CON); - DUMPREG(HDMI_HPD_STATUS); - DUMPREG(HDMI_V13_PHY_RSTOUT); - DUMPREG(HDMI_V13_PHY_VPLL); - DUMPREG(HDMI_V13_PHY_CMU); - DUMPREG(HDMI_V13_CORE_RSTOUT); - - DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix); - DUMPREG(HDMI_CON_0); - DUMPREG(HDMI_CON_1); - DUMPREG(HDMI_CON_2); - DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_V13_PHY_STATUS); - DUMPREG(HDMI_STATUS_EN); - DUMPREG(HDMI_HPD); - DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_V13_HPD_GEN); - DUMPREG(HDMI_V13_DC_CONTROL); - DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN); - - DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); - DUMPREG(HDMI_H_BLANK_0); - DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V13_V_BLANK_0); - DUMPREG(HDMI_V13_V_BLANK_1); - DUMPREG(HDMI_V13_V_BLANK_2); - DUMPREG(HDMI_V13_H_V_LINE_0); - DUMPREG(HDMI_V13_H_V_LINE_1); - DUMPREG(HDMI_V13_H_V_LINE_2); - DUMPREG(HDMI_VSYNC_POL); - DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V13_V_BLANK_F_0); - DUMPREG(HDMI_V13_V_BLANK_F_1); - DUMPREG(HDMI_V13_V_BLANK_F_2); - DUMPREG(HDMI_V13_H_SYNC_GEN_0); - DUMPREG(HDMI_V13_H_SYNC_GEN_1); - DUMPREG(HDMI_V13_H_SYNC_GEN_2); - DUMPREG(HDMI_V13_V_SYNC_GEN_1_0); - DUMPREG(HDMI_V13_V_SYNC_GEN_1_1); - DUMPREG(HDMI_V13_V_SYNC_GEN_1_2); - DUMPREG(HDMI_V13_V_SYNC_GEN_2_0); - DUMPREG(HDMI_V13_V_SYNC_GEN_2_1); - DUMPREG(HDMI_V13_V_SYNC_GEN_2_2); - DUMPREG(HDMI_V13_V_SYNC_GEN_3_0); - DUMPREG(HDMI_V13_V_SYNC_GEN_3_1); - DUMPREG(HDMI_V13_V_SYNC_GEN_3_2); - - DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); - DUMPREG(HDMI_TG_CMD); - DUMPREG(HDMI_TG_H_FSZ_L); - DUMPREG(HDMI_TG_H_FSZ_H); - DUMPREG(HDMI_TG_HACT_ST_L); - DUMPREG(HDMI_TG_HACT_ST_H); - DUMPREG(HDMI_TG_HACT_SZ_L); - DUMPREG(HDMI_TG_HACT_SZ_H); - DUMPREG(HDMI_TG_V_FSZ_L); - DUMPREG(HDMI_TG_V_FSZ_H); - DUMPREG(HDMI_TG_VSYNC_L); - DUMPREG(HDMI_TG_VSYNC_H); - DUMPREG(HDMI_TG_VSYNC2_L); - DUMPREG(HDMI_TG_VSYNC2_H); - DUMPREG(HDMI_TG_VACT_ST_L); - DUMPREG(HDMI_TG_VACT_ST_H); - DUMPREG(HDMI_TG_VACT_SZ_L); - DUMPREG(HDMI_TG_VACT_SZ_H); - DUMPREG(HDMI_TG_FIELD_CHG_L); - DUMPREG(HDMI_TG_FIELD_CHG_H); - DUMPREG(HDMI_TG_VACT_ST2_L); - DUMPREG(HDMI_TG_VACT_ST2_H); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); -#undef DUMPREG -} - -static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix) -{ - int i; - -#define DUMPREG(reg_id) \ - DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ - readl(hdata->regs + reg_id)) - - DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix); - DUMPREG(HDMI_INTC_CON); - DUMPREG(HDMI_INTC_FLAG); - DUMPREG(HDMI_HPD_STATUS); - DUMPREG(HDMI_INTC_CON_1); - DUMPREG(HDMI_INTC_FLAG_1); - DUMPREG(HDMI_PHY_STATUS_0); - DUMPREG(HDMI_PHY_STATUS_PLL); - DUMPREG(HDMI_PHY_CON_0); - DUMPREG(HDMI_PHY_RSTOUT); - DUMPREG(HDMI_PHY_VPLL); - DUMPREG(HDMI_PHY_CMU); - DUMPREG(HDMI_CORE_RSTOUT); - - DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix); - DUMPREG(HDMI_CON_0); - DUMPREG(HDMI_CON_1); - DUMPREG(HDMI_CON_2); - DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_PHY_STATUS_0); - DUMPREG(HDMI_STATUS_EN); - DUMPREG(HDMI_HPD); - DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_ENC_EN); - DUMPREG(HDMI_DC_CONTROL); - DUMPREG(HDMI_VIDEO_PATTERN_GEN); - - DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); - DUMPREG(HDMI_H_BLANK_0); - DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V2_BLANK_0); - DUMPREG(HDMI_V2_BLANK_1); - DUMPREG(HDMI_V1_BLANK_0); - DUMPREG(HDMI_V1_BLANK_1); - DUMPREG(HDMI_V_LINE_0); - DUMPREG(HDMI_V_LINE_1); - DUMPREG(HDMI_H_LINE_0); - DUMPREG(HDMI_H_LINE_1); - DUMPREG(HDMI_HSYNC_POL); - - DUMPREG(HDMI_VSYNC_POL); - DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V_BLANK_F0_0); - DUMPREG(HDMI_V_BLANK_F0_1); - DUMPREG(HDMI_V_BLANK_F1_0); - DUMPREG(HDMI_V_BLANK_F1_1); - - DUMPREG(HDMI_H_SYNC_START_0); - DUMPREG(HDMI_H_SYNC_START_1); - DUMPREG(HDMI_H_SYNC_END_0); - DUMPREG(HDMI_H_SYNC_END_1); - - DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0); - DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1); - DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0); - DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1); - - DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1); - - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1); - - DUMPREG(HDMI_V_BLANK_F2_0); - DUMPREG(HDMI_V_BLANK_F2_1); - DUMPREG(HDMI_V_BLANK_F3_0); - DUMPREG(HDMI_V_BLANK_F3_1); - DUMPREG(HDMI_V_BLANK_F4_0); - DUMPREG(HDMI_V_BLANK_F4_1); - DUMPREG(HDMI_V_BLANK_F5_0); - DUMPREG(HDMI_V_BLANK_F5_1); - - DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1); - - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0); - DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1); - - DUMPREG(HDMI_VACT_SPACE_1_0); - DUMPREG(HDMI_VACT_SPACE_1_1); - DUMPREG(HDMI_VACT_SPACE_2_0); - DUMPREG(HDMI_VACT_SPACE_2_1); - DUMPREG(HDMI_VACT_SPACE_3_0); - DUMPREG(HDMI_VACT_SPACE_3_1); - DUMPREG(HDMI_VACT_SPACE_4_0); - DUMPREG(HDMI_VACT_SPACE_4_1); - DUMPREG(HDMI_VACT_SPACE_5_0); - DUMPREG(HDMI_VACT_SPACE_5_1); - DUMPREG(HDMI_VACT_SPACE_6_0); - DUMPREG(HDMI_VACT_SPACE_6_1); - - DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); - DUMPREG(HDMI_TG_CMD); - DUMPREG(HDMI_TG_H_FSZ_L); - DUMPREG(HDMI_TG_H_FSZ_H); - DUMPREG(HDMI_TG_HACT_ST_L); - DUMPREG(HDMI_TG_HACT_ST_H); - DUMPREG(HDMI_TG_HACT_SZ_L); - DUMPREG(HDMI_TG_HACT_SZ_H); - DUMPREG(HDMI_TG_V_FSZ_L); - DUMPREG(HDMI_TG_V_FSZ_H); - DUMPREG(HDMI_TG_VSYNC_L); - DUMPREG(HDMI_TG_VSYNC_H); - DUMPREG(HDMI_TG_VSYNC2_L); - DUMPREG(HDMI_TG_VSYNC2_H); - DUMPREG(HDMI_TG_VACT_ST_L); - DUMPREG(HDMI_TG_VACT_ST_H); - DUMPREG(HDMI_TG_VACT_SZ_L); - DUMPREG(HDMI_TG_VACT_SZ_H); - DUMPREG(HDMI_TG_FIELD_CHG_L); - DUMPREG(HDMI_TG_FIELD_CHG_H); - DUMPREG(HDMI_TG_VACT_ST2_L); - DUMPREG(HDMI_TG_VACT_ST2_H); - DUMPREG(HDMI_TG_VACT_ST3_L); - DUMPREG(HDMI_TG_VACT_ST3_H); - DUMPREG(HDMI_TG_VACT_ST4_L); - DUMPREG(HDMI_TG_VACT_ST4_H); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); - DUMPREG(HDMI_TG_3D); - - DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix); - DUMPREG(HDMI_AVI_CON); - DUMPREG(HDMI_AVI_HEADER0); - DUMPREG(HDMI_AVI_HEADER1); - DUMPREG(HDMI_AVI_HEADER2); - DUMPREG(HDMI_AVI_CHECK_SUM); - DUMPREG(HDMI_VSI_CON); - DUMPREG(HDMI_VSI_HEADER0); - DUMPREG(HDMI_VSI_HEADER1); - DUMPREG(HDMI_VSI_HEADER2); - for (i = 0; i < 7; ++i) - DUMPREG(HDMI_VSI_DATA(i)); - -#undef DUMPREG -} - -static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) -{ - if (hdata->is_v13) - hdmi_v13_regs_dump(hdata, prefix); - else - hdmi_v14_regs_dump(hdata, prefix); -} - -static int hdmi_v13_conf_index(struct drm_display_mode *mode) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) - if (hdmi_v13_confs[i].width == mode->hdisplay && - hdmi_v13_confs[i].height == mode->vdisplay && - hdmi_v13_confs[i].vrefresh == mode->vrefresh && - hdmi_v13_confs[i].interlace == - ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? - true : false)) - return i; - - return -EINVAL; -} - -static int hdmi_v14_conf_index(struct drm_display_mode *mode) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) - if (hdmi_confs[i].width == mode->hdisplay && - hdmi_confs[i].height == mode->vdisplay && - hdmi_confs[i].vrefresh == mode->vrefresh && - hdmi_confs[i].interlace == - ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? - true : false)) - return i; - - return -EINVAL; -} - -static int hdmi_conf_index(struct hdmi_context *hdata, - struct drm_display_mode *mode) -{ - if (hdata->is_v13) - return hdmi_v13_conf_index(mode); - - return hdmi_v14_conf_index(mode); -} - -static bool hdmi_is_connected(void *ctx) -{ - struct hdmi_context *hdata = ctx; - - return hdata->hpd; -} - -static int hdmi_get_edid(void *ctx, struct drm_connector *connector, - u8 *edid, int len) -{ - struct edid *raw_edid; - struct hdmi_context *hdata = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - if (!hdata->ddc_port) - return -ENODEV; - - raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); - if (raw_edid) { - hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); - memcpy(edid, raw_edid, min((1 + raw_edid->extensions) - * EDID_LENGTH, len)); - DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n", - (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"), - raw_edid->width_cm, raw_edid->height_cm); - } else { - return -ENODEV; - } - - return 0; -} - -static int hdmi_v13_check_timing(struct fb_videomode *check_timing) -{ - int i; - - DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n", - check_timing->xres, check_timing->yres, - check_timing->refresh, (check_timing->vmode & - FB_VMODE_INTERLACED) ? true : false); - - for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) - if (hdmi_v13_confs[i].width == check_timing->xres && - hdmi_v13_confs[i].height == check_timing->yres && - hdmi_v13_confs[i].vrefresh == check_timing->refresh && - hdmi_v13_confs[i].interlace == - ((check_timing->vmode & FB_VMODE_INTERLACED) ? - true : false)) - return 0; - - /* TODO */ - - return -EINVAL; -} - -static int hdmi_v14_check_timing(struct fb_videomode *check_timing) -{ - int i; - - DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n", - check_timing->xres, check_timing->yres, - check_timing->refresh, (check_timing->vmode & - FB_VMODE_INTERLACED) ? true : false); - - for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++) - if (hdmi_confs[i].width == check_timing->xres && - hdmi_confs[i].height == check_timing->yres && - hdmi_confs[i].vrefresh == check_timing->refresh && - hdmi_confs[i].interlace == - ((check_timing->vmode & FB_VMODE_INTERLACED) ? - true : false)) - return 0; - - /* TODO */ - - return -EINVAL; -} - -static int hdmi_check_timing(void *ctx, void *timing) -{ - struct hdmi_context *hdata = ctx; - struct fb_videomode *check_timing = timing; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, - check_timing->yres, check_timing->refresh, - check_timing->vmode); - - if (hdata->is_v13) - return hdmi_v13_check_timing(check_timing); - else - return hdmi_v14_check_timing(check_timing); -} - -static void hdmi_set_acr(u32 freq, u8 *acr) -{ - u32 n, cts; - - switch (freq) { - case 32000: - n = 4096; - cts = 27000; - break; - case 44100: - n = 6272; - cts = 30000; - break; - case 88200: - n = 12544; - cts = 30000; - break; - case 176400: - n = 25088; - cts = 30000; - break; - case 48000: - n = 6144; - cts = 27000; - break; - case 96000: - n = 12288; - cts = 27000; - break; - case 192000: - n = 24576; - cts = 27000; - break; - default: - n = 0; - cts = 0; - break; - } - - acr[1] = cts >> 16; - acr[2] = cts >> 8 & 0xff; - acr[3] = cts & 0xff; - - acr[4] = n >> 16; - acr[5] = n >> 8 & 0xff; - acr[6] = n & 0xff; -} - -static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr) -{ - hdmi_reg_writeb(hdata, HDMI_ACR_N0, acr[6]); - hdmi_reg_writeb(hdata, HDMI_ACR_N1, acr[5]); - hdmi_reg_writeb(hdata, HDMI_ACR_N2, acr[4]); - hdmi_reg_writeb(hdata, HDMI_ACR_MCTS0, acr[3]); - hdmi_reg_writeb(hdata, HDMI_ACR_MCTS1, acr[2]); - hdmi_reg_writeb(hdata, HDMI_ACR_MCTS2, acr[1]); - hdmi_reg_writeb(hdata, HDMI_ACR_CTS0, acr[3]); - hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]); - hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]); - - if (hdata->is_v13) - hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4); - else - hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); -} - -static void hdmi_audio_init(struct hdmi_context *hdata) -{ - u32 sample_rate, bits_per_sample, frame_size_code; - u32 data_num, bit_ch, sample_frq; - u32 val; - u8 acr[7]; - - sample_rate = 44100; - bits_per_sample = 16; - frame_size_code = 0; - - switch (bits_per_sample) { - case 20: - data_num = 2; - bit_ch = 1; - break; - case 24: - data_num = 3; - bit_ch = 1; - break; - default: - data_num = 1; - bit_ch = 0; - break; - } - - hdmi_set_acr(sample_rate, acr); - hdmi_reg_acr(hdata, acr); - - hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE - | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE - | HDMI_I2S_MUX_ENABLE); - - hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN - | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN); - - hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); - - sample_frq = (sample_rate == 44100) ? 0 : - (sample_rate == 48000) ? 2 : - (sample_rate == 32000) ? 3 : - (sample_rate == 96000) ? 0xa : 0x0; - - hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); - hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); - - val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01; - hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val); - - /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */ - hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5) - | HDMI_I2S_SEL_LRCK(6)); - hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1) - | HDMI_I2S_SEL_SDATA2(4)); - hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1) - | HDMI_I2S_SEL_SDATA2(2)); - hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0)); - - /* I2S_CON_1 & 2 */ - hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE - | HDMI_I2S_L_CH_LOW_POL); - hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE - | HDMI_I2S_SET_BIT_CH(bit_ch) - | HDMI_I2S_SET_SDATA_BIT(data_num) - | HDMI_I2S_BASIC_FORMAT); - - /* Configure register related to CUV information */ - hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0 - | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH - | HDMI_I2S_COPYRIGHT - | HDMI_I2S_LINEAR_PCM - | HDMI_I2S_CONSUMER_FORMAT); - hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER); - hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0)); - hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2 - | HDMI_I2S_SET_SMP_FREQ(sample_frq)); - hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4, - HDMI_I2S_ORG_SMP_FREQ_44_1 - | HDMI_I2S_WORD_LEN_MAX24_24BITS - | HDMI_I2S_WORD_LEN_MAX_24BITS); - - hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); -} - -static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) -{ - if (hdata->dvi_mode) - return; - - hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0); - hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ? - HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); -} - -static void hdmi_conf_reset(struct hdmi_context *hdata) -{ - u32 reg; - - if (hdata->is_v13) - reg = HDMI_V13_CORE_RSTOUT; - else - reg = HDMI_CORE_RSTOUT; - - /* resetting HDMI core */ - hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT); - mdelay(10); - hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); - mdelay(10); -} - -static void hdmi_conf_init(struct hdmi_context *hdata) -{ - /* enable HPD interrupts */ - hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL | - HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); - mdelay(10); - hdmi_reg_writemask(hdata, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | - HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); - - /* choose HDMI mode */ - hdmi_reg_writemask(hdata, HDMI_MODE_SEL, - HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); - /* disable bluescreen */ - hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); - - if (hdata->dvi_mode) { - /* choose DVI mode */ - hdmi_reg_writemask(hdata, HDMI_MODE_SEL, - HDMI_MODE_DVI_EN, HDMI_MODE_MASK); - hdmi_reg_writeb(hdata, HDMI_CON_2, - HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS); - } - - if (hdata->is_v13) { - /* choose bluescreen (fecal) color */ - hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12); - hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34); - hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56); - - /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02); - /* force RGB, look to CEA-861-D, table 7 for more detail */ - hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5); - hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); - - hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04); - } else { - /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); - hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); - } -} - -static void hdmi_v13_timing_apply(struct hdmi_context *hdata) -{ - const struct hdmi_v13_preset_conf *conf = - hdmi_v13_confs[hdata->cur_conf].conf; - const struct hdmi_v13_core_regs *core = &conf->core; - const struct hdmi_v13_tg_regs *tg = &conf->tg; - int tries; - - /* setting core registers */ - hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); - hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]); - hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]); - hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]); - hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]); - hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]); - hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); - hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]); - hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]); - hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]); - hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]); - hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]); - hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); - hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); - /* Timing generator registers */ - hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); - hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); - hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l); - hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); - - /* waiting for HDMIPHY's PLL to get to steady state */ - for (tries = 100; tries; --tries) { - u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS); - if (val & HDMI_PHY_STATUS_READY) - break; - mdelay(1); - } - /* steady state not achieved */ - if (tries == 0) { - DRM_ERROR("hdmiphy's pll could not reach steady state.\n"); - hdmi_regs_dump(hdata, "timing apply"); - } - - clk_disable(hdata->res.sclk_hdmi); - clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); - clk_enable(hdata->res.sclk_hdmi); - - /* enable HDMI and timing generator */ - hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); - if (core->int_pro_mode[0]) - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | - HDMI_FIELD_EN); - else - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); -} - -static void hdmi_v14_timing_apply(struct hdmi_context *hdata) -{ - const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf; - const struct hdmi_core_regs *core = &conf->core; - const struct hdmi_tg_regs *tg = &conf->tg; - int tries; - - /* setting core registers */ - hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); - hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]); - hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]); - hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]); - hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]); - hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]); - hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]); - hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]); - hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); - hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0, - core->v_sync_line_bef_2[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1, - core->v_sync_line_bef_2[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0, - core->v_sync_line_bef_1[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1, - core->v_sync_line_bef_1[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0, - core->v_sync_line_aft_2[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1, - core->v_sync_line_aft_2[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0, - core->v_sync_line_aft_1[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1, - core->v_sync_line_aft_1[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, - core->v_sync_line_aft_pxl_2[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1, - core->v_sync_line_aft_pxl_2[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, - core->v_sync_line_aft_pxl_1[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1, - core->v_sync_line_aft_pxl_1[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0, - core->v_sync_line_aft_3[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1, - core->v_sync_line_aft_3[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0, - core->v_sync_line_aft_4[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1, - core->v_sync_line_aft_4[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0, - core->v_sync_line_aft_5[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1, - core->v_sync_line_aft_5[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0, - core->v_sync_line_aft_6[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1, - core->v_sync_line_aft_6[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, - core->v_sync_line_aft_pxl_3[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1, - core->v_sync_line_aft_pxl_3[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, - core->v_sync_line_aft_pxl_4[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1, - core->v_sync_line_aft_pxl_4[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, - core->v_sync_line_aft_pxl_5[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1, - core->v_sync_line_aft_pxl_5[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, - core->v_sync_line_aft_pxl_6[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1, - core->v_sync_line_aft_pxl_6[1]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]); - hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]); - - /* Timing generator registers */ - hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); - hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); - hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); - hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l); - hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l); - hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); - hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); - hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d); - - /* waiting for HDMIPHY's PLL to get to steady state */ - for (tries = 100; tries; --tries) { - u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0); - if (val & HDMI_PHY_STATUS_READY) - break; - mdelay(1); - } - /* steady state not achieved */ - if (tries == 0) { - DRM_ERROR("hdmiphy's pll could not reach steady state.\n"); - hdmi_regs_dump(hdata, "timing apply"); - } - - clk_disable(hdata->res.sclk_hdmi); - clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); - clk_enable(hdata->res.sclk_hdmi); - - /* enable HDMI and timing generator */ - hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); - if (core->int_pro_mode[0]) - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | - HDMI_FIELD_EN); - else - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); -} - -static void hdmi_timing_apply(struct hdmi_context *hdata) -{ - if (hdata->is_v13) - hdmi_v13_timing_apply(hdata); - else - hdmi_v14_timing_apply(hdata); -} - -static void hdmiphy_conf_reset(struct hdmi_context *hdata) -{ - u8 buffer[2]; - u32 reg; - - clk_disable(hdata->res.sclk_hdmi); - clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); - clk_enable(hdata->res.sclk_hdmi); - - /* operation mode */ - buffer[0] = 0x1f; - buffer[1] = 0x00; - - if (hdata->hdmiphy_port) - i2c_master_send(hdata->hdmiphy_port, buffer, 2); - - if (hdata->is_v13) - reg = HDMI_V13_PHY_RSTOUT; - else - reg = HDMI_PHY_RSTOUT; - - /* reset hdmiphy */ - hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT); - mdelay(10); - hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT); - mdelay(10); -} - -static void hdmiphy_conf_apply(struct hdmi_context *hdata) -{ - const u8 *hdmiphy_data; - u8 buffer[32]; - u8 operation[2]; - u8 read_buffer[32] = {0, }; - int ret; - int i; - - if (!hdata->hdmiphy_port) { - DRM_ERROR("hdmiphy is not attached\n"); - return; - } - - /* pixel clock */ - if (hdata->is_v13) - hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data; - else - hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data; - - memcpy(buffer, hdmiphy_data, 32); - ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32); - if (ret != 32) { - DRM_ERROR("failed to configure HDMIPHY via I2C\n"); - return; - } - - mdelay(10); - - /* operation mode */ - operation[0] = 0x1f; - operation[1] = 0x80; - - ret = i2c_master_send(hdata->hdmiphy_port, operation, 2); - if (ret != 2) { - DRM_ERROR("failed to enable hdmiphy\n"); - return; - } - - ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32); - if (ret < 0) { - DRM_ERROR("failed to read hdmiphy config\n"); - return; - } - - for (i = 0; i < ret; i++) - DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - " - "recv [0x%02x]\n", i, buffer[i], read_buffer[i]); -} - -static void hdmi_conf_apply(struct hdmi_context *hdata) -{ - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - hdmiphy_conf_reset(hdata); - hdmiphy_conf_apply(hdata); - - mutex_lock(&hdata->hdmi_mutex); - hdmi_conf_reset(hdata); - hdmi_conf_init(hdata); - mutex_unlock(&hdata->hdmi_mutex); - - hdmi_audio_init(hdata); - - /* setting core registers */ - hdmi_timing_apply(hdata); - hdmi_audio_control(hdata, true); - - hdmi_regs_dump(hdata, "start"); -} - -static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_display_mode *m; - struct hdmi_context *hdata = ctx; - int index; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - drm_mode_set_crtcinfo(adjusted_mode, 0); - - if (hdata->is_v13) - index = hdmi_v13_conf_index(adjusted_mode); - else - index = hdmi_v14_conf_index(adjusted_mode); - - /* just return if user desired mode exists. */ - if (index >= 0) - return; - - /* - * otherwise, find the most suitable mode among modes and change it - * to adjusted_mode. - */ - list_for_each_entry(m, &connector->modes, head) { - if (hdata->is_v13) - index = hdmi_v13_conf_index(m); - else - index = hdmi_v14_conf_index(m); - - if (index >= 0) { - DRM_INFO("desired mode doesn't exist so\n"); - DRM_INFO("use the most suitable mode among modes.\n"); - memcpy(adjusted_mode, m, sizeof(*m)); - break; - } - } -} - -static void hdmi_mode_set(void *ctx, void *mode) -{ - struct hdmi_context *hdata = ctx; - int conf_idx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - conf_idx = hdmi_conf_index(hdata, mode); - if (conf_idx >= 0) - hdata->cur_conf = conf_idx; - else - DRM_DEBUG_KMS("not supported mode\n"); -} - -static void hdmi_get_max_resol(void *ctx, unsigned int *width, - unsigned int *height) -{ - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - *width = MAX_WIDTH; - *height = MAX_HEIGHT; -} - -static void hdmi_commit(void *ctx) -{ - struct hdmi_context *hdata = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - hdmi_conf_apply(hdata); -} - -static void hdmi_poweron(struct hdmi_context *hdata) -{ - struct hdmi_resources *res = &hdata->res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - mutex_lock(&hdata->hdmi_mutex); - if (hdata->powered) { - mutex_unlock(&hdata->hdmi_mutex); - return; - } - - hdata->powered = true; - - if (hdata->cfg_hpd) - hdata->cfg_hpd(true); - mutex_unlock(&hdata->hdmi_mutex); - - pm_runtime_get_sync(hdata->dev); - - regulator_bulk_enable(res->regul_count, res->regul_bulk); - clk_enable(res->hdmiphy); - clk_enable(res->hdmi); - clk_enable(res->sclk_hdmi); -} - -static void hdmi_poweroff(struct hdmi_context *hdata) -{ - struct hdmi_resources *res = &hdata->res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - mutex_lock(&hdata->hdmi_mutex); - if (!hdata->powered) - goto out; - mutex_unlock(&hdata->hdmi_mutex); - - /* - * The TV power domain needs any condition of hdmiphy to turn off and - * its reset state seems to meet the condition. - */ - hdmiphy_conf_reset(hdata); - - clk_disable(res->sclk_hdmi); - clk_disable(res->hdmi); - clk_disable(res->hdmiphy); - regulator_bulk_disable(res->regul_count, res->regul_bulk); - - pm_runtime_put_sync(hdata->dev); - - mutex_lock(&hdata->hdmi_mutex); - if (hdata->cfg_hpd) - hdata->cfg_hpd(false); - - hdata->powered = false; - -out: - mutex_unlock(&hdata->hdmi_mutex); -} - -static void hdmi_dpms(void *ctx, int mode) -{ - struct hdmi_context *hdata = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - switch (mode) { - case DRM_MODE_DPMS_ON: - hdmi_poweron(hdata); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - hdmi_poweroff(hdata); - break; - default: - DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); - break; - } -} - -static struct exynos_hdmi_ops hdmi_ops = { - /* display */ - .is_connected = hdmi_is_connected, - .get_edid = hdmi_get_edid, - .check_timing = hdmi_check_timing, - - /* manager */ - .mode_fixup = hdmi_mode_fixup, - .mode_set = hdmi_mode_set, - .get_max_resol = hdmi_get_max_resol, - .commit = hdmi_commit, - .dpms = hdmi_dpms, -}; - -static irqreturn_t hdmi_external_irq_thread(int irq, void *arg) -{ - struct exynos_drm_hdmi_context *ctx = arg; - struct hdmi_context *hdata = ctx->ctx; - - if (!hdata->get_hpd) - goto out; - - mutex_lock(&hdata->hdmi_mutex); - hdata->hpd = hdata->get_hpd(); - mutex_unlock(&hdata->hdmi_mutex); - - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); - -out: - return IRQ_HANDLED; -} - -static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg) -{ - struct exynos_drm_hdmi_context *ctx = arg; - struct hdmi_context *hdata = ctx->ctx; - u32 intc_flag; - - intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); - /* clearing flags for HPD plug/unplug */ - if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { - DRM_DEBUG_KMS("unplugged\n"); - hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, - HDMI_INTC_FLAG_HPD_UNPLUG); - } - if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { - DRM_DEBUG_KMS("plugged\n"); - hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, - HDMI_INTC_FLAG_HPD_PLUG); - } - - mutex_lock(&hdata->hdmi_mutex); - hdata->hpd = hdmi_reg_read(hdata, HDMI_HPD_STATUS); - if (hdata->powered && hdata->hpd) { - mutex_unlock(&hdata->hdmi_mutex); - goto out; - } - mutex_unlock(&hdata->hdmi_mutex); - - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); - -out: - return IRQ_HANDLED; -} - -static int __devinit hdmi_resources_init(struct hdmi_context *hdata) -{ - struct device *dev = hdata->dev; - struct hdmi_resources *res = &hdata->res; - static char *supply[] = { - /* FIXME: control HDMI_EN gpio using fixed regulator */ - /* "hdmi-en", */ - "vdd", - "vdd_osc", - "vdd_pll", - }; - int i, ret; - - DRM_DEBUG_KMS("HDMI resource init\n"); - - memset(res, 0, sizeof *res); - - /* get clocks, power */ - res->hdmi = clk_get(dev, "hdmi"); - if (IS_ERR_OR_NULL(res->hdmi)) { - DRM_ERROR("failed to get clock 'hdmi'\n"); - goto fail; - } - res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { - DRM_ERROR("failed to get clock 'sclk_hdmi'\n"); - goto fail; - } - res->sclk_pixel = clk_get(dev, "sclk_pixel"); - if (IS_ERR_OR_NULL(res->sclk_pixel)) { - DRM_ERROR("failed to get clock 'sclk_pixel'\n"); - goto fail; - } - res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); - if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { - DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n"); - goto fail; - } - res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR_OR_NULL(res->hdmiphy)) { - DRM_ERROR("failed to get clock 'hdmiphy'\n"); - goto fail; - } - - clk_set_parent(res->sclk_hdmi, res->sclk_pixel); - - res->regul_bulk = kzalloc(ARRAY_SIZE(supply) * - sizeof res->regul_bulk[0], GFP_KERNEL); - if (!res->regul_bulk) { - DRM_ERROR("failed to get memory for regulators\n"); - goto fail; - } - for (i = 0; i < ARRAY_SIZE(supply); ++i) { - res->regul_bulk[i].supply = supply[i]; - res->regul_bulk[i].consumer = NULL; - } - ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); - if (ret) { - DRM_ERROR("failed to get regulators\n"); - goto fail; - } - res->regul_count = ARRAY_SIZE(supply); - - return 0; -fail: - DRM_ERROR("HDMI resource init - failed\n"); - return -ENODEV; -} - -static int hdmi_resources_cleanup(struct hdmi_context *hdata) -{ - struct hdmi_resources *res = &hdata->res; - - regulator_bulk_free(res->regul_count, res->regul_bulk); - /* kfree is NULL-safe */ - kfree(res->regul_bulk); - if (!IS_ERR_OR_NULL(res->hdmiphy)) - clk_put(res->hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) - clk_put(res->sclk_hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_pixel)) - clk_put(res->sclk_pixel); - if (!IS_ERR_OR_NULL(res->sclk_hdmi)) - clk_put(res->sclk_hdmi); - if (!IS_ERR_OR_NULL(res->hdmi)) - clk_put(res->hdmi); - memset(res, 0, sizeof *res); - - return 0; -} - -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; - -void hdmi_attach_ddc_client(struct i2c_client *ddc) -{ - if (ddc) - hdmi_ddc = ddc; -} - -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) -{ - if (hdmiphy) - hdmi_hdmiphy = hdmiphy; -} - -static int __devinit hdmi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *drm_hdmi_ctx; - struct hdmi_context *hdata; - struct exynos_drm_hdmi_pdata *pdata; - struct resource *res; - int ret; - - DRM_DEBUG_KMS("[%d]\n", __LINE__); - - pdata = pdev->dev.platform_data; - if (!pdata) { - DRM_ERROR("no platform data specified\n"); - return -EINVAL; - } - - drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); - if (!drm_hdmi_ctx) { - DRM_ERROR("failed to allocate common hdmi context.\n"); - return -ENOMEM; - } - - hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL); - if (!hdata) { - DRM_ERROR("out of memory\n"); - kfree(drm_hdmi_ctx); - return -ENOMEM; - } - - mutex_init(&hdata->hdmi_mutex); - - drm_hdmi_ctx->ctx = (void *)hdata; - hdata->parent_ctx = (void *)drm_hdmi_ctx; - - platform_set_drvdata(pdev, drm_hdmi_ctx); - - hdata->is_v13 = pdata->is_v13; - hdata->cfg_hpd = pdata->cfg_hpd; - hdata->get_hpd = pdata->get_hpd; - hdata->dev = dev; - - ret = hdmi_resources_init(hdata); - if (ret) { - ret = -EINVAL; - goto err_data; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - DRM_ERROR("failed to find registers\n"); - ret = -ENOENT; - goto err_resource; - } - - hdata->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!hdata->regs_res) { - DRM_ERROR("failed to claim register region\n"); - ret = -ENOENT; - goto err_resource; - } - - hdata->regs = ioremap(res->start, resource_size(res)); - if (!hdata->regs) { - DRM_ERROR("failed to map registers\n"); - ret = -ENXIO; - goto err_req_region; - } - - /* DDC i2c driver */ - if (i2c_add_driver(&ddc_driver)) { - DRM_ERROR("failed to register ddc i2c driver\n"); - ret = -ENOENT; - goto err_iomap; - } - - hdata->ddc_port = hdmi_ddc; - - /* hdmiphy i2c driver */ - if (i2c_add_driver(&hdmiphy_driver)) { - DRM_ERROR("failed to register hdmiphy i2c driver\n"); - ret = -ENOENT; - goto err_ddc; - } - - hdata->hdmiphy_port = hdmi_hdmiphy; - - hdata->external_irq = platform_get_irq_byname(pdev, "external_irq"); - if (hdata->external_irq < 0) { - DRM_ERROR("failed to get platform irq\n"); - ret = hdata->external_irq; - goto err_hdmiphy; - } - - hdata->internal_irq = platform_get_irq_byname(pdev, "internal_irq"); - if (hdata->internal_irq < 0) { - DRM_ERROR("failed to get platform internal irq\n"); - ret = hdata->internal_irq; - goto err_hdmiphy; - } - - ret = request_threaded_irq(hdata->external_irq, NULL, - hdmi_external_irq_thread, IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "hdmi_external", drm_hdmi_ctx); - if (ret) { - DRM_ERROR("failed to register hdmi internal interrupt\n"); - goto err_hdmiphy; - } - - if (hdata->cfg_hpd) - hdata->cfg_hpd(false); - - ret = request_threaded_irq(hdata->internal_irq, NULL, - hdmi_internal_irq_thread, IRQF_ONESHOT, - "hdmi_internal", drm_hdmi_ctx); - if (ret) { - DRM_ERROR("failed to register hdmi internal interrupt\n"); - goto err_free_irq; - } - - /* register specific callbacks to common hdmi. */ - exynos_hdmi_ops_register(&hdmi_ops); - - pm_runtime_enable(dev); - - return 0; - -err_free_irq: - free_irq(hdata->external_irq, drm_hdmi_ctx); -err_hdmiphy: - i2c_del_driver(&hdmiphy_driver); -err_ddc: - i2c_del_driver(&ddc_driver); -err_iomap: - iounmap(hdata->regs); -err_req_region: - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); -err_resource: - hdmi_resources_cleanup(hdata); -err_data: - kfree(hdata); - kfree(drm_hdmi_ctx); - return ret; -} - -static int __devexit hdmi_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev); - struct hdmi_context *hdata = ctx->ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - pm_runtime_disable(dev); - - free_irq(hdata->internal_irq, hdata); - - hdmi_resources_cleanup(hdata); - - iounmap(hdata->regs); - - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); - - /* hdmiphy i2c driver */ - i2c_del_driver(&hdmiphy_driver); - /* DDC i2c driver */ - i2c_del_driver(&ddc_driver); - - kfree(hdata); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int hdmi_suspend(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - struct hdmi_context *hdata = ctx->ctx; - - disable_irq(hdata->internal_irq); - disable_irq(hdata->external_irq); - - hdata->hpd = false; - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); - - hdmi_poweroff(hdata); - - return 0; -} - -static int hdmi_resume(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - struct hdmi_context *hdata = ctx->ctx; - - enable_irq(hdata->external_irq); - enable_irq(hdata->internal_irq); - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(hdmi_pm_ops, hdmi_suspend, hdmi_resume); - -struct platform_driver hdmi_driver = { - .probe = hdmi_probe, - .remove = __devexit_p(hdmi_remove), - .driver = { - .name = "exynos4-hdmi", - .owner = THIS_MODULE, - .pm = &hdmi_pm_ops, - }, -}; diff --git a/drivers/gpu/drm/exynos_tmp/exynos_hdmi.h b/drivers/gpu/drm/exynos_tmp/exynos_hdmi.h deleted file mode 100644 index 1c3b6d8..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_hdmi.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _EXYNOS_HDMI_H_ -#define _EXYNOS_HDMI_H_ - -void hdmi_attach_ddc_client(struct i2c_client *ddc); -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy); - -extern struct i2c_driver hdmiphy_driver; -extern struct i2c_driver ddc_driver; - -#endif diff --git a/drivers/gpu/drm/exynos_tmp/exynos_hdmiphy.c b/drivers/gpu/drm/exynos_tmp/exynos_hdmiphy.c deleted file mode 100644 index 9fe2995..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_hdmiphy.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Seung-Woo Kim <sw0312.kim@samsung.com> - * Inki Dae <inki.dae@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include "drmP.h" - -#include <linux/kernel.h> -#include <linux/i2c.h> -#include <linux/module.h> - -#include "exynos_drm_drv.h" -#include "exynos_hdmi.h" - - -static int hdmiphy_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - hdmi_attach_hdmiphy_client(client); - - dev_info(&client->adapter->dev, "attached s5p_hdmiphy " - "into i2c adapter successfully\n"); - - return 0; -} - -static int hdmiphy_remove(struct i2c_client *client) -{ - dev_info(&client->adapter->dev, "detached s5p_hdmiphy " - "from i2c adapter successfully\n"); - - return 0; -} - -static const struct i2c_device_id hdmiphy_id[] = { - { "s5p_hdmiphy", 0 }, - { }, -}; - -struct i2c_driver hdmiphy_driver = { - .driver = { - .name = "s5p-hdmiphy", - .owner = THIS_MODULE, - }, - .id_table = hdmiphy_id, - .probe = hdmiphy_probe, - .remove = __devexit_p(hdmiphy_remove), - .command = NULL, -}; -EXPORT_SYMBOL(hdmiphy_driver); diff --git a/drivers/gpu/drm/exynos_tmp/exynos_mixer.c b/drivers/gpu/drm/exynos_tmp/exynos_mixer.c deleted file mode 100644 index 68ef010..0000000 --- a/drivers/gpu/drm/exynos_tmp/exynos_mixer.c +++ /dev/null @@ -1,1109 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Authors: - * Seung-Woo Kim <sw0312.kim@samsung.com> - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * - * Based on drivers/media/video/s5p-tv/mixer_reg.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include "drmP.h" - -#include "regs-mixer.h" -#include "regs-vp.h" - -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <linux/i2c.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/delay.h> -#include <linux/pm_runtime.h> -#include <linux/clk.h> -#include <linux/regulator/consumer.h> - -#include <drm/exynos_drm.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_hdmi.h" - -#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev)) - -struct hdmi_win_data { - dma_addr_t dma_addr; - void __iomem *vaddr; - dma_addr_t chroma_dma_addr; - void __iomem *chroma_vaddr; - uint32_t pixel_format; - unsigned int bpp; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_width; - unsigned int crtc_height; - unsigned int fb_x; - unsigned int fb_y; - unsigned int fb_width; - unsigned int fb_height; - unsigned int src_width; - unsigned int src_height; - unsigned int mode_width; - unsigned int mode_height; - unsigned int scan_flags; -}; - -struct mixer_resources { - int irq; - void __iomem *mixer_regs; - void __iomem *vp_regs; - spinlock_t reg_slock; - struct clk *mixer; - struct clk *vp; - struct clk *sclk_mixer; - struct clk *sclk_hdmi; - struct clk *sclk_dac; -}; - -struct mixer_context { - struct device *dev; - int pipe; - bool interlace; - bool powered; - u32 int_en; - - struct mutex mixer_mutex; - struct mixer_resources mixer_res; - struct hdmi_win_data win_data[MIXER_WIN_NR]; -}; - -static const u8 filter_y_horiz_tap8[] = { - 0, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 0, 0, 0, - 0, 2, 4, 5, 6, 6, 6, 6, - 6, 5, 5, 4, 3, 2, 1, 1, - 0, -6, -12, -16, -18, -20, -21, -20, - -20, -18, -16, -13, -10, -8, -5, -2, - 127, 126, 125, 121, 114, 107, 99, 89, - 79, 68, 57, 46, 35, 25, 16, 8, -}; - -static const u8 filter_y_vert_tap4[] = { - 0, -3, -6, -8, -8, -8, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0, - 127, 126, 124, 118, 111, 102, 92, 81, - 70, 59, 48, 37, 27, 19, 11, 5, - 0, 5, 11, 19, 27, 37, 48, 59, - 70, 81, 92, 102, 111, 118, 124, 126, - 0, 0, -1, -1, -2, -3, -4, -5, - -6, -7, -8, -8, -8, -8, -6, -3, -}; - -static const u8 filter_cr_horiz_tap4[] = { - 0, -3, -6, -8, -8, -8, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0, - 127, 126, 124, 118, 111, 102, 92, 81, - 70, 59, 48, 37, 27, 19, 11, 5, -}; - -static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id) -{ - return readl(res->vp_regs + reg_id); -} - -static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id, - u32 val) -{ - writel(val, res->vp_regs + reg_id); -} - -static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id, - u32 val, u32 mask) -{ - u32 old = vp_reg_read(res, reg_id); - - val = (val & mask) | (old & ~mask); - writel(val, res->vp_regs + reg_id); -} - -static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id) -{ - return readl(res->mixer_regs + reg_id); -} - -static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id, - u32 val) -{ - writel(val, res->mixer_regs + reg_id); -} - -static inline void mixer_reg_writemask(struct mixer_resources *res, - u32 reg_id, u32 val, u32 mask) -{ - u32 old = mixer_reg_read(res, reg_id); - - val = (val & mask) | (old & ~mask); - writel(val, res->mixer_regs + reg_id); -} - -static void mixer_regs_dump(struct mixer_context *ctx) -{ -#define DUMPREG(reg_id) \ -do { \ - DRM_DEBUG_KMS(#reg_id " = %08x\n", \ - (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \ -} while (0) - - DUMPREG(MXR_STATUS); - DUMPREG(MXR_CFG); - DUMPREG(MXR_INT_EN); - DUMPREG(MXR_INT_STATUS); - - DUMPREG(MXR_LAYER_CFG); - DUMPREG(MXR_VIDEO_CFG); - - DUMPREG(MXR_GRAPHIC0_CFG); - DUMPREG(MXR_GRAPHIC0_BASE); - DUMPREG(MXR_GRAPHIC0_SPAN); - DUMPREG(MXR_GRAPHIC0_WH); - DUMPREG(MXR_GRAPHIC0_SXY); - DUMPREG(MXR_GRAPHIC0_DXY); - - DUMPREG(MXR_GRAPHIC1_CFG); - DUMPREG(MXR_GRAPHIC1_BASE); - DUMPREG(MXR_GRAPHIC1_SPAN); - DUMPREG(MXR_GRAPHIC1_WH); - DUMPREG(MXR_GRAPHIC1_SXY); - DUMPREG(MXR_GRAPHIC1_DXY); -#undef DUMPREG -} - -static void vp_regs_dump(struct mixer_context *ctx) -{ -#define DUMPREG(reg_id) \ -do { \ - DRM_DEBUG_KMS(#reg_id " = %08x\n", \ - (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \ -} while (0) - - DUMPREG(VP_ENABLE); - DUMPREG(VP_SRESET); - DUMPREG(VP_SHADOW_UPDATE); - DUMPREG(VP_FIELD_ID); - DUMPREG(VP_MODE); - DUMPREG(VP_IMG_SIZE_Y); - DUMPREG(VP_IMG_SIZE_C); - DUMPREG(VP_PER_RATE_CTRL); - DUMPREG(VP_TOP_Y_PTR); - DUMPREG(VP_BOT_Y_PTR); - DUMPREG(VP_TOP_C_PTR); - DUMPREG(VP_BOT_C_PTR); - DUMPREG(VP_ENDIAN_MODE); - DUMPREG(VP_SRC_H_POSITION); - DUMPREG(VP_SRC_V_POSITION); - DUMPREG(VP_SRC_WIDTH); - DUMPREG(VP_SRC_HEIGHT); - DUMPREG(VP_DST_H_POSITION); - DUMPREG(VP_DST_V_POSITION); - DUMPREG(VP_DST_WIDTH); - DUMPREG(VP_DST_HEIGHT); - DUMPREG(VP_H_RATIO); - DUMPREG(VP_V_RATIO); - -#undef DUMPREG -} - -static inline void vp_filter_set(struct mixer_resources *res, - int reg_id, const u8 *data, unsigned int size) -{ - /* assure 4-byte align */ - BUG_ON(size & 3); - for (; size; size -= 4, reg_id += 4, data += 4) { - u32 val = (data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | data[3]; - vp_reg_write(res, reg_id, val); - } -} - -static void vp_default_filter(struct mixer_resources *res) -{ - vp_filter_set(res, VP_POLY8_Y0_LL, - filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); - vp_filter_set(res, VP_POLY4_Y0_LL, - filter_y_vert_tap4, sizeof filter_y_vert_tap4); - vp_filter_set(res, VP_POLY4_C0_LL, - filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4); -} - -static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable) -{ - struct mixer_resources *res = &ctx->mixer_res; - - /* block update on vsync */ - mixer_reg_writemask(res, MXR_STATUS, enable ? - MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE); - - vp_reg_write(res, VP_SHADOW_UPDATE, enable ? - VP_SHADOW_UPDATE_ENABLE : 0); -} - -static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height) -{ - struct mixer_resources *res = &ctx->mixer_res; - u32 val; - - /* choosing between interlace and progressive mode */ - val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE : - MXR_CFG_SCAN_PROGRASSIVE); - - /* choosing between porper HD and SD mode */ - if (height == 480) - val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; - else if (height == 576) - val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; - else if (height == 720) - val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; - else if (height == 1080) - val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; - else - val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; - - mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK); -} - -static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height) -{ - struct mixer_resources *res = &ctx->mixer_res; - u32 val; - - if (height == 480) { - val = MXR_CFG_RGB601_0_255; - } else if (height == 576) { - val = MXR_CFG_RGB601_0_255; - } else if (height == 720) { - val = MXR_CFG_RGB709_16_235; - mixer_reg_write(res, MXR_CM_COEFF_Y, - (1 << 30) | (94 << 20) | (314 << 10) | - (32 << 0)); - mixer_reg_write(res, MXR_CM_COEFF_CB, - (972 << 20) | (851 << 10) | (225 << 0)); - mixer_reg_write(res, MXR_CM_COEFF_CR, - (225 << 20) | (820 << 10) | (1004 << 0)); - } else if (height == 1080) { - val = MXR_CFG_RGB709_16_235; - mixer_reg_write(res, MXR_CM_COEFF_Y, - (1 << 30) | (94 << 20) | (314 << 10) | - (32 << 0)); - mixer_reg_write(res, MXR_CM_COEFF_CB, - (972 << 20) | (851 << 10) | (225 << 0)); - mixer_reg_write(res, MXR_CM_COEFF_CR, - (225 << 20) | (820 << 10) | (1004 << 0)); - } else { - val = MXR_CFG_RGB709_16_235; - mixer_reg_write(res, MXR_CM_COEFF_Y, - (1 << 30) | (94 << 20) | (314 << 10) | - (32 << 0)); - mixer_reg_write(res, MXR_CM_COEFF_CB, - (972 << 20) | (851 << 10) | (225 << 0)); - mixer_reg_write(res, MXR_CM_COEFF_CR, - (225 << 20) | (820 << 10) | (1004 << 0)); - } - - mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK); -} - -static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable) -{ - struct mixer_resources *res = &ctx->mixer_res; - u32 val = enable ? ~0 : 0; - - switch (win) { - case 0: - mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); - break; - case 1: - mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); - break; - case 2: - vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON); - mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_VP_ENABLE); - break; - } -} - -static void mixer_run(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); - - mixer_regs_dump(ctx); -} - -static void vp_video_buffer(struct mixer_context *ctx, int win) -{ - struct mixer_resources *res = &ctx->mixer_res; - unsigned long flags; - struct hdmi_win_data *win_data; - unsigned int x_ratio, y_ratio; - unsigned int buf_num; - dma_addr_t luma_addr[2], chroma_addr[2]; - bool tiled_mode = false; - bool crcb_mode = false; - u32 val; - - win_data = &ctx->win_data[win]; - - switch (win_data->pixel_format) { - case DRM_FORMAT_NV12MT: - tiled_mode = true; - case DRM_FORMAT_NV12M: - crcb_mode = false; - buf_num = 2; - break; - /* TODO: single buffer format NV12, NV21 */ - default: - /* ignore pixel format at disable time */ - if (!win_data->dma_addr) - break; - - DRM_ERROR("pixel format for vp is wrong [%d].\n", - win_data->pixel_format); - return; - } - - /* scaling feature: (src << 16) / dst */ - x_ratio = (win_data->src_width << 16) / win_data->crtc_width; - y_ratio = (win_data->src_height << 16) / win_data->crtc_height; - - if (buf_num == 2) { - luma_addr[0] = win_data->dma_addr; - chroma_addr[0] = win_data->chroma_dma_addr; - } else { - luma_addr[0] = win_data->dma_addr; - chroma_addr[0] = win_data->dma_addr - + (win_data->fb_width * win_data->fb_height); - } - - if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) { - ctx->interlace = true; - if (tiled_mode) { - luma_addr[1] = luma_addr[0] + 0x40; - chroma_addr[1] = chroma_addr[0] + 0x40; - } else { - luma_addr[1] = luma_addr[0] + win_data->fb_width; - chroma_addr[1] = chroma_addr[0] + win_data->fb_width; - } - } else { - ctx->interlace = false; - luma_addr[1] = 0; - chroma_addr[1] = 0; - } - - spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); - - /* interlace or progressive scan mode */ - val = (ctx->interlace ? ~0 : 0); - vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP); - - /* setup format */ - val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12); - val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR); - vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK); - - /* setting size of input image */ - vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) | - VP_IMG_VSIZE(win_data->fb_height)); - /* chroma height has to reduced by 2 to avoid chroma distorions */ - vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) | - VP_IMG_VSIZE(win_data->fb_height / 2)); - - vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width); - vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height); - vp_reg_write(res, VP_SRC_H_POSITION, - VP_SRC_H_POSITION_VAL(win_data->fb_x)); - vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y); - - vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width); - vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x); - if (ctx->interlace) { - vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2); - vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2); - } else { - vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height); - vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y); - } - - vp_reg_write(res, VP_H_RATIO, x_ratio); - vp_reg_write(res, VP_V_RATIO, y_ratio); - - vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); - - /* set buffer address to vp */ - vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]); - vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]); - vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]); - vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]); - - mixer_cfg_scan(ctx, win_data->mode_height); - mixer_cfg_rgb_fmt(ctx, win_data->mode_height); - mixer_cfg_layer(ctx, win, true); - mixer_run(ctx); - - mixer_vsync_set_update(ctx, true); - spin_unlock_irqrestore(&res->reg_slock, flags); - - vp_regs_dump(ctx); -} - -static void mixer_graph_buffer(struct mixer_context *ctx, int win) -{ - struct mixer_resources *res = &ctx->mixer_res; - unsigned long flags; - struct hdmi_win_data *win_data; - unsigned int x_ratio, y_ratio; - unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset; - dma_addr_t dma_addr; - unsigned int fmt; - u32 val; - - win_data = &ctx->win_data[win]; - - #define RGB565 4 - #define ARGB1555 5 - #define ARGB4444 6 - #define ARGB8888 7 - - switch (win_data->bpp) { - case 16: - fmt = ARGB4444; - break; - case 32: - fmt = ARGB8888; - break; - default: - fmt = ARGB8888; - } - - /* 2x scaling feature */ - x_ratio = 0; - y_ratio = 0; - - dst_x_offset = win_data->crtc_x; - dst_y_offset = win_data->crtc_y; - - /* converting dma address base and source offset */ - dma_addr = win_data->dma_addr - + (win_data->fb_x * win_data->bpp >> 3) - + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3); - src_x_offset = 0; - src_y_offset = 0; - - if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) - ctx->interlace = true; - else - ctx->interlace = false; - - spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); - - /* setup format */ - mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win), - MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK); - - /* setup geometry */ - mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width); - - val = MXR_GRP_WH_WIDTH(win_data->crtc_width); - val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height); - val |= MXR_GRP_WH_H_SCALE(x_ratio); - val |= MXR_GRP_WH_V_SCALE(y_ratio); - mixer_reg_write(res, MXR_GRAPHIC_WH(win), val); - - /* setup offsets in source image */ - val = MXR_GRP_SXY_SX(src_x_offset); - val |= MXR_GRP_SXY_SY(src_y_offset); - mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val); - - /* setup offsets in display image */ - val = MXR_GRP_DXY_DX(dst_x_offset); - val |= MXR_GRP_DXY_DY(dst_y_offset); - mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val); - - /* set buffer address to mixer */ - mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr); - - mixer_cfg_scan(ctx, win_data->mode_height); - mixer_cfg_rgb_fmt(ctx, win_data->mode_height); - mixer_cfg_layer(ctx, win, true); - mixer_run(ctx); - - mixer_vsync_set_update(ctx, true); - spin_unlock_irqrestore(&res->reg_slock, flags); -} - -static void vp_win_reset(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - int tries = 100; - - vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING); - for (tries = 100; tries; --tries) { - /* waiting until VP_SRESET_PROCESSING is 0 */ - if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) - break; - mdelay(10); - } - WARN(tries == 0, "failed to reset Video Processor\n"); -} - -static void mixer_win_reset(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - unsigned long flags; - u32 val; /* value stored to register */ - - spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); - - mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK); - - /* set output in RGB888 mode */ - mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK); - - /* 16 beat burst in DMA */ - mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST, - MXR_STATUS_BURST_MASK); - - /* setting default layer priority: layer1 > layer0 > video - * because typical usage scenario would be - * layer1 - OSD - * layer0 - framebuffer - * video - video overlay - */ - val = MXR_LAYER_CFG_GRP1_VAL(3); - val |= MXR_LAYER_CFG_GRP0_VAL(2); - val |= MXR_LAYER_CFG_VP_VAL(1); - mixer_reg_write(res, MXR_LAYER_CFG, val); - - /* setting background color */ - mixer_reg_write(res, MXR_BG_COLOR0, 0x008080); - mixer_reg_write(res, MXR_BG_COLOR1, 0x008080); - mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); - - /* setting graphical layers */ - - val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ - val |= MXR_GRP_CFG_WIN_BLEND_EN; - val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ - - /* the same configuration for both layers */ - mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val); - - val |= MXR_GRP_CFG_BLEND_PRE_MUL; - val |= MXR_GRP_CFG_PIXEL_BLEND_EN; - mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val); - - /* configuration of Video Processor Registers */ - vp_win_reset(ctx); - vp_default_filter(res); - - /* disable all layers */ - mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE); - mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE); - mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE); - - mixer_vsync_set_update(ctx, true); - spin_unlock_irqrestore(&res->reg_slock, flags); -} - -static void mixer_poweron(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - mutex_lock(&ctx->mixer_mutex); - if (ctx->powered) { - mutex_unlock(&ctx->mixer_mutex); - return; - } - ctx->powered = true; - mutex_unlock(&ctx->mixer_mutex); - - pm_runtime_get_sync(ctx->dev); - - clk_enable(res->mixer); - clk_enable(res->vp); - clk_enable(res->sclk_mixer); - - mixer_reg_write(res, MXR_INT_EN, ctx->int_en); - mixer_win_reset(ctx); -} - -static void mixer_poweroff(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - mutex_lock(&ctx->mixer_mutex); - if (!ctx->powered) - goto out; - mutex_unlock(&ctx->mixer_mutex); - - ctx->int_en = mixer_reg_read(res, MXR_INT_EN); - - clk_disable(res->mixer); - clk_disable(res->vp); - clk_disable(res->sclk_mixer); - - pm_runtime_put_sync(ctx->dev); - - mutex_lock(&ctx->mixer_mutex); - ctx->powered = false; - -out: - mutex_unlock(&ctx->mixer_mutex); -} - -static int mixer_enable_vblank(void *ctx, int pipe) -{ - struct mixer_context *mixer_ctx = ctx; - struct mixer_resources *res = &mixer_ctx->mixer_res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - mixer_ctx->pipe = pipe; - - /* enable vsync interrupt */ - mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC, - MXR_INT_EN_VSYNC); - - return 0; -} - -static void mixer_disable_vblank(void *ctx) -{ - struct mixer_context *mixer_ctx = ctx; - struct mixer_resources *res = &mixer_ctx->mixer_res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - /* disable vsync interrupt */ - mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); -} - -static void mixer_dpms(void *ctx, int mode) -{ - struct mixer_context *mixer_ctx = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - switch (mode) { - case DRM_MODE_DPMS_ON: - mixer_poweron(mixer_ctx); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - mixer_poweroff(mixer_ctx); - break; - default: - DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); - break; - } -} - -static void mixer_win_mode_set(void *ctx, - struct exynos_drm_overlay *overlay) -{ - struct mixer_context *mixer_ctx = ctx; - struct hdmi_win_data *win_data; - int win; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - if (!overlay) { - DRM_ERROR("overlay is NULL\n"); - return; - } - - DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n", - overlay->fb_width, overlay->fb_height, - overlay->fb_x, overlay->fb_y, - overlay->crtc_width, overlay->crtc_height, - overlay->crtc_x, overlay->crtc_y); - - win = overlay->zpos; - if (win == DEFAULT_ZPOS) - win = MIXER_DEFAULT_WIN; - - if (win < 0 || win > MIXER_WIN_NR) { - DRM_ERROR("mixer window[%d] is wrong\n", win); - return; - } - - win_data = &mixer_ctx->win_data[win]; - - win_data->dma_addr = overlay->dma_addr[0]; - win_data->vaddr = overlay->vaddr[0]; - win_data->chroma_dma_addr = overlay->dma_addr[1]; - win_data->chroma_vaddr = overlay->vaddr[1]; - win_data->pixel_format = overlay->pixel_format; - win_data->bpp = overlay->bpp; - - win_data->crtc_x = overlay->crtc_x; - win_data->crtc_y = overlay->crtc_y; - win_data->crtc_width = overlay->crtc_width; - win_data->crtc_height = overlay->crtc_height; - - win_data->fb_x = overlay->fb_x; - win_data->fb_y = overlay->fb_y; - win_data->fb_width = overlay->fb_width; - win_data->fb_height = overlay->fb_height; - win_data->src_width = overlay->src_width; - win_data->src_height = overlay->src_height; - - win_data->mode_width = overlay->mode_width; - win_data->mode_height = overlay->mode_height; - - win_data->scan_flags = overlay->scan_flag; -} - -static void mixer_win_commit(void *ctx, int win) -{ - struct mixer_context *mixer_ctx = ctx; - - DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); - - if (win > 1) - vp_video_buffer(mixer_ctx, win); - else - mixer_graph_buffer(mixer_ctx, win); -} - -static void mixer_win_disable(void *ctx, int win) -{ - struct mixer_context *mixer_ctx = ctx; - struct mixer_resources *res = &mixer_ctx->mixer_res; - unsigned long flags; - - DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); - - spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(mixer_ctx, false); - - mixer_cfg_layer(mixer_ctx, win, false); - - mixer_vsync_set_update(mixer_ctx, true); - spin_unlock_irqrestore(&res->reg_slock, flags); -} - -static struct exynos_mixer_ops mixer_ops = { - /* manager */ - .enable_vblank = mixer_enable_vblank, - .disable_vblank = mixer_disable_vblank, - .dpms = mixer_dpms, - - /* overlay */ - .win_mode_set = mixer_win_mode_set, - .win_commit = mixer_win_commit, - .win_disable = mixer_win_disable, -}; - -/* for pageflip event */ -static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc) -{ - struct exynos_drm_private *dev_priv = drm_dev->dev_private; - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned long flags; - bool is_checked = false; - - spin_lock_irqsave(&drm_dev->event_lock, flags); - - list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, - base.link) { - /* if event's pipe isn't same as crtc then ignore it. */ - if (crtc != e->pipe) - continue; - - is_checked = true; - do_gettimeofday(&now); - e->event.sequence = 0; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; - - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - } - - if (is_checked) - /* - * call drm_vblank_put only in case that drm_vblank_get was - * called. - */ - if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) - drm_vblank_put(drm_dev, crtc); - - spin_unlock_irqrestore(&drm_dev->event_lock, flags); -} - -static irqreturn_t mixer_irq_handler(int irq, void *arg) -{ - struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg; - struct mixer_context *ctx = drm_hdmi_ctx->ctx; - struct mixer_resources *res = &ctx->mixer_res; - u32 val, base, shadow; - - spin_lock(&res->reg_slock); - - /* read interrupt status for handling and clearing flags for VSYNC */ - val = mixer_reg_read(res, MXR_INT_STATUS); - - /* handling VSYNC */ - if (val & MXR_INT_STATUS_VSYNC) { - /* interlace scan need to check shadow register */ - if (ctx->interlace) { - base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0)); - shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0)); - if (base != shadow) - goto out; - - base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1)); - shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1)); - if (base != shadow) - goto out; - } - - drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe); - mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe); - } - -out: - /* clear interrupts */ - if (~val & MXR_INT_EN_VSYNC) { - /* vsync interrupt use different bit for read and clear */ - val &= ~MXR_INT_EN_VSYNC; - val |= MXR_INT_CLEAR_VSYNC; - } - mixer_reg_write(res, MXR_INT_STATUS, val); - - spin_unlock(&res->reg_slock); - - return IRQ_HANDLED; -} - -static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, - struct platform_device *pdev) -{ - struct mixer_context *mixer_ctx = ctx->ctx; - struct device *dev = &pdev->dev; - struct mixer_resources *mixer_res = &mixer_ctx->mixer_res; - struct resource *res; - int ret; - - spin_lock_init(&mixer_res->reg_slock); - - mixer_res->mixer = clk_get(dev, "mixer"); - if (IS_ERR_OR_NULL(mixer_res->mixer)) { - dev_err(dev, "failed to get clock 'mixer'\n"); - ret = -ENODEV; - goto fail; - } - mixer_res->vp = clk_get(dev, "vp"); - if (IS_ERR_OR_NULL(mixer_res->vp)) { - dev_err(dev, "failed to get clock 'vp'\n"); - ret = -ENODEV; - goto fail; - } - mixer_res->sclk_mixer = clk_get(dev, "sclk_mixer"); - if (IS_ERR_OR_NULL(mixer_res->sclk_mixer)) { - dev_err(dev, "failed to get clock 'sclk_mixer'\n"); - ret = -ENODEV; - goto fail; - } - mixer_res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) { - dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); - ret = -ENODEV; - goto fail; - } - mixer_res->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(mixer_res->sclk_dac)) { - dev_err(dev, "failed to get clock 'sclk_dac'\n"); - ret = -ENODEV; - goto fail; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr"); - if (res == NULL) { - dev_err(dev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail; - } - - clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); - - mixer_res->mixer_regs = ioremap(res->start, resource_size(res)); - if (mixer_res->mixer_regs == NULL) { - dev_err(dev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp"); - if (res == NULL) { - dev_err(dev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail_mixer_regs; - } - - mixer_res->vp_regs = ioremap(res->start, resource_size(res)); - if (mixer_res->vp_regs == NULL) { - dev_err(dev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail_mixer_regs; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); - if (res == NULL) { - dev_err(dev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail_vp_regs; - } - - ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx); - if (ret) { - dev_err(dev, "request interrupt failed.\n"); - goto fail_vp_regs; - } - mixer_res->irq = res->start; - - return 0; - -fail_vp_regs: - iounmap(mixer_res->vp_regs); - -fail_mixer_regs: - iounmap(mixer_res->mixer_regs); - -fail: - if (!IS_ERR_OR_NULL(mixer_res->sclk_dac)) - clk_put(mixer_res->sclk_dac); - if (!IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) - clk_put(mixer_res->sclk_hdmi); - if (!IS_ERR_OR_NULL(mixer_res->sclk_mixer)) - clk_put(mixer_res->sclk_mixer); - if (!IS_ERR_OR_NULL(mixer_res->vp)) - clk_put(mixer_res->vp); - if (!IS_ERR_OR_NULL(mixer_res->mixer)) - clk_put(mixer_res->mixer); - return ret; -} - -static void mixer_resources_cleanup(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - free_irq(res->irq, ctx); - - iounmap(res->vp_regs); - iounmap(res->mixer_regs); -} - -static int __devinit mixer_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *drm_hdmi_ctx; - struct mixer_context *ctx; - int ret; - - dev_info(dev, "probe start\n"); - - drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); - if (!drm_hdmi_ctx) { - DRM_ERROR("failed to allocate common hdmi context.\n"); - return -ENOMEM; - } - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - DRM_ERROR("failed to alloc mixer context.\n"); - kfree(drm_hdmi_ctx); - return -ENOMEM; - } - - mutex_init(&ctx->mixer_mutex); - - ctx->dev = &pdev->dev; - drm_hdmi_ctx->ctx = (void *)ctx; - - platform_set_drvdata(pdev, drm_hdmi_ctx); - - /* acquire resources: regs, irqs, clocks */ - ret = mixer_resources_init(drm_hdmi_ctx, pdev); - if (ret) - goto fail; - - /* register specific callback point to common hdmi. */ - exynos_mixer_ops_register(&mixer_ops); - - pm_runtime_enable(dev); - - return 0; - - -fail: - dev_info(dev, "probe failed\n"); - return ret; -} - -static int mixer_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *drm_hdmi_ctx = - platform_get_drvdata(pdev); - struct mixer_context *ctx = drm_hdmi_ctx->ctx; - - dev_info(dev, "remove successful\n"); - - pm_runtime_disable(&pdev->dev); - - mixer_resources_cleanup(ctx); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int mixer_suspend(struct device *dev) -{ - struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); - struct mixer_context *ctx = drm_hdmi_ctx->ctx; - - mixer_poweroff(ctx); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(mixer_pm_ops, mixer_suspend, NULL); - -struct platform_driver mixer_driver = { - .driver = { - .name = "s5p-mixer", - .owner = THIS_MODULE, - .pm = &mixer_pm_ops, - }, - .probe = mixer_probe, - .remove = __devexit_p(mixer_remove), -}; diff --git a/drivers/gpu/drm/exynos_tmp/regs-hdmi.h b/drivers/gpu/drm/exynos_tmp/regs-hdmi.h deleted file mode 100644 index 93576a0..0000000 --- a/drivers/gpu/drm/exynos_tmp/regs-hdmi.h +++ /dev/null @@ -1,723 +0,0 @@ -/* - * - * Cloned from drivers/media/video/s5p-tv/regs-hdmi.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * HDMI register header file for Samsung TVOUT driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef SAMSUNG_REGS_HDMI_H -#define SAMSUNG_REGS_HDMI_H - -/* - * Register part -*/ - -/* HDMI Version 1.3 & Common */ -#define HDMI_CTRL_BASE(x) ((x) + 0x00000000) -#define HDMI_CORE_BASE(x) ((x) + 0x00010000) -#define HDMI_I2S_BASE(x) ((x) + 0x00040000) -#define HDMI_TG_BASE(x) ((x) + 0x00050000) - -/* Control registers */ -#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) -#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) -#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) -#define HDMI_V13_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) -#define HDMI_V13_PHY_VPLL HDMI_CTRL_BASE(0x0018) -#define HDMI_V13_PHY_CMU HDMI_CTRL_BASE(0x001C) -#define HDMI_V13_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) - -/* Core registers */ -#define HDMI_CON_0 HDMI_CORE_BASE(0x0000) -#define HDMI_CON_1 HDMI_CORE_BASE(0x0004) -#define HDMI_CON_2 HDMI_CORE_BASE(0x0008) -#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) -#define HDMI_V13_PHY_STATUS HDMI_CORE_BASE(0x0014) -#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) -#define HDMI_HPD HDMI_CORE_BASE(0x0030) -#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) -#define HDMI_ENC_EN HDMI_CORE_BASE(0x0044) -#define HDMI_V13_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) -#define HDMI_V13_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) -#define HDMI_V13_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) -#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) -#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) -#define HDMI_V13_V_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V13_V_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V13_V_BLANK_2 HDMI_CORE_BASE(0x00B8) -#define HDMI_V13_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_V13_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_V13_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) -#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) -#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) -#define HDMI_V13_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V13_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V13_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) -#define HDMI_V13_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) -#define HDMI_V13_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) -#define HDMI_V13_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) -#define HDMI_V13_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V13_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V13_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) -#define HDMI_V13_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V13_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V13_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) -#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) -#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180) -#define HDMI_V13_ACR_CTS0 HDMI_CORE_BASE(0x0190) -#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300) -#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) -#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0) -#define HDMI_V13_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) -#define HDMI_V13_HPD_GEN HDMI_CORE_BASE(0x05C8) -#define HDMI_V13_AUI_CON HDMI_CORE_BASE(0x0360) -#define HDMI_V13_SPD_CON HDMI_CORE_BASE(0x0400) - -/* Timing generator registers */ -#define HDMI_TG_CMD HDMI_TG_BASE(0x0000) -#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018) -#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C) -#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020) -#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024) -#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028) -#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C) -#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030) -#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034) -#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038) -#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C) -#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040) -#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044) -#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048) -#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C) -#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050) -#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054) -#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058) -#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C) -#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060) -#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064) -#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078) -#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C) -#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080) -#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084) -#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088) -#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C) -#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090) -#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094) - -/* - * Bit definition part - */ - -/* HDMI_INTC_CON */ -#define HDMI_INTC_EN_GLOBAL (1 << 6) -#define HDMI_INTC_EN_HPD_PLUG (1 << 3) -#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2) - -/* HDMI_INTC_FLAG */ -#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3) -#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2) - -/* HDMI_PHY_RSTOUT */ -#define HDMI_PHY_SW_RSTOUT (1 << 0) - -/* HDMI_CORE_RSTOUT */ -#define HDMI_CORE_SW_RSTOUT (1 << 0) - -/* HDMI_CON_0 */ -#define HDMI_BLUE_SCR_EN (1 << 5) -#define HDMI_ASP_EN (1 << 2) -#define HDMI_ASP_DIS (0 << 2) -#define HDMI_ASP_MASK (1 << 2) -#define HDMI_EN (1 << 0) - -/* HDMI_CON_2 */ -#define HDMI_VID_PREAMBLE_DIS (1 << 5) -#define HDMI_GUARD_BAND_DIS (1 << 1) - -/* HDMI_PHY_STATUS */ -#define HDMI_PHY_STATUS_READY (1 << 0) - -/* HDMI_MODE_SEL */ -#define HDMI_MODE_HDMI_EN (1 << 1) -#define HDMI_MODE_DVI_EN (1 << 0) -#define HDMI_MODE_MASK (3 << 0) - -/* HDMI_TG_CMD */ -#define HDMI_TG_EN (1 << 0) -#define HDMI_FIELD_EN (1 << 1) - - -/* HDMI Version 1.4 */ -/* Control registers */ -/* #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) */ -/* #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) */ -#define HDMI_HDCP_KEY_LOAD HDMI_CTRL_BASE(0x0008) -/* #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) */ -#define HDMI_INTC_CON_1 HDMI_CTRL_BASE(0x0010) -#define HDMI_INTC_FLAG_1 HDMI_CTRL_BASE(0x0014) -#define HDMI_PHY_STATUS_0 HDMI_CTRL_BASE(0x0020) -#define HDMI_PHY_STATUS_CMU HDMI_CTRL_BASE(0x0024) -#define HDMI_PHY_STATUS_PLL HDMI_CTRL_BASE(0x0028) -#define HDMI_PHY_CON_0 HDMI_CTRL_BASE(0x0030) -#define HDMI_HPD_CTRL HDMI_CTRL_BASE(0x0040) -#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044) -#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050) -#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070) -#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074) -#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078) -#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C) -#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080) - -/* Video related registers */ -#define HDMI_YMAX HDMI_CORE_BASE(0x0060) -#define HDMI_YMIN HDMI_CORE_BASE(0x0064) -#define HDMI_CMAX HDMI_CORE_BASE(0x0068) -#define HDMI_CMIN HDMI_CORE_BASE(0x006C) - -#define HDMI_V2_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V2_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V1_BLANK_0 HDMI_CORE_BASE(0x00B8) -#define HDMI_V1_BLANK_1 HDMI_CORE_BASE(0x00BC) - -#define HDMI_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_H_LINE_0 HDMI_CORE_BASE(0x00C8) -#define HDMI_H_LINE_1 HDMI_CORE_BASE(0x00CC) - -#define HDMI_HSYNC_POL HDMI_CORE_BASE(0x00E0) - -#define HDMI_V_BLANK_F0_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V_BLANK_F0_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V_BLANK_F1_0 HDMI_CORE_BASE(0x0118) -#define HDMI_V_BLANK_F1_1 HDMI_CORE_BASE(0x011C) - -#define HDMI_H_SYNC_START_0 HDMI_CORE_BASE(0x0120) -#define HDMI_H_SYNC_START_1 HDMI_CORE_BASE(0x0124) -#define HDMI_H_SYNC_END_0 HDMI_CORE_BASE(0x0128) -#define HDMI_H_SYNC_END_1 HDMI_CORE_BASE(0x012C) - -#define HDMI_V_SYNC_LINE_BEF_2_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V_SYNC_LINE_BEF_2_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V_SYNC_LINE_BEF_1_0 HDMI_CORE_BASE(0x0138) -#define HDMI_V_SYNC_LINE_BEF_1_1 HDMI_CORE_BASE(0x013C) - -#define HDMI_V_SYNC_LINE_AFT_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V_SYNC_LINE_AFT_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V_SYNC_LINE_AFT_1_0 HDMI_CORE_BASE(0x0148) -#define HDMI_V_SYNC_LINE_AFT_1_1 HDMI_CORE_BASE(0x014C) - -#define HDMI_V_SYNC_LINE_AFT_PXL_2_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V_SYNC_LINE_AFT_PXL_2_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V_SYNC_LINE_AFT_PXL_1_0 HDMI_CORE_BASE(0x0158) -#define HDMI_V_SYNC_LINE_AFT_PXL_1_1 HDMI_CORE_BASE(0x015C) - -#define HDMI_V_BLANK_F2_0 HDMI_CORE_BASE(0x0160) -#define HDMI_V_BLANK_F2_1 HDMI_CORE_BASE(0x0164) -#define HDMI_V_BLANK_F3_0 HDMI_CORE_BASE(0x0168) -#define HDMI_V_BLANK_F3_1 HDMI_CORE_BASE(0x016C) -#define HDMI_V_BLANK_F4_0 HDMI_CORE_BASE(0x0170) -#define HDMI_V_BLANK_F4_1 HDMI_CORE_BASE(0x0174) -#define HDMI_V_BLANK_F5_0 HDMI_CORE_BASE(0x0178) -#define HDMI_V_BLANK_F5_1 HDMI_CORE_BASE(0x017C) - -#define HDMI_V_SYNC_LINE_AFT_3_0 HDMI_CORE_BASE(0x0180) -#define HDMI_V_SYNC_LINE_AFT_3_1 HDMI_CORE_BASE(0x0184) -#define HDMI_V_SYNC_LINE_AFT_4_0 HDMI_CORE_BASE(0x0188) -#define HDMI_V_SYNC_LINE_AFT_4_1 HDMI_CORE_BASE(0x018C) -#define HDMI_V_SYNC_LINE_AFT_5_0 HDMI_CORE_BASE(0x0190) -#define HDMI_V_SYNC_LINE_AFT_5_1 HDMI_CORE_BASE(0x0194) -#define HDMI_V_SYNC_LINE_AFT_6_0 HDMI_CORE_BASE(0x0198) -#define HDMI_V_SYNC_LINE_AFT_6_1 HDMI_CORE_BASE(0x019C) - -#define HDMI_V_SYNC_LINE_AFT_PXL_3_0 HDMI_CORE_BASE(0x01A0) -#define HDMI_V_SYNC_LINE_AFT_PXL_3_1 HDMI_CORE_BASE(0x01A4) -#define HDMI_V_SYNC_LINE_AFT_PXL_4_0 HDMI_CORE_BASE(0x01A8) -#define HDMI_V_SYNC_LINE_AFT_PXL_4_1 HDMI_CORE_BASE(0x01AC) -#define HDMI_V_SYNC_LINE_AFT_PXL_5_0 HDMI_CORE_BASE(0x01B0) -#define HDMI_V_SYNC_LINE_AFT_PXL_5_1 HDMI_CORE_BASE(0x01B4) -#define HDMI_V_SYNC_LINE_AFT_PXL_6_0 HDMI_CORE_BASE(0x01B8) -#define HDMI_V_SYNC_LINE_AFT_PXL_6_1 HDMI_CORE_BASE(0x01BC) - -#define HDMI_VACT_SPACE_1_0 HDMI_CORE_BASE(0x01C0) -#define HDMI_VACT_SPACE_1_1 HDMI_CORE_BASE(0x01C4) -#define HDMI_VACT_SPACE_2_0 HDMI_CORE_BASE(0x01C8) -#define HDMI_VACT_SPACE_2_1 HDMI_CORE_BASE(0x01CC) -#define HDMI_VACT_SPACE_3_0 HDMI_CORE_BASE(0x01D0) -#define HDMI_VACT_SPACE_3_1 HDMI_CORE_BASE(0x01D4) -#define HDMI_VACT_SPACE_4_0 HDMI_CORE_BASE(0x01D8) -#define HDMI_VACT_SPACE_4_1 HDMI_CORE_BASE(0x01DC) -#define HDMI_VACT_SPACE_5_0 HDMI_CORE_BASE(0x01E0) -#define HDMI_VACT_SPACE_5_1 HDMI_CORE_BASE(0x01E4) -#define HDMI_VACT_SPACE_6_0 HDMI_CORE_BASE(0x01E8) -#define HDMI_VACT_SPACE_6_1 HDMI_CORE_BASE(0x01EC) - -#define HDMI_GCP_CON HDMI_CORE_BASE(0x0200) -#define HDMI_GCP_BYTE1 HDMI_CORE_BASE(0x0210) -#define HDMI_GCP_BYTE2 HDMI_CORE_BASE(0x0214) -#define HDMI_GCP_BYTE3 HDMI_CORE_BASE(0x0218) - -/* Audio related registers */ -#define HDMI_ASP_CON HDMI_CORE_BASE(0x0300) -#define HDMI_ASP_SP_FLAT HDMI_CORE_BASE(0x0304) -#define HDMI_ASP_CHCFG0 HDMI_CORE_BASE(0x0310) -#define HDMI_ASP_CHCFG1 HDMI_CORE_BASE(0x0314) -#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318) -#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C) - -#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400) -#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410) -#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414) -#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418) -#define HDMI_ACR_CTS0 HDMI_CORE_BASE(0x0420) -#define HDMI_ACR_CTS1 HDMI_CORE_BASE(0x0424) -#define HDMI_ACR_CTS2 HDMI_CORE_BASE(0x0428) -#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430) -#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434) -#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438) - -/* Packet related registers */ -#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500) -#define HDMI_ACP_TYPE HDMI_CORE_BASE(0x0514) -#define HDMI_ACP_DATA(n) HDMI_CORE_BASE(0x0520 + 4 * (n)) - -#define HDMI_ISRC_CON HDMI_CORE_BASE(0x0600) -#define HDMI_ISRC1_HEADER1 HDMI_CORE_BASE(0x0614) -#define HDMI_ISRC1_DATA(n) HDMI_CORE_BASE(0x0620 + 4 * (n)) -#define HDMI_ISRC2_DATA(n) HDMI_CORE_BASE(0x06A0 + 4 * (n)) - -#define HDMI_AVI_CON HDMI_CORE_BASE(0x0700) -#define HDMI_AVI_HEADER0 HDMI_CORE_BASE(0x0710) -#define HDMI_AVI_HEADER1 HDMI_CORE_BASE(0x0714) -#define HDMI_AVI_HEADER2 HDMI_CORE_BASE(0x0718) -#define HDMI_AVI_CHECK_SUM HDMI_CORE_BASE(0x071C) -#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0720 + 4 * (n)) - -#define HDMI_AUI_CON HDMI_CORE_BASE(0x0800) -#define HDMI_AUI_HEADER0 HDMI_CORE_BASE(0x0810) -#define HDMI_AUI_HEADER1 HDMI_CORE_BASE(0x0814) -#define HDMI_AUI_HEADER2 HDMI_CORE_BASE(0x0818) -#define HDMI_AUI_CHECK_SUM HDMI_CORE_BASE(0x081C) -#define HDMI_AUI_BYTE(n) HDMI_CORE_BASE(0x0820 + 4 * (n)) - -#define HDMI_MPG_CON HDMI_CORE_BASE(0x0900) -#define HDMI_MPG_CHECK_SUM HDMI_CORE_BASE(0x091C) -#define HDMI_MPG_DATA(n) HDMI_CORE_BASE(0x0920 + 4 * (n)) - -#define HDMI_SPD_CON HDMI_CORE_BASE(0x0A00) -#define HDMI_SPD_HEADER0 HDMI_CORE_BASE(0x0A10) -#define HDMI_SPD_HEADER1 HDMI_CORE_BASE(0x0A14) -#define HDMI_SPD_HEADER2 HDMI_CORE_BASE(0x0A18) -#define HDMI_SPD_DATA(n) HDMI_CORE_BASE(0x0A20 + 4 * (n)) - -#define HDMI_GAMUT_CON HDMI_CORE_BASE(0x0B00) -#define HDMI_GAMUT_HEADER0 HDMI_CORE_BASE(0x0B10) -#define HDMI_GAMUT_HEADER1 HDMI_CORE_BASE(0x0B14) -#define HDMI_GAMUT_HEADER2 HDMI_CORE_BASE(0x0B18) -#define HDMI_GAMUT_METADATA(n) HDMI_CORE_BASE(0x0B20 + 4 * (n)) - -#define HDMI_VSI_CON HDMI_CORE_BASE(0x0C00) -#define HDMI_VSI_HEADER0 HDMI_CORE_BASE(0x0C10) -#define HDMI_VSI_HEADER1 HDMI_CORE_BASE(0x0C14) -#define HDMI_VSI_HEADER2 HDMI_CORE_BASE(0x0C18) -#define HDMI_VSI_DATA(n) HDMI_CORE_BASE(0x0C20 + 4 * (n)) - -#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x0D00) -#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x0D04) - -#define HDMI_AN_SEED_SEL HDMI_CORE_BASE(0x0E48) -#define HDMI_AN_SEED_0 HDMI_CORE_BASE(0x0E58) -#define HDMI_AN_SEED_1 HDMI_CORE_BASE(0x0E5C) -#define HDMI_AN_SEED_2 HDMI_CORE_BASE(0x0E60) -#define HDMI_AN_SEED_3 HDMI_CORE_BASE(0x0E64) - -/* HDCP related registers */ -#define HDMI_HDCP_SHA1(n) HDMI_CORE_BASE(0x7000 + 4 * (n)) -#define HDMI_HDCP_KSV_LIST(n) HDMI_CORE_BASE(0x7050 + 4 * (n)) - -#define HDMI_HDCP_KSV_LIST_CON HDMI_CORE_BASE(0x7064) -#define HDMI_HDCP_SHA_RESULT HDMI_CORE_BASE(0x7070) -#define HDMI_HDCP_CTRL1 HDMI_CORE_BASE(0x7080) -#define HDMI_HDCP_CTRL2 HDMI_CORE_BASE(0x7084) -#define HDMI_HDCP_CHECK_RESULT HDMI_CORE_BASE(0x7090) -#define HDMI_HDCP_BKSV(n) HDMI_CORE_BASE(0x70A0 + 4 * (n)) -#define HDMI_HDCP_AKSV(n) HDMI_CORE_BASE(0x70C0 + 4 * (n)) -#define HDMI_HDCP_AN(n) HDMI_CORE_BASE(0x70E0 + 4 * (n)) - -#define HDMI_HDCP_BCAPS HDMI_CORE_BASE(0x7100) -#define HDMI_HDCP_BSTATUS_0 HDMI_CORE_BASE(0x7110) -#define HDMI_HDCP_BSTATUS_1 HDMI_CORE_BASE(0x7114) -#define HDMI_HDCP_RI_0 HDMI_CORE_BASE(0x7140) -#define HDMI_HDCP_RI_1 HDMI_CORE_BASE(0x7144) -#define HDMI_HDCP_I2C_INT HDMI_CORE_BASE(0x7180) -#define HDMI_HDCP_AN_INT HDMI_CORE_BASE(0x7190) -#define HDMI_HDCP_WDT_INT HDMI_CORE_BASE(0x71A0) -#define HDMI_HDCP_RI_INT HDMI_CORE_BASE(0x71B0) -#define HDMI_HDCP_RI_COMPARE_0 HDMI_CORE_BASE(0x71D0) -#define HDMI_HDCP_RI_COMPARE_1 HDMI_CORE_BASE(0x71D4) -#define HDMI_HDCP_FRAME_COUNT HDMI_CORE_BASE(0x71E0) - -#define HDMI_RGB_ROUND_EN HDMI_CORE_BASE(0xD500) -#define HDMI_VACT_SPACE_R_0 HDMI_CORE_BASE(0xD504) -#define HDMI_VACT_SPACE_R_1 HDMI_CORE_BASE(0xD508) -#define HDMI_VACT_SPACE_G_0 HDMI_CORE_BASE(0xD50C) -#define HDMI_VACT_SPACE_G_1 HDMI_CORE_BASE(0xD510) -#define HDMI_VACT_SPACE_B_0 HDMI_CORE_BASE(0xD514) -#define HDMI_VACT_SPACE_B_1 HDMI_CORE_BASE(0xD518) - -#define HDMI_BLUE_SCREEN_B_0 HDMI_CORE_BASE(0xD520) -#define HDMI_BLUE_SCREEN_B_1 HDMI_CORE_BASE(0xD524) -#define HDMI_BLUE_SCREEN_G_0 HDMI_CORE_BASE(0xD528) -#define HDMI_BLUE_SCREEN_G_1 HDMI_CORE_BASE(0xD52C) -#define HDMI_BLUE_SCREEN_R_0 HDMI_CORE_BASE(0xD530) -#define HDMI_BLUE_SCREEN_R_1 HDMI_CORE_BASE(0xD534) - -/* HDMI I2S register */ -#define HDMI_I2S_CLK_CON HDMI_I2S_BASE(0x000) -#define HDMI_I2S_CON_1 HDMI_I2S_BASE(0x004) -#define HDMI_I2S_CON_2 HDMI_I2S_BASE(0x008) -#define HDMI_I2S_PIN_SEL_0 HDMI_I2S_BASE(0x00c) -#define HDMI_I2S_PIN_SEL_1 HDMI_I2S_BASE(0x010) -#define HDMI_I2S_PIN_SEL_2 HDMI_I2S_BASE(0x014) -#define HDMI_I2S_PIN_SEL_3 HDMI_I2S_BASE(0x018) -#define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c) -#define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020) -#define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024) -#define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028) -#define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c) -#define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030) -#define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034) -#define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038) -#define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c) -#define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040) -#define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044) -#define HDMI_I2S_CH_ST_SH_3 HDMI_I2S_BASE(0x048) -#define HDMI_I2S_CH_ST_SH_4 HDMI_I2S_BASE(0x04c) -#define HDMI_I2S_VD_DATA HDMI_I2S_BASE(0x050) -#define HDMI_I2S_MUX_CH HDMI_I2S_BASE(0x054) -#define HDMI_I2S_MUX_CUV HDMI_I2S_BASE(0x058) -#define HDMI_I2S_IRQ_MASK HDMI_I2S_BASE(0x05c) -#define HDMI_I2S_IRQ_STATUS HDMI_I2S_BASE(0x060) - -/* I2S bit definition */ - -/* I2S_CLK_CON */ -#define HDMI_I2S_CLK_DIS (0) -#define HDMI_I2S_CLK_EN (1) - -/* I2S_CON_1 */ -#define HDMI_I2S_SCLK_FALLING_EDGE (0 << 1) -#define HDMI_I2S_SCLK_RISING_EDGE (1 << 1) -#define HDMI_I2S_L_CH_LOW_POL (0) -#define HDMI_I2S_L_CH_HIGH_POL (1) - -/* I2S_CON_2 */ -#define HDMI_I2S_MSB_FIRST_MODE (0 << 6) -#define HDMI_I2S_LSB_FIRST_MODE (1 << 6) -#define HDMI_I2S_BIT_CH_32FS (0 << 4) -#define HDMI_I2S_BIT_CH_48FS (1 << 4) -#define HDMI_I2S_BIT_CH_RESERVED (2 << 4) -#define HDMI_I2S_SDATA_16BIT (1 << 2) -#define HDMI_I2S_SDATA_20BIT (2 << 2) -#define HDMI_I2S_SDATA_24BIT (3 << 2) -#define HDMI_I2S_BASIC_FORMAT (0) -#define HDMI_I2S_L_JUST_FORMAT (2) -#define HDMI_I2S_R_JUST_FORMAT (3) -#define HDMI_I2S_CON_2_CLR (~(0xFF)) -#define HDMI_I2S_SET_BIT_CH(x) (((x) & 0x7) << 4) -#define HDMI_I2S_SET_SDATA_BIT(x) (((x) & 0x7) << 2) - -/* I2S_PIN_SEL_0 */ -#define HDMI_I2S_SEL_SCLK(x) (((x) & 0x7) << 4) -#define HDMI_I2S_SEL_SCLK_DEFAULT_1 (0x7 << 4) -#define HDMI_I2S_SEL_LRCK(x) ((x) & 0x7) -#define HDMI_I2S_SEL_LRCK_DEFAULT_0 (0x7) - -/* I2S_PIN_SEL_1 */ -#define HDMI_I2S_SEL_SDATA1(x) (((x) & 0x7) << 4) -#define HDMI_I2S_SEL_SDATA1_DEFAULT_3 (0x7 << 4) -#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7) -#define HDMI_I2S_SEL_SDATA2_DEFAULT_2 (0x7) - -/* I2S_PIN_SEL_2 */ -#define HDMI_I2S_SEL_SDATA3(x) (((x) & 0x7) << 4) -#define HDMI_I2S_SEL_SDATA3_DEFAULT_5 (0x7 << 4) -#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7) -#define HDMI_I2S_SEL_SDATA2_DEFAULT_4 (0x7) - -/* I2S_PIN_SEL_3 */ -#define HDMI_I2S_SEL_DSD(x) ((x) & 0x7) -#define HDMI_I2S_SEL_DSD_DEFAULT_6 (0x7) - -/* I2S_DSD_CON */ -#define HDMI_I2S_DSD_CLK_RI_EDGE (1 << 1) -#define HDMI_I2S_DSD_CLK_FA_EDGE (0 << 1) -#define HDMI_I2S_DSD_ENABLE (1) -#define HDMI_I2S_DSD_DISABLE (0) - -/* I2S_MUX_CON */ -#define HDMI_I2S_NOISE_FILTER_ZERO (0 << 5) -#define HDMI_I2S_NOISE_FILTER_2_STAGE (1 << 5) -#define HDMI_I2S_NOISE_FILTER_3_STAGE (2 << 5) -#define HDMI_I2S_NOISE_FILTER_4_STAGE (3 << 5) -#define HDMI_I2S_NOISE_FILTER_5_STAGE (4 << 5) -#define HDMI_I2S_IN_DISABLE (1 << 4) -#define HDMI_I2S_IN_ENABLE (0 << 4) -#define HDMI_I2S_AUD_SPDIF (0 << 2) -#define HDMI_I2S_AUD_I2S (1 << 2) -#define HDMI_I2S_AUD_DSD (2 << 2) -#define HDMI_I2S_CUV_SPDIF_ENABLE (0 << 1) -#define HDMI_I2S_CUV_I2S_ENABLE (1 << 1) -#define HDMI_I2S_MUX_DISABLE (0) -#define HDMI_I2S_MUX_ENABLE (1) -#define HDMI_I2S_MUX_CON_CLR (~(0xFF)) - -/* I2S_CH_ST_CON */ -#define HDMI_I2S_CH_STATUS_RELOAD (1) -#define HDMI_I2S_CH_ST_CON_CLR (~(1)) - -/* I2S_CH_ST_0 / I2S_CH_ST_SH_0 */ -#define HDMI_I2S_CH_STATUS_MODE_0 (0 << 6) -#define HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH (0 << 3) -#define HDMI_I2S_2AUD_CH_WITH_PREEMPH (1 << 3) -#define HDMI_I2S_DEFAULT_EMPHASIS (0 << 3) -#define HDMI_I2S_COPYRIGHT (0 << 2) -#define HDMI_I2S_NO_COPYRIGHT (1 << 2) -#define HDMI_I2S_LINEAR_PCM (0 << 1) -#define HDMI_I2S_NO_LINEAR_PCM (1 << 1) -#define HDMI_I2S_CONSUMER_FORMAT (0) -#define HDMI_I2S_PROF_FORMAT (1) -#define HDMI_I2S_CH_ST_0_CLR (~(0xFF)) - -/* I2S_CH_ST_1 / I2S_CH_ST_SH_1 */ -#define HDMI_I2S_CD_PLAYER (0x00) -#define HDMI_I2S_DAT_PLAYER (0x03) -#define HDMI_I2S_DCC_PLAYER (0x43) -#define HDMI_I2S_MINI_DISC_PLAYER (0x49) - -/* I2S_CH_ST_2 / I2S_CH_ST_SH_2 */ -#define HDMI_I2S_CHANNEL_NUM_MASK (0xF << 4) -#define HDMI_I2S_SOURCE_NUM_MASK (0xF) -#define HDMI_I2S_SET_CHANNEL_NUM(x) (((x) & (0xF)) << 4) -#define HDMI_I2S_SET_SOURCE_NUM(x) ((x) & (0xF)) - -/* I2S_CH_ST_3 / I2S_CH_ST_SH_3 */ -#define HDMI_I2S_CLK_ACCUR_LEVEL_1 (1 << 4) -#define HDMI_I2S_CLK_ACCUR_LEVEL_2 (0 << 4) -#define HDMI_I2S_CLK_ACCUR_LEVEL_3 (2 << 4) -#define HDMI_I2S_SMP_FREQ_44_1 (0x0) -#define HDMI_I2S_SMP_FREQ_48 (0x2) -#define HDMI_I2S_SMP_FREQ_32 (0x3) -#define HDMI_I2S_SMP_FREQ_96 (0xA) -#define HDMI_I2S_SET_SMP_FREQ(x) ((x) & (0xF)) - -/* I2S_CH_ST_4 / I2S_CH_ST_SH_4 */ -#define HDMI_I2S_ORG_SMP_FREQ_44_1 (0xF << 4) -#define HDMI_I2S_ORG_SMP_FREQ_88_2 (0x7 << 4) -#define HDMI_I2S_ORG_SMP_FREQ_22_05 (0xB << 4) -#define HDMI_I2S_ORG_SMP_FREQ_176_4 (0x3 << 4) -#define HDMI_I2S_WORD_LEN_NOT_DEFINE (0x0 << 1) -#define HDMI_I2S_WORD_LEN_MAX24_20BITS (0x1 << 1) -#define HDMI_I2S_WORD_LEN_MAX24_22BITS (0x2 << 1) -#define HDMI_I2S_WORD_LEN_MAX24_23BITS (0x4 << 1) -#define HDMI_I2S_WORD_LEN_MAX24_24BITS (0x5 << 1) -#define HDMI_I2S_WORD_LEN_MAX24_21BITS (0x6 << 1) -#define HDMI_I2S_WORD_LEN_MAX20_16BITS (0x1 << 1) -#define HDMI_I2S_WORD_LEN_MAX20_18BITS (0x2 << 1) -#define HDMI_I2S_WORD_LEN_MAX20_19BITS (0x4 << 1) -#define HDMI_I2S_WORD_LEN_MAX20_20BITS (0x5 << 1) -#define HDMI_I2S_WORD_LEN_MAX20_17BITS (0x6 << 1) -#define HDMI_I2S_WORD_LEN_MAX_24BITS (1) -#define HDMI_I2S_WORD_LEN_MAX_20BITS (0) - -/* I2S_VD_DATA */ -#define HDMI_I2S_VD_AUD_SMP_RELIABLE (0) -#define HDMI_I2S_VD_AUD_SMP_UNRELIABLE (1) - -/* I2S_MUX_CH */ -#define HDMI_I2S_CH3_R_EN (1 << 7) -#define HDMI_I2S_CH3_L_EN (1 << 6) -#define HDMI_I2S_CH3_EN (3 << 6) -#define HDMI_I2S_CH2_R_EN (1 << 5) -#define HDMI_I2S_CH2_L_EN (1 << 4) -#define HDMI_I2S_CH2_EN (3 << 4) -#define HDMI_I2S_CH1_R_EN (1 << 3) -#define HDMI_I2S_CH1_L_EN (1 << 2) -#define HDMI_I2S_CH1_EN (3 << 2) -#define HDMI_I2S_CH0_R_EN (1 << 1) -#define HDMI_I2S_CH0_L_EN (1) -#define HDMI_I2S_CH0_EN (3) -#define HDMI_I2S_CH_ALL_EN (0xFF) -#define HDMI_I2S_MUX_CH_CLR (~HDMI_I2S_CH_ALL_EN) - -/* I2S_MUX_CUV */ -#define HDMI_I2S_CUV_R_EN (1 << 1) -#define HDMI_I2S_CUV_L_EN (1) -#define HDMI_I2S_CUV_RL_EN (0x03) - -/* I2S_IRQ_MASK */ -#define HDMI_I2S_INT2_DIS (0 << 1) -#define HDMI_I2S_INT2_EN (1 << 1) - -/* I2S_IRQ_STATUS */ -#define HDMI_I2S_INT2_STATUS (1 << 1) - -/* I2S_CUV_L_R */ -#define HDMI_I2S_CUV_R_DATA_MASK (0x7 << 4) -#define HDMI_I2S_CUV_L_DATA_MASK (0x7) - -/* Audio Related Packet bit definition */ - -/* ASP_CON */ -#define HDMI_AUD_DST_DOUBLE (1 << 7) -#define HDMI_AUD_NO_DST_DOUBLE (0 << 7) -#define HDMI_AUD_TYPE_SAMPLE (0 << 5) -#define HDMI_AUD_TYPE_ONE_BIT (1 << 5) -#define HDMI_AUD_TYPE_HBR (2 << 5) -#define HDMI_AUD_TYPE_DST (3 << 5) -#define HDMI_AUD_MODE_TWO_CH (0 << 4) -#define HDMI_AUD_MODE_MULTI_CH (1 << 4) -#define HDMI_AUD_SP_AUD3_EN (1 << 3) -#define HDMI_AUD_SP_AUD2_EN (1 << 2) -#define HDMI_AUD_SP_AUD1_EN (1 << 1) -#define HDMI_AUD_SP_AUD0_EN (1 << 0) -#define HDMI_AUD_SP_ALL_DIS (0 << 0) - -#define HDMI_AUD_SET_SP_PRE(x) ((x) & 0xF) - -/* ASP_SP_FLAT */ -#define HDMI_ASP_SP_FLAT_AUD_SAMPLE (0) - -/* ASP_CHCFG0/1/2/3 */ -#define HDMI_SPK3R_SEL_I_PCM0L (0 << 27) -#define HDMI_SPK3R_SEL_I_PCM0R (1 << 27) -#define HDMI_SPK3R_SEL_I_PCM1L (2 << 27) -#define HDMI_SPK3R_SEL_I_PCM1R (3 << 27) -#define HDMI_SPK3R_SEL_I_PCM2L (4 << 27) -#define HDMI_SPK3R_SEL_I_PCM2R (5 << 27) -#define HDMI_SPK3R_SEL_I_PCM3L (6 << 27) -#define HDMI_SPK3R_SEL_I_PCM3R (7 << 27) -#define HDMI_SPK3L_SEL_I_PCM0L (0 << 24) -#define HDMI_SPK3L_SEL_I_PCM0R (1 << 24) -#define HDMI_SPK3L_SEL_I_PCM1L (2 << 24) -#define HDMI_SPK3L_SEL_I_PCM1R (3 << 24) -#define HDMI_SPK3L_SEL_I_PCM2L (4 << 24) -#define HDMI_SPK3L_SEL_I_PCM2R (5 << 24) -#define HDMI_SPK3L_SEL_I_PCM3L (6 << 24) -#define HDMI_SPK3L_SEL_I_PCM3R (7 << 24) -#define HDMI_SPK2R_SEL_I_PCM0L (0 << 19) -#define HDMI_SPK2R_SEL_I_PCM0R (1 << 19) -#define HDMI_SPK2R_SEL_I_PCM1L (2 << 19) -#define HDMI_SPK2R_SEL_I_PCM1R (3 << 19) -#define HDMI_SPK2R_SEL_I_PCM2L (4 << 19) -#define HDMI_SPK2R_SEL_I_PCM2R (5 << 19) -#define HDMI_SPK2R_SEL_I_PCM3L (6 << 19) -#define HDMI_SPK2R_SEL_I_PCM3R (7 << 19) -#define HDMI_SPK2L_SEL_I_PCM0L (0 << 16) -#define HDMI_SPK2L_SEL_I_PCM0R (1 << 16) -#define HDMI_SPK2L_SEL_I_PCM1L (2 << 16) -#define HDMI_SPK2L_SEL_I_PCM1R (3 << 16) -#define HDMI_SPK2L_SEL_I_PCM2L (4 << 16) -#define HDMI_SPK2L_SEL_I_PCM2R (5 << 16) -#define HDMI_SPK2L_SEL_I_PCM3L (6 << 16) -#define HDMI_SPK2L_SEL_I_PCM3R (7 << 16) -#define HDMI_SPK1R_SEL_I_PCM0L (0 << 11) -#define HDMI_SPK1R_SEL_I_PCM0R (1 << 11) -#define HDMI_SPK1R_SEL_I_PCM1L (2 << 11) -#define HDMI_SPK1R_SEL_I_PCM1R (3 << 11) -#define HDMI_SPK1R_SEL_I_PCM2L (4 << 11) -#define HDMI_SPK1R_SEL_I_PCM2R (5 << 11) -#define HDMI_SPK1R_SEL_I_PCM3L (6 << 11) -#define HDMI_SPK1R_SEL_I_PCM3R (7 << 11) -#define HDMI_SPK1L_SEL_I_PCM0L (0 << 8) -#define HDMI_SPK1L_SEL_I_PCM0R (1 << 8) -#define HDMI_SPK1L_SEL_I_PCM1L (2 << 8) -#define HDMI_SPK1L_SEL_I_PCM1R (3 << 8) -#define HDMI_SPK1L_SEL_I_PCM2L (4 << 8) -#define HDMI_SPK1L_SEL_I_PCM2R (5 << 8) -#define HDMI_SPK1L_SEL_I_PCM3L (6 << 8) -#define HDMI_SPK1L_SEL_I_PCM3R (7 << 8) -#define HDMI_SPK0R_SEL_I_PCM0L (0 << 3) -#define HDMI_SPK0R_SEL_I_PCM0R (1 << 3) -#define HDMI_SPK0R_SEL_I_PCM1L (2 << 3) -#define HDMI_SPK0R_SEL_I_PCM1R (3 << 3) -#define HDMI_SPK0R_SEL_I_PCM2L (4 << 3) -#define HDMI_SPK0R_SEL_I_PCM2R (5 << 3) -#define HDMI_SPK0R_SEL_I_PCM3L (6 << 3) -#define HDMI_SPK0R_SEL_I_PCM3R (7 << 3) -#define HDMI_SPK0L_SEL_I_PCM0L (0) -#define HDMI_SPK0L_SEL_I_PCM0R (1) -#define HDMI_SPK0L_SEL_I_PCM1L (2) -#define HDMI_SPK0L_SEL_I_PCM1R (3) -#define HDMI_SPK0L_SEL_I_PCM2L (4) -#define HDMI_SPK0L_SEL_I_PCM2R (5) -#define HDMI_SPK0L_SEL_I_PCM3L (6) -#define HDMI_SPK0L_SEL_I_PCM3R (7) - -/* ACR_CON */ -#define HDMI_ALT_CTS_RATE_CTS_1 (0 << 3) -#define HDMI_ALT_CTS_RATE_CTS_11 (1 << 3) -#define HDMI_ALT_CTS_RATE_CTS_21 (2 << 3) -#define HDMI_ALT_CTS_RATE_CTS_31 (3 << 3) -#define HDMI_ACR_TX_MODE_NO_TX (0) -#define HDMI_ACR_TX_MODE_TX_ONCE (1) -#define HDMI_ACR_TX_MODE_TXCNT_VBI (2) -#define HDMI_ACR_TX_MODE_TX_VPC (3) -#define HDMI_ACR_TX_MODE_MESURE_CTS (4) - -/* ACR_MCTS0/1/2 */ - -/* ACR_CTS0/1/2 */ - -/* ACR_N0/1/2 */ - -/* ACR_LSB2 */ -#define HDMI_ACR_LSB2_MASK (0xFF) - -/* ACR_TXCNT */ -#define HDMI_ACR_TXCNT_MASK (0x1F) - -/* ACR_TXINTERNAL */ -#define HDMI_ACR_TX_INTERNAL_MASK (0xFF) - -/* ACR_CTS_OFFSET */ -#define HDMI_ACR_CTS_OFFSET_MASK (0xFF) - -/* GCP_CON */ -#define HDMI_GCP_CON_EN_1ST_VSYNC (1 << 3) -#define HDMI_GCP_CON_EN_2ST_VSYNC (1 << 2) -#define HDMI_GCP_CON_TRANS_EVERY_VSYNC (2) -#define HDMI_GCP_CON_NO_TRAN (0) -#define HDMI_GCP_CON_TRANS_ONCE (1) -#define HDMI_GCP_CON_TRANS_EVERY_VSYNC (2) - -/* GCP_BYTE1 */ -#define HDMI_GCP_BYTE1_MASK (0xFF) - -/* GCP_BYTE2 */ -#define HDMI_GCP_BYTE2_PP_MASK (0xF << 4) -#define HDMI_GCP_24BPP (1 << 2) -#define HDMI_GCP_30BPP (1 << 0 | 1 << 2) -#define HDMI_GCP_36BPP (1 << 1 | 1 << 2) -#define HDMI_GCP_48BPP (1 << 0 | 1 << 1 | 1 << 2) - -/* GCP_BYTE3 */ -#define HDMI_GCP_BYTE3_MASK (0xFF) - -/* Timing generator registers */ -/* TG configure/status registers */ -#define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068) -#define HDMI_TG_VACT_ST3_H HDMI_TG_BASE(0x006c) -#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070) -#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074) -#define HDMI_TG_3D HDMI_TG_BASE(0x00F0) - -#endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/drivers/gpu/drm/exynos_tmp/regs-mixer.h b/drivers/gpu/drm/exynos_tmp/regs-mixer.h deleted file mode 100644 index fd2f4d1..0000000 --- a/drivers/gpu/drm/exynos_tmp/regs-mixer.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - * Cloned from drivers/media/video/s5p-tv/regs-mixer.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Mixer register header file for Samsung Mixer driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ -#ifndef SAMSUNG_REGS_MIXER_H -#define SAMSUNG_REGS_MIXER_H - -/* - * Register part - */ -#define MXR_STATUS 0x0000 -#define MXR_CFG 0x0004 -#define MXR_INT_EN 0x0008 -#define MXR_INT_STATUS 0x000C -#define MXR_LAYER_CFG 0x0010 -#define MXR_VIDEO_CFG 0x0014 -#define MXR_GRAPHIC0_CFG 0x0020 -#define MXR_GRAPHIC0_BASE 0x0024 -#define MXR_GRAPHIC0_SPAN 0x0028 -#define MXR_GRAPHIC0_SXY 0x002C -#define MXR_GRAPHIC0_WH 0x0030 -#define MXR_GRAPHIC0_DXY 0x0034 -#define MXR_GRAPHIC0_BLANK 0x0038 -#define MXR_GRAPHIC1_CFG 0x0040 -#define MXR_GRAPHIC1_BASE 0x0044 -#define MXR_GRAPHIC1_SPAN 0x0048 -#define MXR_GRAPHIC1_SXY 0x004C -#define MXR_GRAPHIC1_WH 0x0050 -#define MXR_GRAPHIC1_DXY 0x0054 -#define MXR_GRAPHIC1_BLANK 0x0058 -#define MXR_BG_CFG 0x0060 -#define MXR_BG_COLOR0 0x0064 -#define MXR_BG_COLOR1 0x0068 -#define MXR_BG_COLOR2 0x006C -#define MXR_CM_COEFF_Y 0x0080 -#define MXR_CM_COEFF_CB 0x0084 -#define MXR_CM_COEFF_CR 0x0088 -#define MXR_GRAPHIC0_BASE_S 0x2024 -#define MXR_GRAPHIC1_BASE_S 0x2044 - -/* for parametrized access to layer registers */ -#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20) -#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20) -#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20) -#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20) -#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20) -#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20) -#define MXR_GRAPHIC_BLANK(i) (0x0038 + (i) * 0x20) -#define MXR_GRAPHIC_BASE_S(i) (0x2024 + (i) * 0x20) - -/* - * Bit definition part - */ - -/* generates mask for range of bits */ -#define MXR_MASK(high_bit, low_bit) \ - (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) - -#define MXR_MASK_VAL(val, high_bit, low_bit) \ - (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit)) - -/* bits for MXR_STATUS */ -#define MXR_STATUS_16_BURST (1 << 7) -#define MXR_STATUS_BURST_MASK (1 << 7) -#define MXR_STATUS_BIG_ENDIAN (1 << 3) -#define MXR_STATUS_ENDIAN_MASK (1 << 3) -#define MXR_STATUS_SYNC_ENABLE (1 << 2) -#define MXR_STATUS_REG_RUN (1 << 0) - -/* bits for MXR_CFG */ -#define MXR_CFG_RGB601_0_255 (0 << 9) -#define MXR_CFG_RGB601_16_235 (1 << 9) -#define MXR_CFG_RGB709_0_255 (2 << 9) -#define MXR_CFG_RGB709_16_235 (3 << 9) -#define MXR_CFG_RGB_FMT_MASK 0x600 -#define MXR_CFG_OUT_YUV444 (0 << 8) -#define MXR_CFG_OUT_RGB888 (1 << 8) -#define MXR_CFG_OUT_MASK (1 << 8) -#define MXR_CFG_DST_SDO (0 << 7) -#define MXR_CFG_DST_HDMI (1 << 7) -#define MXR_CFG_DST_MASK (1 << 7) -#define MXR_CFG_SCAN_HD_720 (0 << 6) -#define MXR_CFG_SCAN_HD_1080 (1 << 6) -#define MXR_CFG_GRP1_ENABLE (1 << 5) -#define MXR_CFG_GRP0_ENABLE (1 << 4) -#define MXR_CFG_VP_ENABLE (1 << 3) -#define MXR_CFG_SCAN_INTERLACE (0 << 2) -#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2) -#define MXR_CFG_SCAN_NTSC (0 << 1) -#define MXR_CFG_SCAN_PAL (1 << 1) -#define MXR_CFG_SCAN_SD (0 << 0) -#define MXR_CFG_SCAN_HD (1 << 0) -#define MXR_CFG_SCAN_MASK 0x47 - -/* bits for MXR_GRAPHICn_CFG */ -#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21) -#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20) -#define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17) -#define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16) -#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8) -#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0) -#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0) - -/* bits for MXR_GRAPHICn_WH */ -#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28) -#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12) -#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_GRAPHICn_SXY */ -#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_GRAPHICn_DXY */ -#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_INT_EN */ -#define MXR_INT_EN_VSYNC (1 << 11) -#define MXR_INT_EN_ALL (0x0f << 8) - -/* bit for MXR_INT_STATUS */ -#define MXR_INT_CLEAR_VSYNC (1 << 11) -#define MXR_INT_STATUS_VSYNC (1 << 0) - -/* bit for MXR_LAYER_CFG */ -#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8) -#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4) -#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0) - -#endif /* SAMSUNG_REGS_MIXER_H */ - diff --git a/drivers/gpu/drm/exynos_tmp/regs-vp.h b/drivers/gpu/drm/exynos_tmp/regs-vp.h deleted file mode 100644 index 10b737a..0000000 --- a/drivers/gpu/drm/exynos_tmp/regs-vp.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Cloned from drivers/media/video/s5p-tv/regs-vp.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Video processor register header file for Samsung Mixer driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef SAMSUNG_REGS_VP_H -#define SAMSUNG_REGS_VP_H - -/* - * Register part - */ - -#define VP_ENABLE 0x0000 -#define VP_SRESET 0x0004 -#define VP_SHADOW_UPDATE 0x0008 -#define VP_FIELD_ID 0x000C -#define VP_MODE 0x0010 -#define VP_IMG_SIZE_Y 0x0014 -#define VP_IMG_SIZE_C 0x0018 -#define VP_PER_RATE_CTRL 0x001C -#define VP_TOP_Y_PTR 0x0028 -#define VP_BOT_Y_PTR 0x002C -#define VP_TOP_C_PTR 0x0030 -#define VP_BOT_C_PTR 0x0034 -#define VP_ENDIAN_MODE 0x03CC -#define VP_SRC_H_POSITION 0x0044 -#define VP_SRC_V_POSITION 0x0048 -#define VP_SRC_WIDTH 0x004C -#define VP_SRC_HEIGHT 0x0050 -#define VP_DST_H_POSITION 0x0054 -#define VP_DST_V_POSITION 0x0058 -#define VP_DST_WIDTH 0x005C -#define VP_DST_HEIGHT 0x0060 -#define VP_H_RATIO 0x0064 -#define VP_V_RATIO 0x0068 -#define VP_POLY8_Y0_LL 0x006C -#define VP_POLY4_Y0_LL 0x00EC -#define VP_POLY4_C0_LL 0x012C - -/* - * Bit definition part - */ - -/* generates mask for range of bits */ - -#define VP_MASK(high_bit, low_bit) \ - (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) - -#define VP_MASK_VAL(val, high_bit, low_bit) \ - (((val) << (low_bit)) & VP_MASK(high_bit, low_bit)) - - /* VP_ENABLE */ -#define VP_ENABLE_ON (1 << 0) - -/* VP_SRESET */ -#define VP_SRESET_PROCESSING (1 << 0) - -/* VP_SHADOW_UPDATE */ -#define VP_SHADOW_UPDATE_ENABLE (1 << 0) - -/* VP_MODE */ -#define VP_MODE_NV12 (0 << 6) -#define VP_MODE_NV21 (1 << 6) -#define VP_MODE_LINE_SKIP (1 << 5) -#define VP_MODE_MEM_LINEAR (0 << 4) -#define VP_MODE_MEM_TILED (1 << 4) -#define VP_MODE_FMT_MASK (5 << 4) -#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2) -#define VP_MODE_2D_IPC (1 << 1) - -/* VP_IMG_SIZE_Y */ -/* VP_IMG_SIZE_C */ -#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16) -#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0) - -/* VP_SRC_H_POSITION */ -#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4) - -/* VP_ENDIAN_MODE */ -#define VP_ENDIAN_MODE_LITTLE (1 << 0) - -#endif /* SAMSUNG_REGS_VP_H */ diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 92101e3..028d1fa 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1411,7 +1411,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, if (args->buffer_count < 1 || args->buffer_count > UINT_MAX / sizeof(*exec2_list)) { - DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count); + DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6bbc686..f797daf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8591,7 +8591,7 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - for_each_pipe(pipe) { + for_each_pipe(pipe) I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 491aa9e..95db2e9 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -733,6 +733,30 @@ static const struct dmi_system_id intel_no_lvds[] = { }, { .callback = intel_no_lvds_dmi_callback, + .ident = "Asus AT5NM10T-I", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Hewlett-Packard t5745", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_BOARD_NAME, "hp t5745"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Hewlett-Packard st5747", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_BOARD_NAME, "hp st5747"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, .ident = "MSI Wind Box DC500", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 9edbf14..466db41 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -3408,17 +3408,6 @@ int evergreen_init(struct radeon_device *rdev) } } - /* Don't start up if the MC ucode is missing on BTC parts. - * The default clocks and voltages before the MC ucode - * is loaded are not suffient for advanced operations. - */ - if (ASIC_IS_DCE5(rdev)) { - if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) { - DRM_ERROR("radeon: MC ucode required for NI+.\n"); - return -EINVAL; - } - } - return 0; } diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 0970bed..ae356cf 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -2244,8 +2244,7 @@ void r100_bm_disable(struct radeon_device *rdev) WREG32(R_000030_BUS_CNTL, (tmp & 0xFFFFFFFF) | 0x00000040); tmp = RREG32(RADEON_BUS_CNTL); mdelay(1); - pci_read_config_word(rdev->pdev, 0x4, &tmp16); - pci_write_config_word(rdev->pdev, 0x4, tmp16 & 0xFFFB); + pci_clear_master(rdev->pdev); mdelay(1); } diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index e9f7662..d25cf86 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -341,16 +341,6 @@ void rs600_hpd_fini(struct radeon_device *rdev) } } -void rs600_bm_disable(struct radeon_device *rdev) -{ - u16 tmp; - - /* disable bus mastering */ - pci_read_config_word(rdev->pdev, 0x4, &tmp); - pci_write_config_word(rdev->pdev, 0x4, tmp & 0xFFFB); - mdelay(1); -} - int rs600_asic_reset(struct radeon_device *rdev) { struct rv515_mc_save save; |