diff options
35 files changed, 9158 insertions, 264 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 86fb75d3fcad..1d1f1e5e33f0 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig | |||
| @@ -45,3 +45,27 @@ config DRM_EXYNOS_G2D | |||
| 45 | depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D | 45 | depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D |
| 46 | help | 46 | help |
| 47 | Choose this option if you want to use Exynos G2D for DRM. | 47 | Choose this option if you want to use Exynos G2D for DRM. |
| 48 | |||
| 49 | config DRM_EXYNOS_IPP | ||
| 50 | bool "Exynos DRM IPP" | ||
| 51 | depends on DRM_EXYNOS | ||
| 52 | help | ||
| 53 | Choose this option if you want to use IPP feature for DRM. | ||
| 54 | |||
| 55 | config DRM_EXYNOS_FIMC | ||
| 56 | bool "Exynos DRM FIMC" | ||
| 57 | depends on DRM_EXYNOS_IPP | ||
| 58 | help | ||
| 59 | Choose this option if you want to use Exynos FIMC for DRM. | ||
| 60 | |||
| 61 | config DRM_EXYNOS_ROTATOR | ||
| 62 | bool "Exynos DRM Rotator" | ||
| 63 | depends on DRM_EXYNOS_IPP | ||
| 64 | help | ||
| 65 | Choose this option if you want to use Exynos Rotator for DRM. | ||
| 66 | |||
| 67 | config DRM_EXYNOS_GSC | ||
| 68 | bool "Exynos DRM GSC" | ||
| 69 | depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 | ||
| 70 | help | ||
| 71 | Choose this option if you want to use Exynos GSC for DRM. | ||
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 26813b8a5056..639b49e1ec05 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile | |||
| @@ -16,5 +16,9 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ | |||
| 16 | exynos_drm_hdmi.o | 16 | exynos_drm_hdmi.o |
| 17 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o | 17 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o |
| 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o | 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o |
| 19 | exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o | ||
| 20 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o | ||
| 21 | exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o | ||
| 22 | exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o | ||
| 19 | 23 | ||
| 20 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o | 24 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o |
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c index 37e6ec704e1d..bef43e0342a6 100644 --- a/drivers/gpu/drm/exynos/exynos_ddc.c +++ b/drivers/gpu/drm/exynos/exynos_ddc.c | |||
| @@ -48,6 +48,7 @@ static struct i2c_device_id ddc_idtable[] = { | |||
| 48 | { }, | 48 | { }, |
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | #ifdef CONFIG_OF | ||
| 51 | static struct of_device_id hdmiddc_match_types[] = { | 52 | static struct of_device_id hdmiddc_match_types[] = { |
| 52 | { | 53 | { |
| 53 | .compatible = "samsung,exynos5-hdmiddc", | 54 | .compatible = "samsung,exynos5-hdmiddc", |
| @@ -55,12 +56,13 @@ static struct of_device_id hdmiddc_match_types[] = { | |||
| 55 | /* end node */ | 56 | /* end node */ |
| 56 | } | 57 | } |
| 57 | }; | 58 | }; |
| 59 | #endif | ||
| 58 | 60 | ||
| 59 | struct i2c_driver ddc_driver = { | 61 | struct i2c_driver ddc_driver = { |
| 60 | .driver = { | 62 | .driver = { |
| 61 | .name = "exynos-hdmiddc", | 63 | .name = "exynos-hdmiddc", |
| 62 | .owner = THIS_MODULE, | 64 | .owner = THIS_MODULE, |
| 63 | .of_match_table = hdmiddc_match_types, | 65 | .of_match_table = of_match_ptr(hdmiddc_match_types), |
| 64 | }, | 66 | }, |
| 65 | .id_table = ddc_idtable, | 67 | .id_table = ddc_idtable, |
| 66 | .probe = s5p_ddc_probe, | 68 | .probe = s5p_ddc_probe, |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 72bf97b96ba0..9601bad47a2e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c | |||
| @@ -34,7 +34,8 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, | |||
| 34 | unsigned int flags, struct exynos_drm_gem_buf *buf) | 34 | unsigned int flags, struct exynos_drm_gem_buf *buf) |
| 35 | { | 35 | { |
| 36 | int ret = 0; | 36 | int ret = 0; |
| 37 | enum dma_attr attr = DMA_ATTR_FORCE_CONTIGUOUS; | 37 | enum dma_attr attr; |
| 38 | unsigned int nr_pages; | ||
| 38 | 39 | ||
| 39 | DRM_DEBUG_KMS("%s\n", __FILE__); | 40 | DRM_DEBUG_KMS("%s\n", __FILE__); |
| 40 | 41 | ||
| @@ -45,44 +46,49 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, | |||
| 45 | 46 | ||
| 46 | init_dma_attrs(&buf->dma_attrs); | 47 | init_dma_attrs(&buf->dma_attrs); |
| 47 | 48 | ||
| 48 | if (flags & EXYNOS_BO_NONCONTIG) | 49 | /* |
| 50 | * if EXYNOS_BO_CONTIG, fully physically contiguous memory | ||
| 51 | * region will be allocated else physically contiguous | ||
| 52 | * as possible. | ||
| 53 | */ | ||
| 54 | if (flags & EXYNOS_BO_CONTIG) | ||
| 55 | dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs); | ||
| 56 | |||
| 57 | /* | ||
| 58 | * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping | ||
| 59 | * else cachable mapping. | ||
| 60 | */ | ||
| 61 | if (flags & EXYNOS_BO_WC || !(flags & EXYNOS_BO_CACHABLE)) | ||
| 49 | attr = DMA_ATTR_WRITE_COMBINE; | 62 | attr = DMA_ATTR_WRITE_COMBINE; |
| 63 | else | ||
| 64 | attr = DMA_ATTR_NON_CONSISTENT; | ||
| 50 | 65 | ||
| 51 | dma_set_attr(attr, &buf->dma_attrs); | 66 | dma_set_attr(attr, &buf->dma_attrs); |
| 67 | dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs); | ||
| 52 | 68 | ||
| 53 | buf->kvaddr = dma_alloc_attrs(dev->dev, buf->size, | 69 | buf->pages = dma_alloc_attrs(dev->dev, buf->size, |
| 54 | &buf->dma_addr, GFP_KERNEL, &buf->dma_attrs); | 70 | &buf->dma_addr, GFP_KERNEL, &buf->dma_attrs); |
| 55 | if (!buf->kvaddr) { | 71 | if (!buf->pages) { |
| 56 | DRM_ERROR("failed to allocate buffer.\n"); | 72 | DRM_ERROR("failed to allocate buffer.\n"); |
| 57 | return -ENOMEM; | 73 | return -ENOMEM; |
| 58 | } | 74 | } |
| 59 | 75 | ||
| 60 | buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); | 76 | nr_pages = buf->size >> PAGE_SHIFT; |
| 77 | buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages); | ||
| 61 | if (!buf->sgt) { | 78 | if (!buf->sgt) { |
| 62 | DRM_ERROR("failed to allocate sg table.\n"); | 79 | DRM_ERROR("failed to get sg table.\n"); |
| 63 | ret = -ENOMEM; | 80 | ret = -ENOMEM; |
| 64 | goto err_free_attrs; | 81 | goto err_free_attrs; |
| 65 | } | 82 | } |
| 66 | 83 | ||
| 67 | ret = dma_get_sgtable(dev->dev, buf->sgt, buf->kvaddr, buf->dma_addr, | 84 | DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", |
| 68 | buf->size); | ||
| 69 | if (ret < 0) { | ||
| 70 | DRM_ERROR("failed to get sgtable.\n"); | ||
| 71 | goto err_free_sgt; | ||
| 72 | } | ||
| 73 | |||
| 74 | DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", | ||
| 75 | (unsigned long)buf->kvaddr, | ||
| 76 | (unsigned long)buf->dma_addr, | 85 | (unsigned long)buf->dma_addr, |
| 77 | buf->size); | 86 | buf->size); |
| 78 | 87 | ||
| 79 | return ret; | 88 | return ret; |
| 80 | 89 | ||
| 81 | err_free_sgt: | ||
| 82 | kfree(buf->sgt); | ||
| 83 | buf->sgt = NULL; | ||
| 84 | err_free_attrs: | 90 | err_free_attrs: |
| 85 | dma_free_attrs(dev->dev, buf->size, buf->kvaddr, | 91 | dma_free_attrs(dev->dev, buf->size, buf->pages, |
| 86 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); | 92 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); |
| 87 | buf->dma_addr = (dma_addr_t)NULL; | 93 | buf->dma_addr = (dma_addr_t)NULL; |
| 88 | 94 | ||
| @@ -99,8 +105,7 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, | |||
| 99 | return; | 105 | return; |
| 100 | } | 106 | } |
| 101 | 107 | ||
| 102 | DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", | 108 | DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", |
| 103 | (unsigned long)buf->kvaddr, | ||
| 104 | (unsigned long)buf->dma_addr, | 109 | (unsigned long)buf->dma_addr, |
| 105 | buf->size); | 110 | buf->size); |
| 106 | 111 | ||
| @@ -109,7 +114,7 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, | |||
| 109 | kfree(buf->sgt); | 114 | kfree(buf->sgt); |
| 110 | buf->sgt = NULL; | 115 | buf->sgt = NULL; |
| 111 | 116 | ||
| 112 | dma_free_attrs(dev->dev, buf->size, buf->kvaddr, | 117 | dma_free_attrs(dev->dev, buf->size, buf->pages, |
| 113 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); | 118 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); |
| 114 | buf->dma_addr = (dma_addr_t)NULL; | 119 | buf->dma_addr = (dma_addr_t)NULL; |
| 115 | } | 120 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index 539da9f4eb97..61d5a8402eb8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | |||
| @@ -30,63 +30,107 @@ | |||
| 30 | 30 | ||
| 31 | #include <linux/dma-buf.h> | 31 | #include <linux/dma-buf.h> |
| 32 | 32 | ||
| 33 | static struct sg_table *exynos_get_sgt(struct drm_device *drm_dev, | 33 | struct exynos_drm_dmabuf_attachment { |
| 34 | struct exynos_drm_gem_buf *buf) | 34 | struct sg_table sgt; |
| 35 | enum dma_data_direction dir; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static int exynos_gem_attach_dma_buf(struct dma_buf *dmabuf, | ||
| 39 | struct device *dev, | ||
| 40 | struct dma_buf_attachment *attach) | ||
| 35 | { | 41 | { |
| 36 | struct sg_table *sgt = NULL; | 42 | struct exynos_drm_dmabuf_attachment *exynos_attach; |
| 37 | int ret; | ||
| 38 | 43 | ||
| 39 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | 44 | exynos_attach = kzalloc(sizeof(*exynos_attach), GFP_KERNEL); |
| 40 | if (!sgt) | 45 | if (!exynos_attach) |
| 41 | goto out; | 46 | return -ENOMEM; |
| 42 | 47 | ||
| 43 | ret = dma_get_sgtable(drm_dev->dev, sgt, buf->kvaddr, | 48 | exynos_attach->dir = DMA_NONE; |
| 44 | buf->dma_addr, buf->size); | 49 | attach->priv = exynos_attach; |
| 45 | if (ret < 0) { | ||
| 46 | DRM_ERROR("failed to get sgtable.\n"); | ||
| 47 | goto err_free_sgt; | ||
| 48 | } | ||
| 49 | 50 | ||
| 50 | return sgt; | 51 | return 0; |
| 52 | } | ||
| 51 | 53 | ||
| 52 | err_free_sgt: | 54 | static void exynos_gem_detach_dma_buf(struct dma_buf *dmabuf, |
| 53 | kfree(sgt); | 55 | struct dma_buf_attachment *attach) |
| 54 | sgt = NULL; | 56 | { |
| 55 | out: | 57 | struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv; |
| 56 | return NULL; | 58 | struct sg_table *sgt; |
| 59 | |||
| 60 | if (!exynos_attach) | ||
| 61 | return; | ||
| 62 | |||
| 63 | sgt = &exynos_attach->sgt; | ||
| 64 | |||
| 65 | if (exynos_attach->dir != DMA_NONE) | ||
| 66 | dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, | ||
| 67 | exynos_attach->dir); | ||
| 68 | |||
| 69 | sg_free_table(sgt); | ||
| 70 | kfree(exynos_attach); | ||
| 71 | attach->priv = NULL; | ||
| 57 | } | 72 | } |
| 58 | 73 | ||
| 59 | static struct sg_table * | 74 | static struct sg_table * |
| 60 | exynos_gem_map_dma_buf(struct dma_buf_attachment *attach, | 75 | exynos_gem_map_dma_buf(struct dma_buf_attachment *attach, |
| 61 | enum dma_data_direction dir) | 76 | enum dma_data_direction dir) |
| 62 | { | 77 | { |
| 78 | struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv; | ||
| 63 | struct exynos_drm_gem_obj *gem_obj = attach->dmabuf->priv; | 79 | struct exynos_drm_gem_obj *gem_obj = attach->dmabuf->priv; |
| 64 | struct drm_device *dev = gem_obj->base.dev; | 80 | struct drm_device *dev = gem_obj->base.dev; |
| 65 | struct exynos_drm_gem_buf *buf; | 81 | struct exynos_drm_gem_buf *buf; |
| 82 | struct scatterlist *rd, *wr; | ||
| 66 | struct sg_table *sgt = NULL; | 83 | struct sg_table *sgt = NULL; |
| 67 | int nents; | 84 | unsigned int i; |
| 85 | int nents, ret; | ||
| 68 | 86 | ||
| 69 | DRM_DEBUG_PRIME("%s\n", __FILE__); | 87 | DRM_DEBUG_PRIME("%s\n", __FILE__); |
| 70 | 88 | ||
| 89 | if (WARN_ON(dir == DMA_NONE)) | ||
| 90 | return ERR_PTR(-EINVAL); | ||
| 91 | |||
| 92 | /* just return current sgt if already requested. */ | ||
| 93 | if (exynos_attach->dir == dir) | ||
| 94 | return &exynos_attach->sgt; | ||
| 95 | |||
| 96 | /* reattaching is not allowed. */ | ||
| 97 | if (WARN_ON(exynos_attach->dir != DMA_NONE)) | ||
| 98 | return ERR_PTR(-EBUSY); | ||
| 99 | |||
| 71 | buf = gem_obj->buffer; | 100 | buf = gem_obj->buffer; |
| 72 | if (!buf) { | 101 | if (!buf) { |
| 73 | DRM_ERROR("buffer is null.\n"); | 102 | DRM_ERROR("buffer is null.\n"); |
| 74 | return sgt; | 103 | return ERR_PTR(-ENOMEM); |
| 104 | } | ||
| 105 | |||
| 106 | sgt = &exynos_attach->sgt; | ||
| 107 | |||
| 108 | ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL); | ||
| 109 | if (ret) { | ||
| 110 | DRM_ERROR("failed to alloc sgt.\n"); | ||
| 111 | return ERR_PTR(-ENOMEM); | ||
| 75 | } | 112 | } |
| 76 | 113 | ||
| 77 | mutex_lock(&dev->struct_mutex); | 114 | mutex_lock(&dev->struct_mutex); |
| 78 | 115 | ||
| 79 | sgt = exynos_get_sgt(dev, buf); | 116 | rd = buf->sgt->sgl; |
| 80 | if (!sgt) | 117 | wr = sgt->sgl; |
| 81 | goto err_unlock; | 118 | for (i = 0; i < sgt->orig_nents; ++i) { |
| 119 | sg_set_page(wr, sg_page(rd), rd->length, rd->offset); | ||
| 120 | rd = sg_next(rd); | ||
| 121 | wr = sg_next(wr); | ||
| 122 | } | ||
| 82 | 123 | ||
| 83 | nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); | 124 | nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); |
| 84 | if (!nents) { | 125 | if (!nents) { |
| 85 | DRM_ERROR("failed to map sgl with iommu.\n"); | 126 | DRM_ERROR("failed to map sgl with iommu.\n"); |
| 86 | sgt = NULL; | 127 | sgt = ERR_PTR(-EIO); |
| 87 | goto err_unlock; | 128 | goto err_unlock; |
| 88 | } | 129 | } |
| 89 | 130 | ||
| 131 | exynos_attach->dir = dir; | ||
| 132 | attach->priv = exynos_attach; | ||
| 133 | |||
| 90 | DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size); | 134 | DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size); |
| 91 | 135 | ||
| 92 | err_unlock: | 136 | err_unlock: |
| @@ -98,11 +142,7 @@ static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach, | |||
| 98 | struct sg_table *sgt, | 142 | struct sg_table *sgt, |
| 99 | enum dma_data_direction dir) | 143 | enum dma_data_direction dir) |
| 100 | { | 144 | { |
| 101 | dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); | 145 | /* Nothing to do. */ |
| 102 | |||
| 103 | sg_free_table(sgt); | ||
| 104 | kfree(sgt); | ||
| 105 | sgt = NULL; | ||
| 106 | } | 146 | } |
| 107 | 147 | ||
| 108 | static void exynos_dmabuf_release(struct dma_buf *dmabuf) | 148 | static void exynos_dmabuf_release(struct dma_buf *dmabuf) |
| @@ -164,6 +204,8 @@ static int exynos_gem_dmabuf_mmap(struct dma_buf *dma_buf, | |||
| 164 | } | 204 | } |
| 165 | 205 | ||
| 166 | static struct dma_buf_ops exynos_dmabuf_ops = { | 206 | static struct dma_buf_ops exynos_dmabuf_ops = { |
| 207 | .attach = exynos_gem_attach_dma_buf, | ||
| 208 | .detach = exynos_gem_detach_dma_buf, | ||
| 167 | .map_dma_buf = exynos_gem_map_dma_buf, | 209 | .map_dma_buf = exynos_gem_map_dma_buf, |
| 168 | .unmap_dma_buf = exynos_gem_unmap_dma_buf, | 210 | .unmap_dma_buf = exynos_gem_unmap_dma_buf, |
| 169 | .kmap = exynos_gem_dmabuf_kmap, | 211 | .kmap = exynos_gem_dmabuf_kmap, |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2b287d2fc92e..e0a8e8024b01 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | #include "exynos_drm_vidi.h" | 40 | #include "exynos_drm_vidi.h" |
| 41 | #include "exynos_drm_dmabuf.h" | 41 | #include "exynos_drm_dmabuf.h" |
| 42 | #include "exynos_drm_g2d.h" | 42 | #include "exynos_drm_g2d.h" |
| 43 | #include "exynos_drm_ipp.h" | ||
| 43 | #include "exynos_drm_iommu.h" | 44 | #include "exynos_drm_iommu.h" |
| 44 | 45 | ||
| 45 | #define DRIVER_NAME "exynos" | 46 | #define DRIVER_NAME "exynos" |
| @@ -50,6 +51,9 @@ | |||
| 50 | 51 | ||
| 51 | #define VBLANK_OFF_DELAY 50000 | 52 | #define VBLANK_OFF_DELAY 50000 |
| 52 | 53 | ||
| 54 | /* platform device pointer for eynos drm device. */ | ||
| 55 | static struct platform_device *exynos_drm_pdev; | ||
| 56 | |||
| 53 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | 57 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) |
| 54 | { | 58 | { |
| 55 | struct exynos_drm_private *private; | 59 | struct exynos_drm_private *private; |
| @@ -246,6 +250,14 @@ static struct drm_ioctl_desc exynos_ioctls[] = { | |||
| 246 | exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), | 250 | exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), |
| 247 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, | 251 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, |
| 248 | exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), | 252 | exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), |
| 253 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, | ||
| 254 | exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH), | ||
| 255 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, | ||
| 256 | exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH), | ||
| 257 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, | ||
| 258 | exynos_drm_ipp_queue_buf, DRM_UNLOCKED | DRM_AUTH), | ||
| 259 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, | ||
| 260 | exynos_drm_ipp_cmd_ctrl, DRM_UNLOCKED | DRM_AUTH), | ||
| 249 | }; | 261 | }; |
| 250 | 262 | ||
| 251 | static const struct file_operations exynos_drm_driver_fops = { | 263 | static const struct file_operations exynos_drm_driver_fops = { |
| @@ -296,6 +308,7 @@ static int exynos_drm_platform_probe(struct platform_device *pdev) | |||
| 296 | { | 308 | { |
| 297 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | 309 | DRM_DEBUG_DRIVER("%s\n", __FILE__); |
| 298 | 310 | ||
| 311 | pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | ||
| 299 | exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); | 312 | exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); |
| 300 | 313 | ||
| 301 | return drm_platform_init(&exynos_drm_driver, pdev); | 314 | return drm_platform_init(&exynos_drm_driver, pdev); |
| @@ -341,6 +354,10 @@ static int __init exynos_drm_init(void) | |||
| 341 | ret = platform_driver_register(&exynos_drm_common_hdmi_driver); | 354 | ret = platform_driver_register(&exynos_drm_common_hdmi_driver); |
| 342 | if (ret < 0) | 355 | if (ret < 0) |
| 343 | goto out_common_hdmi; | 356 | goto out_common_hdmi; |
| 357 | |||
| 358 | ret = exynos_platform_device_hdmi_register(); | ||
| 359 | if (ret < 0) | ||
| 360 | goto out_common_hdmi_dev; | ||
| 344 | #endif | 361 | #endif |
| 345 | 362 | ||
| 346 | #ifdef CONFIG_DRM_EXYNOS_VIDI | 363 | #ifdef CONFIG_DRM_EXYNOS_VIDI |
| @@ -355,24 +372,80 @@ static int __init exynos_drm_init(void) | |||
| 355 | goto out_g2d; | 372 | goto out_g2d; |
| 356 | #endif | 373 | #endif |
| 357 | 374 | ||
| 375 | #ifdef CONFIG_DRM_EXYNOS_FIMC | ||
| 376 | ret = platform_driver_register(&fimc_driver); | ||
| 377 | if (ret < 0) | ||
| 378 | goto out_fimc; | ||
| 379 | #endif | ||
| 380 | |||
| 381 | #ifdef CONFIG_DRM_EXYNOS_ROTATOR | ||
| 382 | ret = platform_driver_register(&rotator_driver); | ||
| 383 | if (ret < 0) | ||
| 384 | goto out_rotator; | ||
| 385 | #endif | ||
| 386 | |||
| 387 | #ifdef CONFIG_DRM_EXYNOS_GSC | ||
| 388 | ret = platform_driver_register(&gsc_driver); | ||
| 389 | if (ret < 0) | ||
| 390 | goto out_gsc; | ||
| 391 | #endif | ||
| 392 | |||
| 393 | #ifdef CONFIG_DRM_EXYNOS_IPP | ||
| 394 | ret = platform_driver_register(&ipp_driver); | ||
| 395 | if (ret < 0) | ||
| 396 | goto out_ipp; | ||
| 397 | #endif | ||
| 398 | |||
| 358 | ret = platform_driver_register(&exynos_drm_platform_driver); | 399 | ret = platform_driver_register(&exynos_drm_platform_driver); |
| 359 | if (ret < 0) | 400 | if (ret < 0) |
| 401 | goto out_drm; | ||
| 402 | |||
| 403 | exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1, | ||
| 404 | NULL, 0); | ||
| 405 | if (IS_ERR_OR_NULL(exynos_drm_pdev)) { | ||
| 406 | ret = PTR_ERR(exynos_drm_pdev); | ||
| 360 | goto out; | 407 | goto out; |
| 408 | } | ||
| 361 | 409 | ||
| 362 | return 0; | 410 | return 0; |
| 363 | 411 | ||
| 364 | out: | 412 | out: |
| 413 | platform_driver_unregister(&exynos_drm_platform_driver); | ||
| 414 | |||
| 415 | out_drm: | ||
| 416 | #ifdef CONFIG_DRM_EXYNOS_IPP | ||
| 417 | platform_driver_unregister(&ipp_driver); | ||
| 418 | out_ipp: | ||
| 419 | #endif | ||
| 420 | |||
| 421 | #ifdef CONFIG_DRM_EXYNOS_GSC | ||
| 422 | platform_driver_unregister(&gsc_driver); | ||
| 423 | out_gsc: | ||
| 424 | #endif | ||
| 425 | |||
| 426 | #ifdef CONFIG_DRM_EXYNOS_ROTATOR | ||
| 427 | platform_driver_unregister(&rotator_driver); | ||
| 428 | out_rotator: | ||
| 429 | #endif | ||
| 430 | |||
| 431 | #ifdef CONFIG_DRM_EXYNOS_FIMC | ||
| 432 | platform_driver_unregister(&fimc_driver); | ||
| 433 | out_fimc: | ||
| 434 | #endif | ||
| 435 | |||
| 365 | #ifdef CONFIG_DRM_EXYNOS_G2D | 436 | #ifdef CONFIG_DRM_EXYNOS_G2D |
| 366 | platform_driver_unregister(&g2d_driver); | 437 | platform_driver_unregister(&g2d_driver); |
| 367 | out_g2d: | 438 | out_g2d: |
| 368 | #endif | 439 | #endif |
| 369 | 440 | ||
| 370 | #ifdef CONFIG_DRM_EXYNOS_VIDI | 441 | #ifdef CONFIG_DRM_EXYNOS_VIDI |
| 371 | out_vidi: | ||
| 372 | platform_driver_unregister(&vidi_driver); | 442 | platform_driver_unregister(&vidi_driver); |
| 443 | out_vidi: | ||
| 373 | #endif | 444 | #endif |
| 374 | 445 | ||
| 375 | #ifdef CONFIG_DRM_EXYNOS_HDMI | 446 | #ifdef CONFIG_DRM_EXYNOS_HDMI |
| 447 | exynos_platform_device_hdmi_unregister(); | ||
| 448 | out_common_hdmi_dev: | ||
| 376 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); | 449 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); |
| 377 | out_common_hdmi: | 450 | out_common_hdmi: |
| 378 | platform_driver_unregister(&mixer_driver); | 451 | platform_driver_unregister(&mixer_driver); |
| @@ -392,13 +465,32 @@ static void __exit exynos_drm_exit(void) | |||
| 392 | { | 465 | { |
| 393 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | 466 | DRM_DEBUG_DRIVER("%s\n", __FILE__); |
| 394 | 467 | ||
| 468 | platform_device_unregister(exynos_drm_pdev); | ||
| 469 | |||
| 395 | platform_driver_unregister(&exynos_drm_platform_driver); | 470 | platform_driver_unregister(&exynos_drm_platform_driver); |
| 396 | 471 | ||
| 472 | #ifdef CONFIG_DRM_EXYNOS_IPP | ||
| 473 | platform_driver_unregister(&ipp_driver); | ||
| 474 | #endif | ||
| 475 | |||
| 476 | #ifdef CONFIG_DRM_EXYNOS_GSC | ||
| 477 | platform_driver_unregister(&gsc_driver); | ||
| 478 | #endif | ||
| 479 | |||
| 480 | #ifdef CONFIG_DRM_EXYNOS_ROTATOR | ||
| 481 | platform_driver_unregister(&rotator_driver); | ||
| 482 | #endif | ||
| 483 | |||
| 484 | #ifdef CONFIG_DRM_EXYNOS_FIMC | ||
| 485 | platform_driver_unregister(&fimc_driver); | ||
| 486 | #endif | ||
| 487 | |||
| 397 | #ifdef CONFIG_DRM_EXYNOS_G2D | 488 | #ifdef CONFIG_DRM_EXYNOS_G2D |
| 398 | platform_driver_unregister(&g2d_driver); | 489 | platform_driver_unregister(&g2d_driver); |
| 399 | #endif | 490 | #endif |
| 400 | 491 | ||
| 401 | #ifdef CONFIG_DRM_EXYNOS_HDMI | 492 | #ifdef CONFIG_DRM_EXYNOS_HDMI |
| 493 | exynos_platform_device_hdmi_unregister(); | ||
| 402 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); | 494 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); |
| 403 | platform_driver_unregister(&mixer_driver); | 495 | platform_driver_unregister(&mixer_driver); |
| 404 | platform_driver_unregister(&hdmi_driver); | 496 | platform_driver_unregister(&hdmi_driver); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 9c9c2dc75828..f5a97745bf93 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
| @@ -74,8 +74,6 @@ enum exynos_drm_output_type { | |||
| 74 | * @commit: apply hardware specific overlay data to registers. | 74 | * @commit: apply hardware specific overlay data to registers. |
| 75 | * @enable: enable hardware specific overlay. | 75 | * @enable: enable hardware specific overlay. |
| 76 | * @disable: disable hardware specific overlay. | 76 | * @disable: disable hardware specific overlay. |
| 77 | * @wait_for_vblank: wait for vblank interrupt to make sure that | ||
| 78 | * hardware overlay is disabled. | ||
| 79 | */ | 77 | */ |
| 80 | struct exynos_drm_overlay_ops { | 78 | struct exynos_drm_overlay_ops { |
| 81 | void (*mode_set)(struct device *subdrv_dev, | 79 | void (*mode_set)(struct device *subdrv_dev, |
| @@ -83,7 +81,6 @@ struct exynos_drm_overlay_ops { | |||
| 83 | void (*commit)(struct device *subdrv_dev, int zpos); | 81 | void (*commit)(struct device *subdrv_dev, int zpos); |
| 84 | void (*enable)(struct device *subdrv_dev, int zpos); | 82 | void (*enable)(struct device *subdrv_dev, int zpos); |
| 85 | void (*disable)(struct device *subdrv_dev, int zpos); | 83 | void (*disable)(struct device *subdrv_dev, int zpos); |
| 86 | void (*wait_for_vblank)(struct device *subdrv_dev); | ||
| 87 | }; | 84 | }; |
| 88 | 85 | ||
| 89 | /* | 86 | /* |
| @@ -110,7 +107,6 @@ struct exynos_drm_overlay_ops { | |||
| 110 | * @pixel_format: fourcc pixel format of this overlay | 107 | * @pixel_format: fourcc pixel format of this overlay |
| 111 | * @dma_addr: array of bus(accessed by dma) address to the memory region | 108 | * @dma_addr: array of bus(accessed by dma) address to the memory region |
| 112 | * allocated for a overlay. | 109 | * allocated for a overlay. |
| 113 | * @vaddr: array of virtual memory addresss to this overlay. | ||
| 114 | * @zpos: order of overlay layer(z position). | 110 | * @zpos: order of overlay layer(z position). |
| 115 | * @default_win: a window to be enabled. | 111 | * @default_win: a window to be enabled. |
| 116 | * @color_key: color key on or off. | 112 | * @color_key: color key on or off. |
| @@ -142,7 +138,6 @@ struct exynos_drm_overlay { | |||
| 142 | unsigned int pitch; | 138 | unsigned int pitch; |
| 143 | uint32_t pixel_format; | 139 | uint32_t pixel_format; |
| 144 | dma_addr_t dma_addr[MAX_FB_BUFFER]; | 140 | dma_addr_t dma_addr[MAX_FB_BUFFER]; |
| 145 | void __iomem *vaddr[MAX_FB_BUFFER]; | ||
| 146 | int zpos; | 141 | int zpos; |
| 147 | 142 | ||
| 148 | bool default_win; | 143 | bool default_win; |
| @@ -186,6 +181,8 @@ struct exynos_drm_display_ops { | |||
| 186 | * @commit: set current hw specific display mode to hw. | 181 | * @commit: set current hw specific display mode to hw. |
| 187 | * @enable_vblank: specific driver callback for enabling vblank interrupt. | 182 | * @enable_vblank: specific driver callback for enabling vblank interrupt. |
| 188 | * @disable_vblank: specific driver callback for disabling vblank interrupt. | 183 | * @disable_vblank: specific driver callback for disabling vblank interrupt. |
| 184 | * @wait_for_vblank: wait for vblank interrupt to make sure that | ||
| 185 | * hardware overlay is updated. | ||
| 189 | */ | 186 | */ |
| 190 | struct exynos_drm_manager_ops { | 187 | struct exynos_drm_manager_ops { |
| 191 | void (*dpms)(struct device *subdrv_dev, int mode); | 188 | void (*dpms)(struct device *subdrv_dev, int mode); |
| @@ -200,6 +197,7 @@ struct exynos_drm_manager_ops { | |||
| 200 | void (*commit)(struct device *subdrv_dev); | 197 | void (*commit)(struct device *subdrv_dev); |
| 201 | int (*enable_vblank)(struct device *subdrv_dev); | 198 | int (*enable_vblank)(struct device *subdrv_dev); |
| 202 | void (*disable_vblank)(struct device *subdrv_dev); | 199 | void (*disable_vblank)(struct device *subdrv_dev); |
| 200 | void (*wait_for_vblank)(struct device *subdrv_dev); | ||
| 203 | }; | 201 | }; |
| 204 | 202 | ||
| 205 | /* | 203 | /* |
| @@ -234,8 +232,14 @@ struct exynos_drm_g2d_private { | |||
| 234 | struct list_head userptr_list; | 232 | struct list_head userptr_list; |
| 235 | }; | 233 | }; |
| 236 | 234 | ||
| 235 | struct exynos_drm_ipp_private { | ||
| 236 | struct device *dev; | ||
| 237 | struct list_head event_list; | ||
| 238 | }; | ||
| 239 | |||
| 237 | struct drm_exynos_file_private { | 240 | struct drm_exynos_file_private { |
| 238 | struct exynos_drm_g2d_private *g2d_priv; | 241 | struct exynos_drm_g2d_private *g2d_priv; |
| 242 | struct exynos_drm_ipp_private *ipp_priv; | ||
| 239 | }; | 243 | }; |
| 240 | 244 | ||
| 241 | /* | 245 | /* |
| @@ -328,10 +332,25 @@ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv); | |||
| 328 | int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file); | 332 | int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file); |
| 329 | void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file); | 333 | void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file); |
| 330 | 334 | ||
| 335 | /* | ||
| 336 | * this function registers exynos drm hdmi platform device. It ensures only one | ||
| 337 | * instance of the device is created. | ||
| 338 | */ | ||
| 339 | extern int exynos_platform_device_hdmi_register(void); | ||
| 340 | |||
| 341 | /* | ||
| 342 | * this function unregisters exynos drm hdmi platform device if it exists. | ||
| 343 | */ | ||
| 344 | void exynos_platform_device_hdmi_unregister(void); | ||
| 345 | |||
| 331 | extern struct platform_driver fimd_driver; | 346 | extern struct platform_driver fimd_driver; |
| 332 | extern struct platform_driver hdmi_driver; | 347 | extern struct platform_driver hdmi_driver; |
| 333 | extern struct platform_driver mixer_driver; | 348 | extern struct platform_driver mixer_driver; |
| 334 | extern struct platform_driver exynos_drm_common_hdmi_driver; | 349 | extern struct platform_driver exynos_drm_common_hdmi_driver; |
| 335 | extern struct platform_driver vidi_driver; | 350 | extern struct platform_driver vidi_driver; |
| 336 | extern struct platform_driver g2d_driver; | 351 | extern struct platform_driver g2d_driver; |
| 352 | extern struct platform_driver fimc_driver; | ||
| 353 | extern struct platform_driver rotator_driver; | ||
| 354 | extern struct platform_driver gsc_driver; | ||
| 355 | extern struct platform_driver ipp_driver; | ||
| 337 | #endif | 356 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index e5001dd85afc..301485215a70 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c | |||
| @@ -237,8 +237,7 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder) | |||
| 237 | void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb) | 237 | void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb) |
| 238 | { | 238 | { |
| 239 | struct exynos_drm_encoder *exynos_encoder; | 239 | struct exynos_drm_encoder *exynos_encoder; |
| 240 | struct exynos_drm_overlay_ops *overlay_ops; | 240 | struct exynos_drm_manager_ops *ops; |
| 241 | struct exynos_drm_manager *manager; | ||
| 242 | struct drm_device *dev = fb->dev; | 241 | struct drm_device *dev = fb->dev; |
| 243 | struct drm_encoder *encoder; | 242 | struct drm_encoder *encoder; |
| 244 | 243 | ||
| @@ -248,21 +247,15 @@ void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb) | |||
| 248 | */ | 247 | */ |
| 249 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 248 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
| 250 | exynos_encoder = to_exynos_encoder(encoder); | 249 | exynos_encoder = to_exynos_encoder(encoder); |
| 251 | 250 | ops = exynos_encoder->manager->ops; | |
| 252 | /* if exynos was disabled, just ignor it. */ | ||
| 253 | if (exynos_encoder->dpms > DRM_MODE_DPMS_ON) | ||
| 254 | continue; | ||
| 255 | |||
| 256 | manager = exynos_encoder->manager; | ||
| 257 | overlay_ops = manager->overlay_ops; | ||
| 258 | 251 | ||
| 259 | /* | 252 | /* |
| 260 | * wait for vblank interrupt | 253 | * wait for vblank interrupt |
| 261 | * - this makes sure that overlay data are updated to | 254 | * - this makes sure that overlay data are updated to |
| 262 | * real hardware. | 255 | * real hardware. |
| 263 | */ | 256 | */ |
| 264 | if (overlay_ops->wait_for_vblank) | 257 | if (ops->wait_for_vblank) |
| 265 | overlay_ops->wait_for_vblank(manager->dev); | 258 | ops->wait_for_vblank(exynos_encoder->manager->dev); |
| 266 | } | 259 | } |
| 267 | } | 260 | } |
| 268 | 261 | ||
| @@ -538,14 +531,4 @@ void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) | |||
| 538 | 531 | ||
| 539 | if (overlay_ops && overlay_ops->disable) | 532 | if (overlay_ops && overlay_ops->disable) |
| 540 | overlay_ops->disable(manager->dev, zpos); | 533 | overlay_ops->disable(manager->dev, zpos); |
| 541 | |||
| 542 | /* | ||
| 543 | * wait for vblank interrupt | ||
| 544 | * - this makes sure that hardware overlay is disabled to avoid | ||
| 545 | * for the dma accesses to memory after gem buffer was released | ||
| 546 | * because the setting for disabling the overlay will be updated | ||
| 547 | * at vsync. | ||
| 548 | */ | ||
| 549 | if (overlay_ops && overlay_ops->wait_for_vblank) | ||
| 550 | overlay_ops->wait_for_vblank(manager->dev); | ||
| 551 | } | 534 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 7413f4b729b0..5426cc5a5e8d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c | |||
| @@ -177,6 +177,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev, | |||
| 177 | return ERR_PTR(-ENOMEM); | 177 | return ERR_PTR(-ENOMEM); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); | ||
| 180 | exynos_fb->exynos_gem_obj[0] = exynos_gem_obj; | 181 | exynos_fb->exynos_gem_obj[0] = exynos_gem_obj; |
| 181 | 182 | ||
| 182 | ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); | 183 | ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); |
| @@ -185,8 +186,6 @@ exynos_drm_framebuffer_init(struct drm_device *dev, | |||
| 185 | return ERR_PTR(ret); | 186 | return ERR_PTR(ret); |
| 186 | } | 187 | } |
| 187 | 188 | ||
| 188 | drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); | ||
| 189 | |||
| 190 | return &exynos_fb->fb; | 189 | return &exynos_fb->fb; |
| 191 | } | 190 | } |
| 192 | 191 | ||
| @@ -232,9 +231,8 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, | |||
| 232 | struct drm_mode_fb_cmd2 *mode_cmd) | 231 | struct drm_mode_fb_cmd2 *mode_cmd) |
| 233 | { | 232 | { |
| 234 | struct drm_gem_object *obj; | 233 | struct drm_gem_object *obj; |
| 235 | struct drm_framebuffer *fb; | ||
| 236 | struct exynos_drm_fb *exynos_fb; | 234 | struct exynos_drm_fb *exynos_fb; |
| 237 | int i; | 235 | int i, ret; |
| 238 | 236 | ||
| 239 | DRM_DEBUG_KMS("%s\n", __FILE__); | 237 | DRM_DEBUG_KMS("%s\n", __FILE__); |
| 240 | 238 | ||
| @@ -244,13 +242,14 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, | |||
| 244 | return ERR_PTR(-ENOENT); | 242 | return ERR_PTR(-ENOENT); |
| 245 | } | 243 | } |
| 246 | 244 | ||
| 247 | fb = exynos_drm_framebuffer_init(dev, mode_cmd, obj); | 245 | exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); |
| 248 | if (IS_ERR(fb)) { | 246 | if (!exynos_fb) { |
| 249 | drm_gem_object_unreference_unlocked(obj); | 247 | DRM_ERROR("failed to allocate exynos drm framebuffer\n"); |
| 250 | return fb; | 248 | return ERR_PTR(-ENOMEM); |
| 251 | } | 249 | } |
| 252 | 250 | ||
| 253 | exynos_fb = to_exynos_fb(fb); | 251 | drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); |
| 252 | exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj); | ||
| 254 | exynos_fb->buf_cnt = exynos_drm_format_num_buffers(mode_cmd); | 253 | exynos_fb->buf_cnt = exynos_drm_format_num_buffers(mode_cmd); |
| 255 | 254 | ||
| 256 | DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt); | 255 | DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt); |
| @@ -263,7 +262,7 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, | |||
| 263 | mode_cmd->handles[i]); | 262 | mode_cmd->handles[i]); |
| 264 | if (!obj) { | 263 | if (!obj) { |
| 265 | DRM_ERROR("failed to lookup gem object\n"); | 264 | DRM_ERROR("failed to lookup gem object\n"); |
| 266 | exynos_drm_fb_destroy(fb); | 265 | kfree(exynos_fb); |
| 267 | return ERR_PTR(-ENOENT); | 266 | return ERR_PTR(-ENOENT); |
| 268 | } | 267 | } |
| 269 | 268 | ||
| @@ -272,14 +271,27 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, | |||
| 272 | ret = check_fb_gem_memory_type(dev, exynos_gem_obj); | 271 | ret = check_fb_gem_memory_type(dev, exynos_gem_obj); |
| 273 | if (ret < 0) { | 272 | if (ret < 0) { |
| 274 | DRM_ERROR("cannot use this gem memory type for fb.\n"); | 273 | DRM_ERROR("cannot use this gem memory type for fb.\n"); |
| 275 | exynos_drm_fb_destroy(fb); | 274 | kfree(exynos_fb); |
| 276 | return ERR_PTR(ret); | 275 | return ERR_PTR(ret); |
| 277 | } | 276 | } |
| 278 | 277 | ||
| 279 | exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj); | 278 | exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj); |
| 280 | } | 279 | } |
| 281 | 280 | ||
| 282 | return fb; | 281 | ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); |
| 282 | if (ret) { | ||
| 283 | for (i = 0; i < exynos_fb->buf_cnt; i++) { | ||
| 284 | struct exynos_drm_gem_obj *gem_obj; | ||
| 285 | |||
| 286 | gem_obj = exynos_fb->exynos_gem_obj[i]; | ||
| 287 | drm_gem_object_unreference_unlocked(&gem_obj->base); | ||
| 288 | } | ||
| 289 | |||
| 290 | kfree(exynos_fb); | ||
| 291 | return ERR_PTR(ret); | ||
| 292 | } | ||
| 293 | |||
| 294 | return &exynos_fb->fb; | ||
| 283 | } | 295 | } |
| 284 | 296 | ||
| 285 | struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, | 297 | struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, |
| @@ -297,9 +309,7 @@ struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, | |||
| 297 | if (!buffer) | 309 | if (!buffer) |
| 298 | return NULL; | 310 | return NULL; |
| 299 | 311 | ||
| 300 | DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n", | 312 | DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr); |
| 301 | (unsigned long)buffer->kvaddr, | ||
| 302 | (unsigned long)buffer->dma_addr); | ||
| 303 | 313 | ||
| 304 | return buffer; | 314 | return buffer; |
| 305 | } | 315 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index a2232792e0c0..f433eb7533a9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c | |||
| @@ -65,7 +65,7 @@ static int exynos_drm_fb_mmap(struct fb_info *info, | |||
| 65 | if (vm_size > buffer->size) | 65 | if (vm_size > buffer->size) |
| 66 | return -EINVAL; | 66 | return -EINVAL; |
| 67 | 67 | ||
| 68 | ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->kvaddr, | 68 | ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages, |
| 69 | buffer->dma_addr, buffer->size, &buffer->dma_attrs); | 69 | buffer->dma_addr, buffer->size, &buffer->dma_attrs); |
| 70 | if (ret < 0) { | 70 | if (ret < 0) { |
| 71 | DRM_ERROR("failed to mmap.\n"); | 71 | DRM_ERROR("failed to mmap.\n"); |
| @@ -109,6 +109,17 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, | |||
| 109 | return -EFAULT; | 109 | return -EFAULT; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | /* map pages with kernel virtual space. */ | ||
| 113 | if (!buffer->kvaddr) { | ||
| 114 | unsigned int nr_pages = buffer->size >> PAGE_SHIFT; | ||
| 115 | buffer->kvaddr = vmap(buffer->pages, nr_pages, VM_MAP, | ||
| 116 | pgprot_writecombine(PAGE_KERNEL)); | ||
| 117 | if (!buffer->kvaddr) { | ||
| 118 | DRM_ERROR("failed to map pages to kernel space.\n"); | ||
| 119 | return -EIO; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 112 | /* buffer count to framebuffer always is 1 at booting time. */ | 123 | /* buffer count to framebuffer always is 1 at booting time. */ |
| 113 | exynos_drm_fb_set_buf_cnt(fb, 1); | 124 | exynos_drm_fb_set_buf_cnt(fb, 1); |
| 114 | 125 | ||
| @@ -164,7 +175,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |||
| 164 | exynos_gem_obj = exynos_drm_gem_create(dev, 0, size); | 175 | exynos_gem_obj = exynos_drm_gem_create(dev, 0, size); |
| 165 | if (IS_ERR(exynos_gem_obj)) { | 176 | if (IS_ERR(exynos_gem_obj)) { |
| 166 | ret = PTR_ERR(exynos_gem_obj); | 177 | ret = PTR_ERR(exynos_gem_obj); |
| 167 | goto out; | 178 | goto err_release_framebuffer; |
| 168 | } | 179 | } |
| 169 | 180 | ||
| 170 | exynos_fbdev->exynos_gem_obj = exynos_gem_obj; | 181 | exynos_fbdev->exynos_gem_obj = exynos_gem_obj; |
| @@ -174,7 +185,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |||
| 174 | if (IS_ERR_OR_NULL(helper->fb)) { | 185 | if (IS_ERR_OR_NULL(helper->fb)) { |
| 175 | DRM_ERROR("failed to create drm framebuffer.\n"); | 186 | DRM_ERROR("failed to create drm framebuffer.\n"); |
| 176 | ret = PTR_ERR(helper->fb); | 187 | ret = PTR_ERR(helper->fb); |
| 177 | goto out; | 188 | goto err_destroy_gem; |
| 178 | } | 189 | } |
| 179 | 190 | ||
| 180 | helper->fbdev = fbi; | 191 | helper->fbdev = fbi; |
| @@ -186,14 +197,24 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |||
| 186 | ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | 197 | ret = fb_alloc_cmap(&fbi->cmap, 256, 0); |
| 187 | if (ret) { | 198 | if (ret) { |
| 188 | DRM_ERROR("failed to allocate cmap.\n"); | 199 | DRM_ERROR("failed to allocate cmap.\n"); |
| 189 | goto out; | 200 | goto err_destroy_framebuffer; |
| 190 | } | 201 | } |
| 191 | 202 | ||
| 192 | ret = exynos_drm_fbdev_update(helper, helper->fb); | 203 | ret = exynos_drm_fbdev_update(helper, helper->fb); |
| 193 | if (ret < 0) { | 204 | if (ret < 0) |
| 194 | fb_dealloc_cmap(&fbi->cmap); | 205 | goto err_dealloc_cmap; |
| 195 | goto out; | 206 | |
| 196 | } | 207 | mutex_unlock(&dev->struct_mutex); |
| 208 | return ret; | ||
| 209 | |||
| 210 | err_dealloc_cmap: | ||
| 211 | fb_dealloc_cmap(&fbi->cmap); | ||
| 212 | err_destroy_framebuffer: | ||
| 213 | drm_framebuffer_cleanup(helper->fb); | ||
| 214 | err_destroy_gem: | ||
| 215 | exynos_drm_gem_destroy(exynos_gem_obj); | ||
| 216 | err_release_framebuffer: | ||
| 217 | framebuffer_release(fbi); | ||
| 197 | 218 | ||
| 198 | /* | 219 | /* |
| 199 | * if failed, all resources allocated above would be released by | 220 | * if failed, all resources allocated above would be released by |
| @@ -295,8 +316,13 @@ err_init: | |||
| 295 | static void exynos_drm_fbdev_destroy(struct drm_device *dev, | 316 | static void exynos_drm_fbdev_destroy(struct drm_device *dev, |
| 296 | struct drm_fb_helper *fb_helper) | 317 | struct drm_fb_helper *fb_helper) |
| 297 | { | 318 | { |
| 319 | struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper); | ||
| 320 | struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; | ||
| 298 | struct drm_framebuffer *fb; | 321 | struct drm_framebuffer *fb; |
| 299 | 322 | ||
| 323 | if (exynos_gem_obj->buffer->kvaddr) | ||
| 324 | vunmap(exynos_gem_obj->buffer->kvaddr); | ||
| 325 | |||
| 300 | /* release drm framebuffer and real buffer */ | 326 | /* release drm framebuffer and real buffer */ |
| 301 | if (fb_helper->fb && fb_helper->fb->funcs) { | 327 | if (fb_helper->fb && fb_helper->fb->funcs) { |
| 302 | fb = fb_helper->fb; | 328 | fb = fb_helper->fb; |
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 000000000000..61ea24296b52 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c | |||
| @@ -0,0 +1,2001 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
| 3 | * Authors: | ||
| 4 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 5 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 6 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/pm_runtime.h> | ||
| 19 | #include <plat/map-base.h> | ||
| 20 | |||
| 21 | #include <drm/drmP.h> | ||
| 22 | #include <drm/exynos_drm.h> | ||
| 23 | #include "regs-fimc.h" | ||
| 24 | #include "exynos_drm_ipp.h" | ||
| 25 | #include "exynos_drm_fimc.h" | ||
| 26 | |||
| 27 | /* | ||
| 28 | * FIMC is stand for Fully Interactive Mobile Camera and | ||
| 29 | * supports image scaler/rotator and input/output DMA operations. | ||
| 30 | * input DMA reads image data from the memory. | ||
| 31 | * output DMA writes image data to memory. | ||
| 32 | * FIMC supports image rotation and image effect functions. | ||
| 33 | * | ||
| 34 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
| 35 | * Memory ----> FIMC H/W ----> Memory. | ||
| 36 | * Writeback operation : supports cloned screen with FIMD. | ||
| 37 | * FIMD ----> FIMC H/W ----> Memory. | ||
| 38 | * Output operation : supports direct display using local path. | ||
| 39 | * Memory ----> FIMC H/W ----> FIMD. | ||
| 40 | */ | ||
| 41 | |||
| 42 | /* | ||
| 43 | * TODO | ||
| 44 | * 1. check suspend/resume api if needed. | ||
| 45 | * 2. need to check use case platform_device_id. | ||
| 46 | * 3. check src/dst size with, height. | ||
| 47 | * 4. added check_prepare api for right register. | ||
| 48 | * 5. need to add supported list in prop_list. | ||
| 49 | * 6. check prescaler/scaler optimization. | ||
| 50 | */ | ||
| 51 | |||
| 52 | #define FIMC_MAX_DEVS 4 | ||
| 53 | #define FIMC_MAX_SRC 2 | ||
| 54 | #define FIMC_MAX_DST 32 | ||
| 55 | #define FIMC_SHFACTOR 10 | ||
| 56 | #define FIMC_BUF_STOP 1 | ||
| 57 | #define FIMC_BUF_START 2 | ||
| 58 | #define FIMC_REG_SZ 32 | ||
| 59 | #define FIMC_WIDTH_ITU_709 1280 | ||
| 60 | #define FIMC_REFRESH_MAX 60 | ||
| 61 | #define FIMC_REFRESH_MIN 12 | ||
| 62 | #define FIMC_CROP_MAX 8192 | ||
| 63 | #define FIMC_CROP_MIN 32 | ||
| 64 | #define FIMC_SCALE_MAX 4224 | ||
| 65 | #define FIMC_SCALE_MIN 32 | ||
| 66 | |||
| 67 | #define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
| 68 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
| 69 | struct fimc_context, ippdrv); | ||
| 70 | #define fimc_read(offset) readl(ctx->regs + (offset)) | ||
| 71 | #define fimc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) | ||
| 72 | |||
| 73 | enum fimc_wb { | ||
| 74 | FIMC_WB_NONE, | ||
| 75 | FIMC_WB_A, | ||
| 76 | FIMC_WB_B, | ||
| 77 | }; | ||
| 78 | |||
| 79 | /* | ||
| 80 | * A structure of scaler. | ||
| 81 | * | ||
| 82 | * @range: narrow, wide. | ||
| 83 | * @bypass: unused scaler path. | ||
| 84 | * @up_h: horizontal scale up. | ||
| 85 | * @up_v: vertical scale up. | ||
| 86 | * @hratio: horizontal ratio. | ||
| 87 | * @vratio: vertical ratio. | ||
| 88 | */ | ||
| 89 | struct fimc_scaler { | ||
| 90 | bool range; | ||
| 91 | bool bypass; | ||
| 92 | bool up_h; | ||
| 93 | bool up_v; | ||
| 94 | u32 hratio; | ||
| 95 | u32 vratio; | ||
| 96 | }; | ||
| 97 | |||
| 98 | /* | ||
| 99 | * A structure of scaler capability. | ||
| 100 | * | ||
| 101 | * find user manual table 43-1. | ||
| 102 | * @in_hori: scaler input horizontal size. | ||
| 103 | * @bypass: scaler bypass mode. | ||
| 104 | * @dst_h_wo_rot: target horizontal size without output rotation. | ||
| 105 | * @dst_h_rot: target horizontal size with output rotation. | ||
| 106 | * @rl_w_wo_rot: real width without input rotation. | ||
| 107 | * @rl_h_rot: real height without output rotation. | ||
| 108 | */ | ||
| 109 | struct fimc_capability { | ||
| 110 | /* scaler */ | ||
| 111 | u32 in_hori; | ||
| 112 | u32 bypass; | ||
| 113 | /* output rotator */ | ||
| 114 | u32 dst_h_wo_rot; | ||
| 115 | u32 dst_h_rot; | ||
| 116 | /* input rotator */ | ||
| 117 | u32 rl_w_wo_rot; | ||
| 118 | u32 rl_h_rot; | ||
| 119 | }; | ||
| 120 | |||
| 121 | /* | ||
| 122 | * A structure of fimc driver data. | ||
| 123 | * | ||
| 124 | * @parent_clk: name of parent clock. | ||
| 125 | */ | ||
| 126 | struct fimc_driverdata { | ||
| 127 | char *parent_clk; | ||
| 128 | }; | ||
| 129 | |||
| 130 | /* | ||
| 131 | * A structure of fimc context. | ||
| 132 | * | ||
| 133 | * @ippdrv: prepare initialization using ippdrv. | ||
| 134 | * @regs_res: register resources. | ||
| 135 | * @regs: memory mapped io registers. | ||
| 136 | * @lock: locking of operations. | ||
| 137 | * @sclk_fimc_clk: fimc source clock. | ||
| 138 | * @fimc_clk: fimc clock. | ||
| 139 | * @wb_clk: writeback a clock. | ||
| 140 | * @wb_b_clk: writeback b clock. | ||
| 141 | * @sc: scaler infomations. | ||
| 142 | * @odr: ordering of YUV. | ||
| 143 | * @ver: fimc version. | ||
| 144 | * @pol: porarity of writeback. | ||
| 145 | * @id: fimc id. | ||
| 146 | * @irq: irq number. | ||
| 147 | * @suspended: qos operations. | ||
| 148 | */ | ||
| 149 | struct fimc_context { | ||
| 150 | struct exynos_drm_ippdrv ippdrv; | ||
| 151 | struct resource *regs_res; | ||
| 152 | void __iomem *regs; | ||
| 153 | struct mutex lock; | ||
| 154 | struct clk *sclk_fimc_clk; | ||
| 155 | struct clk *fimc_clk; | ||
| 156 | struct clk *wb_clk; | ||
| 157 | struct clk *wb_b_clk; | ||
| 158 | struct fimc_scaler sc; | ||
| 159 | struct fimc_driverdata *ddata; | ||
| 160 | struct exynos_drm_ipp_pol pol; | ||
| 161 | int id; | ||
| 162 | int irq; | ||
| 163 | bool suspended; | ||
| 164 | }; | ||
| 165 | |||
| 166 | static void fimc_sw_reset(struct fimc_context *ctx, bool pattern) | ||
| 167 | { | ||
| 168 | u32 cfg; | ||
| 169 | |||
| 170 | DRM_DEBUG_KMS("%s:pattern[%d]\n", __func__, pattern); | ||
| 171 | |||
| 172 | cfg = fimc_read(EXYNOS_CISRCFMT); | ||
| 173 | cfg |= EXYNOS_CISRCFMT_ITU601_8BIT; | ||
| 174 | if (pattern) | ||
| 175 | cfg |= EXYNOS_CIGCTRL_TESTPATTERN_COLOR_BAR; | ||
| 176 | |||
| 177 | fimc_write(cfg, EXYNOS_CISRCFMT); | ||
| 178 | |||
| 179 | /* s/w reset */ | ||
| 180 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 181 | cfg |= (EXYNOS_CIGCTRL_SWRST); | ||
| 182 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 183 | |||
| 184 | /* s/w reset complete */ | ||
| 185 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 186 | cfg &= ~EXYNOS_CIGCTRL_SWRST; | ||
| 187 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 188 | |||
| 189 | /* reset sequence */ | ||
| 190 | fimc_write(0x0, EXYNOS_CIFCNTSEQ); | ||
| 191 | } | ||
| 192 | |||
| 193 | static void fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) | ||
| 194 | { | ||
| 195 | u32 camblk_cfg; | ||
| 196 | |||
| 197 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 198 | |||
| 199 | camblk_cfg = readl(SYSREG_CAMERA_BLK); | ||
| 200 | camblk_cfg &= ~(SYSREG_FIMD0WB_DEST_MASK); | ||
| 201 | camblk_cfg |= ctx->id << (SYSREG_FIMD0WB_DEST_SHIFT); | ||
| 202 | |||
| 203 | writel(camblk_cfg, SYSREG_CAMERA_BLK); | ||
| 204 | } | ||
| 205 | |||
| 206 | static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) | ||
| 207 | { | ||
| 208 | u32 cfg; | ||
| 209 | |||
| 210 | DRM_DEBUG_KMS("%s:wb[%d]\n", __func__, wb); | ||
| 211 | |||
| 212 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 213 | cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | | ||
| 214 | EXYNOS_CIGCTRL_SELCAM_ITU_MASK | | ||
| 215 | EXYNOS_CIGCTRL_SELCAM_MIPI_MASK | | ||
| 216 | EXYNOS_CIGCTRL_SELCAM_FIMC_MASK | | ||
| 217 | EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | | ||
| 218 | EXYNOS_CIGCTRL_SELWRITEBACK_MASK); | ||
| 219 | |||
| 220 | switch (wb) { | ||
| 221 | case FIMC_WB_A: | ||
| 222 | cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A | | ||
| 223 | EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); | ||
| 224 | break; | ||
| 225 | case FIMC_WB_B: | ||
| 226 | cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B | | ||
| 227 | EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); | ||
| 228 | break; | ||
| 229 | case FIMC_WB_NONE: | ||
| 230 | default: | ||
| 231 | cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | | ||
| 232 | EXYNOS_CIGCTRL_SELWRITEBACK_A | | ||
| 233 | EXYNOS_CIGCTRL_SELCAM_MIPI_A | | ||
| 234 | EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); | ||
| 235 | break; | ||
| 236 | } | ||
| 237 | |||
| 238 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 239 | } | ||
| 240 | |||
| 241 | static void fimc_set_polarity(struct fimc_context *ctx, | ||
| 242 | struct exynos_drm_ipp_pol *pol) | ||
| 243 | { | ||
| 244 | u32 cfg; | ||
| 245 | |||
| 246 | DRM_DEBUG_KMS("%s:inv_pclk[%d]inv_vsync[%d]\n", | ||
| 247 | __func__, pol->inv_pclk, pol->inv_vsync); | ||
| 248 | DRM_DEBUG_KMS("%s:inv_href[%d]inv_hsync[%d]\n", | ||
| 249 | __func__, pol->inv_href, pol->inv_hsync); | ||
| 250 | |||
| 251 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 252 | cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | | ||
| 253 | EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); | ||
| 254 | |||
| 255 | if (pol->inv_pclk) | ||
| 256 | cfg |= EXYNOS_CIGCTRL_INVPOLPCLK; | ||
| 257 | if (pol->inv_vsync) | ||
| 258 | cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC; | ||
| 259 | if (pol->inv_href) | ||
| 260 | cfg |= EXYNOS_CIGCTRL_INVPOLHREF; | ||
| 261 | if (pol->inv_hsync) | ||
| 262 | cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; | ||
| 263 | |||
| 264 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 265 | } | ||
| 266 | |||
| 267 | static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) | ||
| 268 | { | ||
| 269 | u32 cfg; | ||
| 270 | |||
| 271 | DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); | ||
| 272 | |||
| 273 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 274 | if (enable) | ||
| 275 | cfg |= EXYNOS_CIGCTRL_CAM_JPEG; | ||
| 276 | else | ||
| 277 | cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG; | ||
| 278 | |||
| 279 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 280 | } | ||
| 281 | |||
| 282 | static void fimc_handle_irq(struct fimc_context *ctx, bool enable, | ||
| 283 | bool overflow, bool level) | ||
| 284 | { | ||
| 285 | u32 cfg; | ||
| 286 | |||
| 287 | DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, | ||
| 288 | enable, overflow, level); | ||
| 289 | |||
| 290 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 291 | if (enable) { | ||
| 292 | cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL); | ||
| 293 | cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE; | ||
| 294 | if (overflow) | ||
| 295 | cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN; | ||
| 296 | if (level) | ||
| 297 | cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL; | ||
| 298 | } else | ||
| 299 | cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE); | ||
| 300 | |||
| 301 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 302 | } | ||
| 303 | |||
| 304 | static void fimc_clear_irq(struct fimc_context *ctx) | ||
| 305 | { | ||
| 306 | u32 cfg; | ||
| 307 | |||
| 308 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 309 | |||
| 310 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 311 | cfg |= EXYNOS_CIGCTRL_IRQ_CLR; | ||
| 312 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 313 | } | ||
| 314 | |||
| 315 | static bool fimc_check_ovf(struct fimc_context *ctx) | ||
| 316 | { | ||
| 317 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 318 | u32 cfg, status, flag; | ||
| 319 | |||
| 320 | status = fimc_read(EXYNOS_CISTATUS); | ||
| 321 | flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | | ||
| 322 | EXYNOS_CISTATUS_OVFICR; | ||
| 323 | |||
| 324 | DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag); | ||
| 325 | |||
| 326 | if (status & flag) { | ||
| 327 | cfg = fimc_read(EXYNOS_CIWDOFST); | ||
| 328 | cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | | ||
| 329 | EXYNOS_CIWDOFST_CLROVFICR); | ||
| 330 | |||
| 331 | fimc_write(cfg, EXYNOS_CIWDOFST); | ||
| 332 | |||
| 333 | cfg = fimc_read(EXYNOS_CIWDOFST); | ||
| 334 | cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | | ||
| 335 | EXYNOS_CIWDOFST_CLROVFICR); | ||
| 336 | |||
| 337 | fimc_write(cfg, EXYNOS_CIWDOFST); | ||
| 338 | |||
| 339 | dev_err(ippdrv->dev, "occured overflow at %d, status 0x%x.\n", | ||
| 340 | ctx->id, status); | ||
| 341 | return true; | ||
| 342 | } | ||
| 343 | |||
| 344 | return false; | ||
| 345 | } | ||
| 346 | |||
| 347 | static bool fimc_check_frame_end(struct fimc_context *ctx) | ||
| 348 | { | ||
| 349 | u32 cfg; | ||
| 350 | |||
| 351 | cfg = fimc_read(EXYNOS_CISTATUS); | ||
| 352 | |||
| 353 | DRM_DEBUG_KMS("%s:cfg[0x%x]\n", __func__, cfg); | ||
| 354 | |||
| 355 | if (!(cfg & EXYNOS_CISTATUS_FRAMEEND)) | ||
| 356 | return false; | ||
| 357 | |||
| 358 | cfg &= ~(EXYNOS_CISTATUS_FRAMEEND); | ||
| 359 | fimc_write(cfg, EXYNOS_CISTATUS); | ||
| 360 | |||
| 361 | return true; | ||
| 362 | } | ||
| 363 | |||
| 364 | static int fimc_get_buf_id(struct fimc_context *ctx) | ||
| 365 | { | ||
| 366 | u32 cfg; | ||
| 367 | int frame_cnt, buf_id; | ||
| 368 | |||
| 369 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 370 | |||
| 371 | cfg = fimc_read(EXYNOS_CISTATUS2); | ||
| 372 | frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); | ||
| 373 | |||
| 374 | if (frame_cnt == 0) | ||
| 375 | frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg); | ||
| 376 | |||
| 377 | DRM_DEBUG_KMS("%s:present[%d]before[%d]\n", __func__, | ||
| 378 | EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg), | ||
| 379 | EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg)); | ||
| 380 | |||
| 381 | if (frame_cnt == 0) { | ||
| 382 | DRM_ERROR("failed to get frame count.\n"); | ||
| 383 | return -EIO; | ||
| 384 | } | ||
| 385 | |||
| 386 | buf_id = frame_cnt - 1; | ||
| 387 | DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); | ||
| 388 | |||
| 389 | return buf_id; | ||
| 390 | } | ||
| 391 | |||
| 392 | static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) | ||
| 393 | { | ||
| 394 | u32 cfg; | ||
| 395 | |||
| 396 | DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); | ||
| 397 | |||
| 398 | cfg = fimc_read(EXYNOS_CIOCTRL); | ||
| 399 | if (enable) | ||
| 400 | cfg |= EXYNOS_CIOCTRL_LASTENDEN; | ||
| 401 | else | ||
| 402 | cfg &= ~EXYNOS_CIOCTRL_LASTENDEN; | ||
| 403 | |||
| 404 | fimc_write(cfg, EXYNOS_CIOCTRL); | ||
| 405 | } | ||
| 406 | |||
| 407 | |||
| 408 | static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) | ||
| 409 | { | ||
| 410 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 411 | u32 cfg; | ||
| 412 | |||
| 413 | DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); | ||
| 414 | |||
| 415 | /* RGB */ | ||
| 416 | cfg = fimc_read(EXYNOS_CISCCTRL); | ||
| 417 | cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK; | ||
| 418 | |||
| 419 | switch (fmt) { | ||
| 420 | case DRM_FORMAT_RGB565: | ||
| 421 | cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; | ||
| 422 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 423 | return 0; | ||
| 424 | case DRM_FORMAT_RGB888: | ||
| 425 | case DRM_FORMAT_XRGB8888: | ||
| 426 | cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; | ||
| 427 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 428 | return 0; | ||
| 429 | default: | ||
| 430 | /* bypass */ | ||
| 431 | break; | ||
| 432 | } | ||
| 433 | |||
| 434 | /* YUV */ | ||
| 435 | cfg = fimc_read(EXYNOS_MSCTRL); | ||
| 436 | cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK | | ||
| 437 | EXYNOS_MSCTRL_C_INT_IN_2PLANE | | ||
| 438 | EXYNOS_MSCTRL_ORDER422_YCBYCR); | ||
| 439 | |||
| 440 | switch (fmt) { | ||
| 441 | case DRM_FORMAT_YUYV: | ||
| 442 | cfg |= EXYNOS_MSCTRL_ORDER422_YCBYCR; | ||
| 443 | break; | ||
| 444 | case DRM_FORMAT_YVYU: | ||
| 445 | cfg |= EXYNOS_MSCTRL_ORDER422_YCRYCB; | ||
| 446 | break; | ||
| 447 | case DRM_FORMAT_UYVY: | ||
| 448 | cfg |= EXYNOS_MSCTRL_ORDER422_CBYCRY; | ||
| 449 | break; | ||
| 450 | case DRM_FORMAT_VYUY: | ||
| 451 | case DRM_FORMAT_YUV444: | ||
| 452 | cfg |= EXYNOS_MSCTRL_ORDER422_CRYCBY; | ||
| 453 | break; | ||
| 454 | case DRM_FORMAT_NV21: | ||
| 455 | case DRM_FORMAT_NV61: | ||
| 456 | cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CRCB | | ||
| 457 | EXYNOS_MSCTRL_C_INT_IN_2PLANE); | ||
| 458 | break; | ||
| 459 | case DRM_FORMAT_YUV422: | ||
| 460 | case DRM_FORMAT_YUV420: | ||
| 461 | case DRM_FORMAT_YVU420: | ||
| 462 | cfg |= EXYNOS_MSCTRL_C_INT_IN_3PLANE; | ||
| 463 | break; | ||
| 464 | case DRM_FORMAT_NV12: | ||
| 465 | case DRM_FORMAT_NV12MT: | ||
| 466 | case DRM_FORMAT_NV16: | ||
| 467 | cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | | ||
| 468 | EXYNOS_MSCTRL_C_INT_IN_2PLANE); | ||
| 469 | break; | ||
| 470 | default: | ||
| 471 | dev_err(ippdrv->dev, "inavlid source yuv order 0x%x.\n", fmt); | ||
| 472 | return -EINVAL; | ||
| 473 | } | ||
| 474 | |||
| 475 | fimc_write(cfg, EXYNOS_MSCTRL); | ||
| 476 | |||
| 477 | return 0; | ||
| 478 | } | ||
| 479 | |||
| 480 | static int fimc_src_set_fmt(struct device *dev, u32 fmt) | ||
| 481 | { | ||
| 482 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 483 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 484 | u32 cfg; | ||
| 485 | |||
| 486 | DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); | ||
| 487 | |||
| 488 | cfg = fimc_read(EXYNOS_MSCTRL); | ||
| 489 | cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; | ||
| 490 | |||
| 491 | switch (fmt) { | ||
| 492 | case DRM_FORMAT_RGB565: | ||
| 493 | case DRM_FORMAT_RGB888: | ||
| 494 | case DRM_FORMAT_XRGB8888: | ||
| 495 | cfg |= EXYNOS_MSCTRL_INFORMAT_RGB; | ||
| 496 | break; | ||
| 497 | case DRM_FORMAT_YUV444: | ||
| 498 | cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; | ||
| 499 | break; | ||
| 500 | case DRM_FORMAT_YUYV: | ||
| 501 | case DRM_FORMAT_YVYU: | ||
| 502 | case DRM_FORMAT_UYVY: | ||
| 503 | case DRM_FORMAT_VYUY: | ||
| 504 | cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE; | ||
| 505 | break; | ||
| 506 | case DRM_FORMAT_NV16: | ||
| 507 | case DRM_FORMAT_NV61: | ||
| 508 | case DRM_FORMAT_YUV422: | ||
| 509 | cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422; | ||
| 510 | break; | ||
| 511 | case DRM_FORMAT_YUV420: | ||
| 512 | case DRM_FORMAT_YVU420: | ||
| 513 | case DRM_FORMAT_NV12: | ||
| 514 | case DRM_FORMAT_NV21: | ||
| 515 | case DRM_FORMAT_NV12MT: | ||
| 516 | cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; | ||
| 517 | break; | ||
| 518 | default: | ||
| 519 | dev_err(ippdrv->dev, "inavlid source format 0x%x.\n", fmt); | ||
| 520 | return -EINVAL; | ||
| 521 | } | ||
| 522 | |||
| 523 | fimc_write(cfg, EXYNOS_MSCTRL); | ||
| 524 | |||
| 525 | cfg = fimc_read(EXYNOS_CIDMAPARAM); | ||
| 526 | cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; | ||
| 527 | |||
| 528 | if (fmt == DRM_FORMAT_NV12MT) | ||
| 529 | cfg |= EXYNOS_CIDMAPARAM_R_MODE_64X32; | ||
| 530 | else | ||
| 531 | cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; | ||
| 532 | |||
| 533 | fimc_write(cfg, EXYNOS_CIDMAPARAM); | ||
| 534 | |||
| 535 | return fimc_src_set_fmt_order(ctx, fmt); | ||
| 536 | } | ||
| 537 | |||
| 538 | static int fimc_src_set_transf(struct device *dev, | ||
| 539 | enum drm_exynos_degree degree, | ||
| 540 | enum drm_exynos_flip flip, bool *swap) | ||
| 541 | { | ||
| 542 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 543 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 544 | u32 cfg1, cfg2; | ||
| 545 | |||
| 546 | DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, | ||
| 547 | degree, flip); | ||
| 548 | |||
| 549 | cfg1 = fimc_read(EXYNOS_MSCTRL); | ||
| 550 | cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | | ||
| 551 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); | ||
| 552 | |||
| 553 | cfg2 = fimc_read(EXYNOS_CITRGFMT); | ||
| 554 | cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; | ||
| 555 | |||
| 556 | switch (degree) { | ||
| 557 | case EXYNOS_DRM_DEGREE_0: | ||
| 558 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 559 | cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; | ||
| 560 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 561 | cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; | ||
| 562 | break; | ||
| 563 | case EXYNOS_DRM_DEGREE_90: | ||
| 564 | cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; | ||
| 565 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 566 | cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; | ||
| 567 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 568 | cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; | ||
| 569 | break; | ||
| 570 | case EXYNOS_DRM_DEGREE_180: | ||
| 571 | cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | | ||
| 572 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); | ||
| 573 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 574 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; | ||
| 575 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 576 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; | ||
| 577 | break; | ||
| 578 | case EXYNOS_DRM_DEGREE_270: | ||
| 579 | cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | | ||
| 580 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); | ||
| 581 | cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; | ||
| 582 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 583 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; | ||
| 584 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 585 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; | ||
| 586 | break; | ||
| 587 | default: | ||
| 588 | dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); | ||
| 589 | return -EINVAL; | ||
| 590 | } | ||
| 591 | |||
| 592 | fimc_write(cfg1, EXYNOS_MSCTRL); | ||
| 593 | fimc_write(cfg2, EXYNOS_CITRGFMT); | ||
| 594 | *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; | ||
| 595 | |||
| 596 | return 0; | ||
| 597 | } | ||
| 598 | |||
| 599 | static int fimc_set_window(struct fimc_context *ctx, | ||
| 600 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | ||
| 601 | { | ||
| 602 | u32 cfg, h1, h2, v1, v2; | ||
| 603 | |||
| 604 | /* cropped image */ | ||
| 605 | h1 = pos->x; | ||
| 606 | h2 = sz->hsize - pos->w - pos->x; | ||
| 607 | v1 = pos->y; | ||
| 608 | v2 = sz->vsize - pos->h - pos->y; | ||
| 609 | |||
| 610 | DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", | ||
| 611 | __func__, pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); | ||
| 612 | DRM_DEBUG_KMS("%s:h1[%d]h2[%d]v1[%d]v2[%d]\n", __func__, | ||
| 613 | h1, h2, v1, v2); | ||
| 614 | |||
| 615 | /* | ||
| 616 | * set window offset 1, 2 size | ||
| 617 | * check figure 43-21 in user manual | ||
| 618 | */ | ||
| 619 | cfg = fimc_read(EXYNOS_CIWDOFST); | ||
| 620 | cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK | | ||
| 621 | EXYNOS_CIWDOFST_WINVEROFST_MASK); | ||
| 622 | cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) | | ||
| 623 | EXYNOS_CIWDOFST_WINVEROFST(v1)); | ||
| 624 | cfg |= EXYNOS_CIWDOFST_WINOFSEN; | ||
| 625 | fimc_write(cfg, EXYNOS_CIWDOFST); | ||
| 626 | |||
| 627 | cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | | ||
| 628 | EXYNOS_CIWDOFST2_WINVEROFST2(v2)); | ||
| 629 | fimc_write(cfg, EXYNOS_CIWDOFST2); | ||
| 630 | |||
| 631 | return 0; | ||
| 632 | } | ||
| 633 | |||
| 634 | static int fimc_src_set_size(struct device *dev, int swap, | ||
| 635 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | ||
| 636 | { | ||
| 637 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 638 | struct drm_exynos_pos img_pos = *pos; | ||
| 639 | struct drm_exynos_sz img_sz = *sz; | ||
| 640 | u32 cfg; | ||
| 641 | |||
| 642 | DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", | ||
| 643 | __func__, swap, sz->hsize, sz->vsize); | ||
| 644 | |||
| 645 | /* original size */ | ||
| 646 | cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | | ||
| 647 | EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); | ||
| 648 | |||
| 649 | fimc_write(cfg, EXYNOS_ORGISIZE); | ||
| 650 | |||
| 651 | DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", __func__, | ||
| 652 | pos->x, pos->y, pos->w, pos->h); | ||
| 653 | |||
| 654 | if (swap) { | ||
| 655 | img_pos.w = pos->h; | ||
| 656 | img_pos.h = pos->w; | ||
| 657 | img_sz.hsize = sz->vsize; | ||
| 658 | img_sz.vsize = sz->hsize; | ||
| 659 | } | ||
| 660 | |||
| 661 | /* set input DMA image size */ | ||
| 662 | cfg = fimc_read(EXYNOS_CIREAL_ISIZE); | ||
| 663 | cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | | ||
| 664 | EXYNOS_CIREAL_ISIZE_WIDTH_MASK); | ||
| 665 | cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) | | ||
| 666 | EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h)); | ||
| 667 | fimc_write(cfg, EXYNOS_CIREAL_ISIZE); | ||
| 668 | |||
| 669 | /* | ||
| 670 | * set input FIFO image size | ||
| 671 | * for now, we support only ITU601 8 bit mode | ||
| 672 | */ | ||
| 673 | cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | | ||
| 674 | EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | | ||
| 675 | EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); | ||
| 676 | fimc_write(cfg, EXYNOS_CISRCFMT); | ||
| 677 | |||
| 678 | /* offset Y(RGB), Cb, Cr */ | ||
| 679 | cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) | | ||
| 680 | EXYNOS_CIIYOFF_VERTICAL(img_pos.y)); | ||
| 681 | fimc_write(cfg, EXYNOS_CIIYOFF); | ||
| 682 | cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) | | ||
| 683 | EXYNOS_CIICBOFF_VERTICAL(img_pos.y)); | ||
| 684 | fimc_write(cfg, EXYNOS_CIICBOFF); | ||
| 685 | cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) | | ||
| 686 | EXYNOS_CIICROFF_VERTICAL(img_pos.y)); | ||
| 687 | fimc_write(cfg, EXYNOS_CIICROFF); | ||
| 688 | |||
| 689 | return fimc_set_window(ctx, &img_pos, &img_sz); | ||
| 690 | } | ||
| 691 | |||
| 692 | static int fimc_src_set_addr(struct device *dev, | ||
| 693 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | ||
| 694 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 695 | { | ||
| 696 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 697 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 698 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 699 | struct drm_exynos_ipp_property *property; | ||
| 700 | struct drm_exynos_ipp_config *config; | ||
| 701 | |||
| 702 | if (!c_node) { | ||
| 703 | DRM_ERROR("failed to get c_node.\n"); | ||
| 704 | return -EINVAL; | ||
| 705 | } | ||
| 706 | |||
| 707 | property = &c_node->property; | ||
| 708 | if (!property) { | ||
| 709 | DRM_ERROR("failed to get property.\n"); | ||
| 710 | return -EINVAL; | ||
| 711 | } | ||
| 712 | |||
| 713 | DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, | ||
| 714 | property->prop_id, buf_id, buf_type); | ||
| 715 | |||
| 716 | if (buf_id > FIMC_MAX_SRC) { | ||
| 717 | dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); | ||
| 718 | return -ENOMEM; | ||
| 719 | } | ||
| 720 | |||
| 721 | /* address register set */ | ||
| 722 | switch (buf_type) { | ||
| 723 | case IPP_BUF_ENQUEUE: | ||
| 724 | config = &property->config[EXYNOS_DRM_OPS_SRC]; | ||
| 725 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], | ||
| 726 | EXYNOS_CIIYSA(buf_id)); | ||
| 727 | |||
| 728 | if (config->fmt == DRM_FORMAT_YVU420) { | ||
| 729 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 730 | EXYNOS_CIICBSA(buf_id)); | ||
| 731 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 732 | EXYNOS_CIICRSA(buf_id)); | ||
| 733 | } else { | ||
| 734 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 735 | EXYNOS_CIICBSA(buf_id)); | ||
| 736 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 737 | EXYNOS_CIICRSA(buf_id)); | ||
| 738 | } | ||
| 739 | break; | ||
| 740 | case IPP_BUF_DEQUEUE: | ||
| 741 | fimc_write(0x0, EXYNOS_CIIYSA(buf_id)); | ||
| 742 | fimc_write(0x0, EXYNOS_CIICBSA(buf_id)); | ||
| 743 | fimc_write(0x0, EXYNOS_CIICRSA(buf_id)); | ||
| 744 | break; | ||
| 745 | default: | ||
| 746 | /* bypass */ | ||
| 747 | break; | ||
| 748 | } | ||
| 749 | |||
| 750 | return 0; | ||
| 751 | } | ||
| 752 | |||
| 753 | static struct exynos_drm_ipp_ops fimc_src_ops = { | ||
| 754 | .set_fmt = fimc_src_set_fmt, | ||
| 755 | .set_transf = fimc_src_set_transf, | ||
| 756 | .set_size = fimc_src_set_size, | ||
| 757 | .set_addr = fimc_src_set_addr, | ||
| 758 | }; | ||
| 759 | |||
| 760 | static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) | ||
| 761 | { | ||
| 762 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 763 | u32 cfg; | ||
| 764 | |||
| 765 | DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); | ||
| 766 | |||
| 767 | /* RGB */ | ||
| 768 | cfg = fimc_read(EXYNOS_CISCCTRL); | ||
| 769 | cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK; | ||
| 770 | |||
| 771 | switch (fmt) { | ||
| 772 | case DRM_FORMAT_RGB565: | ||
| 773 | cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; | ||
| 774 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 775 | return 0; | ||
| 776 | case DRM_FORMAT_RGB888: | ||
| 777 | cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; | ||
| 778 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 779 | return 0; | ||
| 780 | case DRM_FORMAT_XRGB8888: | ||
| 781 | cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | | ||
| 782 | EXYNOS_CISCCTRL_EXTRGB_EXTENSION); | ||
| 783 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 784 | break; | ||
| 785 | default: | ||
| 786 | /* bypass */ | ||
| 787 | break; | ||
| 788 | } | ||
| 789 | |||
| 790 | /* YUV */ | ||
| 791 | cfg = fimc_read(EXYNOS_CIOCTRL); | ||
| 792 | cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK | | ||
| 793 | EXYNOS_CIOCTRL_ORDER422_MASK | | ||
| 794 | EXYNOS_CIOCTRL_YCBCR_PLANE_MASK); | ||
| 795 | |||
| 796 | switch (fmt) { | ||
| 797 | case DRM_FORMAT_XRGB8888: | ||
| 798 | cfg |= EXYNOS_CIOCTRL_ALPHA_OUT; | ||
| 799 | break; | ||
| 800 | case DRM_FORMAT_YUYV: | ||
| 801 | cfg |= EXYNOS_CIOCTRL_ORDER422_YCBYCR; | ||
| 802 | break; | ||
| 803 | case DRM_FORMAT_YVYU: | ||
| 804 | cfg |= EXYNOS_CIOCTRL_ORDER422_YCRYCB; | ||
| 805 | break; | ||
| 806 | case DRM_FORMAT_UYVY: | ||
| 807 | cfg |= EXYNOS_CIOCTRL_ORDER422_CBYCRY; | ||
| 808 | break; | ||
| 809 | case DRM_FORMAT_VYUY: | ||
| 810 | cfg |= EXYNOS_CIOCTRL_ORDER422_CRYCBY; | ||
| 811 | break; | ||
| 812 | case DRM_FORMAT_NV21: | ||
| 813 | case DRM_FORMAT_NV61: | ||
| 814 | cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB; | ||
| 815 | cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; | ||
| 816 | break; | ||
| 817 | case DRM_FORMAT_YUV422: | ||
| 818 | case DRM_FORMAT_YUV420: | ||
| 819 | case DRM_FORMAT_YVU420: | ||
| 820 | cfg |= EXYNOS_CIOCTRL_YCBCR_3PLANE; | ||
| 821 | break; | ||
| 822 | case DRM_FORMAT_NV12: | ||
| 823 | case DRM_FORMAT_NV12MT: | ||
| 824 | case DRM_FORMAT_NV16: | ||
| 825 | cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; | ||
| 826 | cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; | ||
| 827 | break; | ||
| 828 | default: | ||
| 829 | dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); | ||
| 830 | return -EINVAL; | ||
| 831 | } | ||
| 832 | |||
| 833 | fimc_write(cfg, EXYNOS_CIOCTRL); | ||
| 834 | |||
| 835 | return 0; | ||
| 836 | } | ||
| 837 | |||
| 838 | static int fimc_dst_set_fmt(struct device *dev, u32 fmt) | ||
| 839 | { | ||
| 840 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 841 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 842 | u32 cfg; | ||
| 843 | |||
| 844 | DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); | ||
| 845 | |||
| 846 | cfg = fimc_read(EXYNOS_CIEXTEN); | ||
| 847 | |||
| 848 | if (fmt == DRM_FORMAT_AYUV) { | ||
| 849 | cfg |= EXYNOS_CIEXTEN_YUV444_OUT; | ||
| 850 | fimc_write(cfg, EXYNOS_CIEXTEN); | ||
| 851 | } else { | ||
| 852 | cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT; | ||
| 853 | fimc_write(cfg, EXYNOS_CIEXTEN); | ||
| 854 | |||
| 855 | cfg = fimc_read(EXYNOS_CITRGFMT); | ||
| 856 | cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK; | ||
| 857 | |||
| 858 | switch (fmt) { | ||
| 859 | case DRM_FORMAT_RGB565: | ||
| 860 | case DRM_FORMAT_RGB888: | ||
| 861 | case DRM_FORMAT_XRGB8888: | ||
| 862 | cfg |= EXYNOS_CITRGFMT_OUTFORMAT_RGB; | ||
| 863 | break; | ||
| 864 | case DRM_FORMAT_YUYV: | ||
| 865 | case DRM_FORMAT_YVYU: | ||
| 866 | case DRM_FORMAT_UYVY: | ||
| 867 | case DRM_FORMAT_VYUY: | ||
| 868 | cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE; | ||
| 869 | break; | ||
| 870 | case DRM_FORMAT_NV16: | ||
| 871 | case DRM_FORMAT_NV61: | ||
| 872 | case DRM_FORMAT_YUV422: | ||
| 873 | cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422; | ||
| 874 | break; | ||
| 875 | case DRM_FORMAT_YUV420: | ||
| 876 | case DRM_FORMAT_YVU420: | ||
| 877 | case DRM_FORMAT_NV12: | ||
| 878 | case DRM_FORMAT_NV12MT: | ||
| 879 | case DRM_FORMAT_NV21: | ||
| 880 | cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; | ||
| 881 | break; | ||
| 882 | default: | ||
| 883 | dev_err(ippdrv->dev, "inavlid target format 0x%x.\n", | ||
| 884 | fmt); | ||
| 885 | return -EINVAL; | ||
| 886 | } | ||
| 887 | |||
| 888 | fimc_write(cfg, EXYNOS_CITRGFMT); | ||
| 889 | } | ||
| 890 | |||
| 891 | cfg = fimc_read(EXYNOS_CIDMAPARAM); | ||
| 892 | cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; | ||
| 893 | |||
| 894 | if (fmt == DRM_FORMAT_NV12MT) | ||
| 895 | cfg |= EXYNOS_CIDMAPARAM_W_MODE_64X32; | ||
| 896 | else | ||
| 897 | cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; | ||
| 898 | |||
| 899 | fimc_write(cfg, EXYNOS_CIDMAPARAM); | ||
| 900 | |||
| 901 | return fimc_dst_set_fmt_order(ctx, fmt); | ||
| 902 | } | ||
| 903 | |||
| 904 | static int fimc_dst_set_transf(struct device *dev, | ||
| 905 | enum drm_exynos_degree degree, | ||
| 906 | enum drm_exynos_flip flip, bool *swap) | ||
| 907 | { | ||
| 908 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 909 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 910 | u32 cfg; | ||
| 911 | |||
| 912 | DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, | ||
| 913 | degree, flip); | ||
| 914 | |||
| 915 | cfg = fimc_read(EXYNOS_CITRGFMT); | ||
| 916 | cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; | ||
| 917 | cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; | ||
| 918 | |||
| 919 | switch (degree) { | ||
| 920 | case EXYNOS_DRM_DEGREE_0: | ||
| 921 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 922 | cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; | ||
| 923 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 924 | cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | ||
| 925 | break; | ||
| 926 | case EXYNOS_DRM_DEGREE_90: | ||
| 927 | cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; | ||
| 928 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 929 | cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; | ||
| 930 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 931 | cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | ||
| 932 | break; | ||
| 933 | case EXYNOS_DRM_DEGREE_180: | ||
| 934 | cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | | ||
| 935 | EXYNOS_CITRGFMT_FLIP_Y_MIRROR); | ||
| 936 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 937 | cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; | ||
| 938 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 939 | cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | ||
| 940 | break; | ||
| 941 | case EXYNOS_DRM_DEGREE_270: | ||
| 942 | cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | | ||
| 943 | EXYNOS_CITRGFMT_FLIP_X_MIRROR | | ||
| 944 | EXYNOS_CITRGFMT_FLIP_Y_MIRROR); | ||
| 945 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 946 | cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; | ||
| 947 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 948 | cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | ||
| 949 | break; | ||
| 950 | default: | ||
| 951 | dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); | ||
| 952 | return -EINVAL; | ||
| 953 | } | ||
| 954 | |||
| 955 | fimc_write(cfg, EXYNOS_CITRGFMT); | ||
| 956 | *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; | ||
| 957 | |||
| 958 | return 0; | ||
| 959 | } | ||
| 960 | |||
| 961 | static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift) | ||
| 962 | { | ||
| 963 | DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); | ||
| 964 | |||
| 965 | if (src >= dst * 64) { | ||
| 966 | DRM_ERROR("failed to make ratio and shift.\n"); | ||
| 967 | return -EINVAL; | ||
| 968 | } else if (src >= dst * 32) { | ||
| 969 | *ratio = 32; | ||
| 970 | *shift = 5; | ||
| 971 | } else if (src >= dst * 16) { | ||
| 972 | *ratio = 16; | ||
| 973 | *shift = 4; | ||
| 974 | } else if (src >= dst * 8) { | ||
| 975 | *ratio = 8; | ||
| 976 | *shift = 3; | ||
| 977 | } else if (src >= dst * 4) { | ||
| 978 | *ratio = 4; | ||
| 979 | *shift = 2; | ||
| 980 | } else if (src >= dst * 2) { | ||
| 981 | *ratio = 2; | ||
| 982 | *shift = 1; | ||
| 983 | } else { | ||
| 984 | *ratio = 1; | ||
| 985 | *shift = 0; | ||
| 986 | } | ||
| 987 | |||
| 988 | return 0; | ||
| 989 | } | ||
| 990 | |||
| 991 | static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, | ||
| 992 | struct drm_exynos_pos *src, struct drm_exynos_pos *dst) | ||
| 993 | { | ||
| 994 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 995 | u32 cfg, cfg_ext, shfactor; | ||
| 996 | u32 pre_dst_width, pre_dst_height; | ||
| 997 | u32 pre_hratio, hfactor, pre_vratio, vfactor; | ||
| 998 | int ret = 0; | ||
| 999 | u32 src_w, src_h, dst_w, dst_h; | ||
| 1000 | |||
| 1001 | cfg_ext = fimc_read(EXYNOS_CITRGFMT); | ||
| 1002 | if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) { | ||
| 1003 | src_w = src->h; | ||
| 1004 | src_h = src->w; | ||
| 1005 | } else { | ||
| 1006 | src_w = src->w; | ||
| 1007 | src_h = src->h; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | if (cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) { | ||
| 1011 | dst_w = dst->h; | ||
| 1012 | dst_h = dst->w; | ||
| 1013 | } else { | ||
| 1014 | dst_w = dst->w; | ||
| 1015 | dst_h = dst->h; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor); | ||
| 1019 | if (ret) { | ||
| 1020 | dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); | ||
| 1021 | return ret; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | ret = fimc_get_ratio_shift(src_h, dst_h, &pre_vratio, &vfactor); | ||
| 1025 | if (ret) { | ||
| 1026 | dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); | ||
| 1027 | return ret; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | pre_dst_width = src_w / pre_hratio; | ||
| 1031 | pre_dst_height = src_h / pre_vratio; | ||
| 1032 | DRM_DEBUG_KMS("%s:pre_dst_width[%d]pre_dst_height[%d]\n", __func__, | ||
| 1033 | pre_dst_width, pre_dst_height); | ||
| 1034 | DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", | ||
| 1035 | __func__, pre_hratio, hfactor, pre_vratio, vfactor); | ||
| 1036 | |||
| 1037 | sc->hratio = (src_w << 14) / (dst_w << hfactor); | ||
| 1038 | sc->vratio = (src_h << 14) / (dst_h << vfactor); | ||
| 1039 | sc->up_h = (dst_w >= src_w) ? true : false; | ||
| 1040 | sc->up_v = (dst_h >= src_h) ? true : false; | ||
| 1041 | DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", | ||
| 1042 | __func__, sc->hratio, sc->vratio, sc->up_h, sc->up_v); | ||
| 1043 | |||
| 1044 | shfactor = FIMC_SHFACTOR - (hfactor + vfactor); | ||
| 1045 | DRM_DEBUG_KMS("%s:shfactor[%d]\n", __func__, shfactor); | ||
| 1046 | |||
| 1047 | cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | | ||
| 1048 | EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) | | ||
| 1049 | EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio)); | ||
| 1050 | fimc_write(cfg, EXYNOS_CISCPRERATIO); | ||
| 1051 | |||
| 1052 | cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) | | ||
| 1053 | EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height)); | ||
| 1054 | fimc_write(cfg, EXYNOS_CISCPREDST); | ||
| 1055 | |||
| 1056 | return ret; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) | ||
| 1060 | { | ||
| 1061 | u32 cfg, cfg_ext; | ||
| 1062 | |||
| 1063 | DRM_DEBUG_KMS("%s:range[%d]bypass[%d]up_h[%d]up_v[%d]\n", | ||
| 1064 | __func__, sc->range, sc->bypass, sc->up_h, sc->up_v); | ||
| 1065 | DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]\n", | ||
| 1066 | __func__, sc->hratio, sc->vratio); | ||
| 1067 | |||
| 1068 | cfg = fimc_read(EXYNOS_CISCCTRL); | ||
| 1069 | cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | | ||
| 1070 | EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V | | ||
| 1071 | EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK | | ||
| 1072 | EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK | | ||
| 1073 | EXYNOS_CISCCTRL_CSCR2Y_WIDE | | ||
| 1074 | EXYNOS_CISCCTRL_CSCY2R_WIDE); | ||
| 1075 | |||
| 1076 | if (sc->range) | ||
| 1077 | cfg |= (EXYNOS_CISCCTRL_CSCR2Y_WIDE | | ||
| 1078 | EXYNOS_CISCCTRL_CSCY2R_WIDE); | ||
| 1079 | if (sc->bypass) | ||
| 1080 | cfg |= EXYNOS_CISCCTRL_SCALERBYPASS; | ||
| 1081 | if (sc->up_h) | ||
| 1082 | cfg |= EXYNOS_CISCCTRL_SCALEUP_H; | ||
| 1083 | if (sc->up_v) | ||
| 1084 | cfg |= EXYNOS_CISCCTRL_SCALEUP_V; | ||
| 1085 | |||
| 1086 | cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) | | ||
| 1087 | EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6))); | ||
| 1088 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 1089 | |||
| 1090 | cfg_ext = fimc_read(EXYNOS_CIEXTEN); | ||
| 1091 | cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK; | ||
| 1092 | cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK; | ||
| 1093 | cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) | | ||
| 1094 | EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio)); | ||
| 1095 | fimc_write(cfg_ext, EXYNOS_CIEXTEN); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | static int fimc_dst_set_size(struct device *dev, int swap, | ||
| 1099 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | ||
| 1100 | { | ||
| 1101 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1102 | struct drm_exynos_pos img_pos = *pos; | ||
| 1103 | struct drm_exynos_sz img_sz = *sz; | ||
| 1104 | u32 cfg; | ||
| 1105 | |||
| 1106 | DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", | ||
| 1107 | __func__, swap, sz->hsize, sz->vsize); | ||
| 1108 | |||
| 1109 | /* original size */ | ||
| 1110 | cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | | ||
| 1111 | EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); | ||
| 1112 | |||
| 1113 | fimc_write(cfg, EXYNOS_ORGOSIZE); | ||
| 1114 | |||
| 1115 | DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", | ||
| 1116 | __func__, pos->x, pos->y, pos->w, pos->h); | ||
| 1117 | |||
| 1118 | /* CSC ITU */ | ||
| 1119 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 1120 | cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; | ||
| 1121 | |||
| 1122 | if (sz->hsize >= FIMC_WIDTH_ITU_709) | ||
| 1123 | cfg |= EXYNOS_CIGCTRL_CSC_ITU709; | ||
| 1124 | else | ||
| 1125 | cfg |= EXYNOS_CIGCTRL_CSC_ITU601; | ||
| 1126 | |||
| 1127 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 1128 | |||
| 1129 | if (swap) { | ||
| 1130 | img_pos.w = pos->h; | ||
| 1131 | img_pos.h = pos->w; | ||
| 1132 | img_sz.hsize = sz->vsize; | ||
| 1133 | img_sz.vsize = sz->hsize; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | /* target image size */ | ||
| 1137 | cfg = fimc_read(EXYNOS_CITRGFMT); | ||
| 1138 | cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | | ||
| 1139 | EXYNOS_CITRGFMT_TARGETV_MASK); | ||
| 1140 | cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | | ||
| 1141 | EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); | ||
| 1142 | fimc_write(cfg, EXYNOS_CITRGFMT); | ||
| 1143 | |||
| 1144 | /* target area */ | ||
| 1145 | cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); | ||
| 1146 | fimc_write(cfg, EXYNOS_CITAREA); | ||
| 1147 | |||
| 1148 | /* offset Y(RGB), Cb, Cr */ | ||
| 1149 | cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | | ||
| 1150 | EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); | ||
| 1151 | fimc_write(cfg, EXYNOS_CIOYOFF); | ||
| 1152 | cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | | ||
| 1153 | EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); | ||
| 1154 | fimc_write(cfg, EXYNOS_CIOCBOFF); | ||
| 1155 | cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | | ||
| 1156 | EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); | ||
| 1157 | fimc_write(cfg, EXYNOS_CIOCROFF); | ||
| 1158 | |||
| 1159 | return 0; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | static int fimc_dst_get_buf_seq(struct fimc_context *ctx) | ||
| 1163 | { | ||
| 1164 | u32 cfg, i, buf_num = 0; | ||
| 1165 | u32 mask = 0x00000001; | ||
| 1166 | |||
| 1167 | cfg = fimc_read(EXYNOS_CIFCNTSEQ); | ||
| 1168 | |||
| 1169 | for (i = 0; i < FIMC_REG_SZ; i++) | ||
| 1170 | if (cfg & (mask << i)) | ||
| 1171 | buf_num++; | ||
| 1172 | |||
| 1173 | DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); | ||
| 1174 | |||
| 1175 | return buf_num; | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, | ||
| 1179 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 1180 | { | ||
| 1181 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1182 | bool enable; | ||
| 1183 | u32 cfg; | ||
| 1184 | u32 mask = 0x00000001 << buf_id; | ||
| 1185 | int ret = 0; | ||
| 1186 | |||
| 1187 | DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, | ||
| 1188 | buf_id, buf_type); | ||
| 1189 | |||
| 1190 | mutex_lock(&ctx->lock); | ||
| 1191 | |||
| 1192 | /* mask register set */ | ||
| 1193 | cfg = fimc_read(EXYNOS_CIFCNTSEQ); | ||
| 1194 | |||
| 1195 | switch (buf_type) { | ||
| 1196 | case IPP_BUF_ENQUEUE: | ||
| 1197 | enable = true; | ||
| 1198 | break; | ||
| 1199 | case IPP_BUF_DEQUEUE: | ||
| 1200 | enable = false; | ||
| 1201 | break; | ||
| 1202 | default: | ||
| 1203 | dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); | ||
| 1204 | ret = -EINVAL; | ||
| 1205 | goto err_unlock; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | /* sequence id */ | ||
| 1209 | cfg &= (~mask); | ||
| 1210 | cfg |= (enable << buf_id); | ||
| 1211 | fimc_write(cfg, EXYNOS_CIFCNTSEQ); | ||
| 1212 | |||
| 1213 | /* interrupt enable */ | ||
| 1214 | if (buf_type == IPP_BUF_ENQUEUE && | ||
| 1215 | fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START) | ||
| 1216 | fimc_handle_irq(ctx, true, false, true); | ||
| 1217 | |||
| 1218 | /* interrupt disable */ | ||
| 1219 | if (buf_type == IPP_BUF_DEQUEUE && | ||
| 1220 | fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP) | ||
| 1221 | fimc_handle_irq(ctx, false, false, true); | ||
| 1222 | |||
| 1223 | err_unlock: | ||
| 1224 | mutex_unlock(&ctx->lock); | ||
| 1225 | return ret; | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | static int fimc_dst_set_addr(struct device *dev, | ||
| 1229 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | ||
| 1230 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 1231 | { | ||
| 1232 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1233 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1234 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 1235 | struct drm_exynos_ipp_property *property; | ||
| 1236 | struct drm_exynos_ipp_config *config; | ||
| 1237 | |||
| 1238 | if (!c_node) { | ||
| 1239 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1240 | return -EINVAL; | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | property = &c_node->property; | ||
| 1244 | if (!property) { | ||
| 1245 | DRM_ERROR("failed to get property.\n"); | ||
| 1246 | return -EINVAL; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, | ||
| 1250 | property->prop_id, buf_id, buf_type); | ||
| 1251 | |||
| 1252 | if (buf_id > FIMC_MAX_DST) { | ||
| 1253 | dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); | ||
| 1254 | return -ENOMEM; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | /* address register set */ | ||
| 1258 | switch (buf_type) { | ||
| 1259 | case IPP_BUF_ENQUEUE: | ||
| 1260 | config = &property->config[EXYNOS_DRM_OPS_DST]; | ||
| 1261 | |||
| 1262 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], | ||
| 1263 | EXYNOS_CIOYSA(buf_id)); | ||
| 1264 | |||
| 1265 | if (config->fmt == DRM_FORMAT_YVU420) { | ||
| 1266 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 1267 | EXYNOS_CIOCBSA(buf_id)); | ||
| 1268 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 1269 | EXYNOS_CIOCRSA(buf_id)); | ||
| 1270 | } else { | ||
| 1271 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 1272 | EXYNOS_CIOCBSA(buf_id)); | ||
| 1273 | fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 1274 | EXYNOS_CIOCRSA(buf_id)); | ||
| 1275 | } | ||
| 1276 | break; | ||
| 1277 | case IPP_BUF_DEQUEUE: | ||
| 1278 | fimc_write(0x0, EXYNOS_CIOYSA(buf_id)); | ||
| 1279 | fimc_write(0x0, EXYNOS_CIOCBSA(buf_id)); | ||
| 1280 | fimc_write(0x0, EXYNOS_CIOCRSA(buf_id)); | ||
| 1281 | break; | ||
| 1282 | default: | ||
| 1283 | /* bypass */ | ||
| 1284 | break; | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | return fimc_dst_set_buf_seq(ctx, buf_id, buf_type); | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | static struct exynos_drm_ipp_ops fimc_dst_ops = { | ||
| 1291 | .set_fmt = fimc_dst_set_fmt, | ||
| 1292 | .set_transf = fimc_dst_set_transf, | ||
| 1293 | .set_size = fimc_dst_set_size, | ||
| 1294 | .set_addr = fimc_dst_set_addr, | ||
| 1295 | }; | ||
| 1296 | |||
| 1297 | static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) | ||
| 1298 | { | ||
| 1299 | DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); | ||
| 1300 | |||
| 1301 | if (enable) { | ||
| 1302 | clk_enable(ctx->sclk_fimc_clk); | ||
| 1303 | clk_enable(ctx->fimc_clk); | ||
| 1304 | clk_enable(ctx->wb_clk); | ||
| 1305 | ctx->suspended = false; | ||
| 1306 | } else { | ||
| 1307 | clk_disable(ctx->sclk_fimc_clk); | ||
| 1308 | clk_disable(ctx->fimc_clk); | ||
| 1309 | clk_disable(ctx->wb_clk); | ||
| 1310 | ctx->suspended = true; | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | return 0; | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | static irqreturn_t fimc_irq_handler(int irq, void *dev_id) | ||
| 1317 | { | ||
| 1318 | struct fimc_context *ctx = dev_id; | ||
| 1319 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1320 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 1321 | struct drm_exynos_ipp_event_work *event_work = | ||
| 1322 | c_node->event_work; | ||
| 1323 | int buf_id; | ||
| 1324 | |||
| 1325 | DRM_DEBUG_KMS("%s:fimc id[%d]\n", __func__, ctx->id); | ||
| 1326 | |||
| 1327 | fimc_clear_irq(ctx); | ||
| 1328 | if (fimc_check_ovf(ctx)) | ||
| 1329 | return IRQ_NONE; | ||
| 1330 | |||
| 1331 | if (!fimc_check_frame_end(ctx)) | ||
| 1332 | return IRQ_NONE; | ||
| 1333 | |||
| 1334 | buf_id = fimc_get_buf_id(ctx); | ||
| 1335 | if (buf_id < 0) | ||
| 1336 | return IRQ_HANDLED; | ||
| 1337 | |||
| 1338 | DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); | ||
| 1339 | |||
| 1340 | if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) { | ||
| 1341 | DRM_ERROR("failed to dequeue.\n"); | ||
| 1342 | return IRQ_HANDLED; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | event_work->ippdrv = ippdrv; | ||
| 1346 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id; | ||
| 1347 | queue_work(ippdrv->event_workq, (struct work_struct *)event_work); | ||
| 1348 | |||
| 1349 | return IRQ_HANDLED; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | ||
| 1353 | { | ||
| 1354 | struct drm_exynos_ipp_prop_list *prop_list; | ||
| 1355 | |||
| 1356 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1357 | |||
| 1358 | prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); | ||
| 1359 | if (!prop_list) { | ||
| 1360 | DRM_ERROR("failed to alloc property list.\n"); | ||
| 1361 | return -ENOMEM; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | prop_list->version = 1; | ||
| 1365 | prop_list->writeback = 1; | ||
| 1366 | prop_list->refresh_min = FIMC_REFRESH_MIN; | ||
| 1367 | prop_list->refresh_max = FIMC_REFRESH_MAX; | ||
| 1368 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_NONE) | | ||
| 1369 | (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
| 1370 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
| 1371 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
| 1372 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
| 1373 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
| 1374 | (1 << EXYNOS_DRM_DEGREE_270); | ||
| 1375 | prop_list->csc = 1; | ||
| 1376 | prop_list->crop = 1; | ||
| 1377 | prop_list->crop_max.hsize = FIMC_CROP_MAX; | ||
| 1378 | prop_list->crop_max.vsize = FIMC_CROP_MAX; | ||
| 1379 | prop_list->crop_min.hsize = FIMC_CROP_MIN; | ||
| 1380 | prop_list->crop_min.vsize = FIMC_CROP_MIN; | ||
| 1381 | prop_list->scale = 1; | ||
| 1382 | prop_list->scale_max.hsize = FIMC_SCALE_MAX; | ||
| 1383 | prop_list->scale_max.vsize = FIMC_SCALE_MAX; | ||
| 1384 | prop_list->scale_min.hsize = FIMC_SCALE_MIN; | ||
| 1385 | prop_list->scale_min.vsize = FIMC_SCALE_MIN; | ||
| 1386 | |||
| 1387 | ippdrv->prop_list = prop_list; | ||
| 1388 | |||
| 1389 | return 0; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip) | ||
| 1393 | { | ||
| 1394 | switch (flip) { | ||
| 1395 | case EXYNOS_DRM_FLIP_NONE: | ||
| 1396 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 1397 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 1398 | return true; | ||
| 1399 | default: | ||
| 1400 | DRM_DEBUG_KMS("%s:invalid flip\n", __func__); | ||
| 1401 | return false; | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | static int fimc_ippdrv_check_property(struct device *dev, | ||
| 1406 | struct drm_exynos_ipp_property *property) | ||
| 1407 | { | ||
| 1408 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1409 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1410 | struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list; | ||
| 1411 | struct drm_exynos_ipp_config *config; | ||
| 1412 | struct drm_exynos_pos *pos; | ||
| 1413 | struct drm_exynos_sz *sz; | ||
| 1414 | bool swap; | ||
| 1415 | int i; | ||
| 1416 | |||
| 1417 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1418 | |||
| 1419 | for_each_ipp_ops(i) { | ||
| 1420 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1421 | (property->cmd == IPP_CMD_WB)) | ||
| 1422 | continue; | ||
| 1423 | |||
| 1424 | config = &property->config[i]; | ||
| 1425 | pos = &config->pos; | ||
| 1426 | sz = &config->sz; | ||
| 1427 | |||
| 1428 | /* check for flip */ | ||
| 1429 | if (!fimc_check_drm_flip(config->flip)) { | ||
| 1430 | DRM_ERROR("invalid flip.\n"); | ||
| 1431 | goto err_property; | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | /* check for degree */ | ||
| 1435 | switch (config->degree) { | ||
| 1436 | case EXYNOS_DRM_DEGREE_90: | ||
| 1437 | case EXYNOS_DRM_DEGREE_270: | ||
| 1438 | swap = true; | ||
| 1439 | break; | ||
| 1440 | case EXYNOS_DRM_DEGREE_0: | ||
| 1441 | case EXYNOS_DRM_DEGREE_180: | ||
| 1442 | swap = false; | ||
| 1443 | break; | ||
| 1444 | default: | ||
| 1445 | DRM_ERROR("invalid degree.\n"); | ||
| 1446 | goto err_property; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /* check for buffer bound */ | ||
| 1450 | if ((pos->x + pos->w > sz->hsize) || | ||
| 1451 | (pos->y + pos->h > sz->vsize)) { | ||
| 1452 | DRM_ERROR("out of buf bound.\n"); | ||
| 1453 | goto err_property; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | /* check for crop */ | ||
| 1457 | if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { | ||
| 1458 | if (swap) { | ||
| 1459 | if ((pos->h < pp->crop_min.hsize) || | ||
| 1460 | (sz->vsize > pp->crop_max.hsize) || | ||
| 1461 | (pos->w < pp->crop_min.vsize) || | ||
| 1462 | (sz->hsize > pp->crop_max.vsize)) { | ||
| 1463 | DRM_ERROR("out of crop size.\n"); | ||
| 1464 | goto err_property; | ||
| 1465 | } | ||
| 1466 | } else { | ||
| 1467 | if ((pos->w < pp->crop_min.hsize) || | ||
| 1468 | (sz->hsize > pp->crop_max.hsize) || | ||
| 1469 | (pos->h < pp->crop_min.vsize) || | ||
| 1470 | (sz->vsize > pp->crop_max.vsize)) { | ||
| 1471 | DRM_ERROR("out of crop size.\n"); | ||
| 1472 | goto err_property; | ||
| 1473 | } | ||
| 1474 | } | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | /* check for scale */ | ||
| 1478 | if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { | ||
| 1479 | if (swap) { | ||
| 1480 | if ((pos->h < pp->scale_min.hsize) || | ||
| 1481 | (sz->vsize > pp->scale_max.hsize) || | ||
| 1482 | (pos->w < pp->scale_min.vsize) || | ||
| 1483 | (sz->hsize > pp->scale_max.vsize)) { | ||
| 1484 | DRM_ERROR("out of scale size.\n"); | ||
| 1485 | goto err_property; | ||
| 1486 | } | ||
| 1487 | } else { | ||
| 1488 | if ((pos->w < pp->scale_min.hsize) || | ||
| 1489 | (sz->hsize > pp->scale_max.hsize) || | ||
| 1490 | (pos->h < pp->scale_min.vsize) || | ||
| 1491 | (sz->vsize > pp->scale_max.vsize)) { | ||
| 1492 | DRM_ERROR("out of scale size.\n"); | ||
| 1493 | goto err_property; | ||
| 1494 | } | ||
| 1495 | } | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | return 0; | ||
| 1500 | |||
| 1501 | err_property: | ||
| 1502 | for_each_ipp_ops(i) { | ||
| 1503 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1504 | (property->cmd == IPP_CMD_WB)) | ||
| 1505 | continue; | ||
| 1506 | |||
| 1507 | config = &property->config[i]; | ||
| 1508 | pos = &config->pos; | ||
| 1509 | sz = &config->sz; | ||
| 1510 | |||
| 1511 | DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", | ||
| 1512 | i ? "dst" : "src", config->flip, config->degree, | ||
| 1513 | pos->x, pos->y, pos->w, pos->h, | ||
| 1514 | sz->hsize, sz->vsize); | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | return -EINVAL; | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | static void fimc_clear_addr(struct fimc_context *ctx) | ||
| 1521 | { | ||
| 1522 | int i; | ||
| 1523 | |||
| 1524 | DRM_DEBUG_KMS("%s:\n", __func__); | ||
| 1525 | |||
| 1526 | for (i = 0; i < FIMC_MAX_SRC; i++) { | ||
| 1527 | fimc_write(0, EXYNOS_CIIYSA(i)); | ||
| 1528 | fimc_write(0, EXYNOS_CIICBSA(i)); | ||
| 1529 | fimc_write(0, EXYNOS_CIICRSA(i)); | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | for (i = 0; i < FIMC_MAX_DST; i++) { | ||
| 1533 | fimc_write(0, EXYNOS_CIOYSA(i)); | ||
| 1534 | fimc_write(0, EXYNOS_CIOCBSA(i)); | ||
| 1535 | fimc_write(0, EXYNOS_CIOCRSA(i)); | ||
| 1536 | } | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | static int fimc_ippdrv_reset(struct device *dev) | ||
| 1540 | { | ||
| 1541 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1542 | |||
| 1543 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1544 | |||
| 1545 | /* reset h/w block */ | ||
| 1546 | fimc_sw_reset(ctx, false); | ||
| 1547 | |||
| 1548 | /* reset scaler capability */ | ||
| 1549 | memset(&ctx->sc, 0x0, sizeof(ctx->sc)); | ||
| 1550 | |||
| 1551 | fimc_clear_addr(ctx); | ||
| 1552 | |||
| 1553 | return 0; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | ||
| 1557 | { | ||
| 1558 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1559 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1560 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 1561 | struct drm_exynos_ipp_property *property; | ||
| 1562 | struct drm_exynos_ipp_config *config; | ||
| 1563 | struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; | ||
| 1564 | struct drm_exynos_ipp_set_wb set_wb; | ||
| 1565 | int ret, i; | ||
| 1566 | u32 cfg0, cfg1; | ||
| 1567 | |||
| 1568 | DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); | ||
| 1569 | |||
| 1570 | if (!c_node) { | ||
| 1571 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1572 | return -EINVAL; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | property = &c_node->property; | ||
| 1576 | if (!property) { | ||
| 1577 | DRM_ERROR("failed to get property.\n"); | ||
| 1578 | return -EINVAL; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | fimc_handle_irq(ctx, true, false, true); | ||
| 1582 | |||
| 1583 | for_each_ipp_ops(i) { | ||
| 1584 | config = &property->config[i]; | ||
| 1585 | img_pos[i] = config->pos; | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | ret = fimc_set_prescaler(ctx, &ctx->sc, | ||
| 1589 | &img_pos[EXYNOS_DRM_OPS_SRC], | ||
| 1590 | &img_pos[EXYNOS_DRM_OPS_DST]); | ||
| 1591 | if (ret) { | ||
| 1592 | dev_err(dev, "failed to set precalser.\n"); | ||
| 1593 | return ret; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | /* If set ture, we can save jpeg about screen */ | ||
| 1597 | fimc_handle_jpeg(ctx, false); | ||
| 1598 | fimc_set_scaler(ctx, &ctx->sc); | ||
| 1599 | fimc_set_polarity(ctx, &ctx->pol); | ||
| 1600 | |||
| 1601 | switch (cmd) { | ||
| 1602 | case IPP_CMD_M2M: | ||
| 1603 | fimc_set_type_ctrl(ctx, FIMC_WB_NONE); | ||
| 1604 | fimc_handle_lastend(ctx, false); | ||
| 1605 | |||
| 1606 | /* setup dma */ | ||
| 1607 | cfg0 = fimc_read(EXYNOS_MSCTRL); | ||
| 1608 | cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; | ||
| 1609 | cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; | ||
| 1610 | fimc_write(cfg0, EXYNOS_MSCTRL); | ||
| 1611 | break; | ||
| 1612 | case IPP_CMD_WB: | ||
| 1613 | fimc_set_type_ctrl(ctx, FIMC_WB_A); | ||
| 1614 | fimc_handle_lastend(ctx, true); | ||
| 1615 | |||
| 1616 | /* setup FIMD */ | ||
| 1617 | fimc_set_camblk_fimd0_wb(ctx); | ||
| 1618 | |||
| 1619 | set_wb.enable = 1; | ||
| 1620 | set_wb.refresh = property->refresh_rate; | ||
| 1621 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1622 | break; | ||
| 1623 | case IPP_CMD_OUTPUT: | ||
| 1624 | default: | ||
| 1625 | ret = -EINVAL; | ||
| 1626 | dev_err(dev, "invalid operations.\n"); | ||
| 1627 | return ret; | ||
| 1628 | } | ||
| 1629 | |||
| 1630 | /* Reset status */ | ||
| 1631 | fimc_write(0x0, EXYNOS_CISTATUS); | ||
| 1632 | |||
| 1633 | cfg0 = fimc_read(EXYNOS_CIIMGCPT); | ||
| 1634 | cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC; | ||
| 1635 | cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC; | ||
| 1636 | |||
| 1637 | /* Scaler */ | ||
| 1638 | cfg1 = fimc_read(EXYNOS_CISCCTRL); | ||
| 1639 | cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK; | ||
| 1640 | cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE | | ||
| 1641 | EXYNOS_CISCCTRL_SCALERSTART); | ||
| 1642 | |||
| 1643 | fimc_write(cfg1, EXYNOS_CISCCTRL); | ||
| 1644 | |||
| 1645 | /* Enable image capture*/ | ||
| 1646 | cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN; | ||
| 1647 | fimc_write(cfg0, EXYNOS_CIIMGCPT); | ||
| 1648 | |||
| 1649 | /* Disable frame end irq */ | ||
| 1650 | cfg0 = fimc_read(EXYNOS_CIGCTRL); | ||
| 1651 | cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE; | ||
| 1652 | fimc_write(cfg0, EXYNOS_CIGCTRL); | ||
| 1653 | |||
| 1654 | cfg0 = fimc_read(EXYNOS_CIOCTRL); | ||
| 1655 | cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK; | ||
| 1656 | fimc_write(cfg0, EXYNOS_CIOCTRL); | ||
| 1657 | |||
| 1658 | if (cmd == IPP_CMD_M2M) { | ||
| 1659 | cfg0 = fimc_read(EXYNOS_MSCTRL); | ||
| 1660 | cfg0 |= EXYNOS_MSCTRL_ENVID; | ||
| 1661 | fimc_write(cfg0, EXYNOS_MSCTRL); | ||
| 1662 | |||
| 1663 | cfg0 = fimc_read(EXYNOS_MSCTRL); | ||
| 1664 | cfg0 |= EXYNOS_MSCTRL_ENVID; | ||
| 1665 | fimc_write(cfg0, EXYNOS_MSCTRL); | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | return 0; | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) | ||
| 1672 | { | ||
| 1673 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1674 | struct drm_exynos_ipp_set_wb set_wb = {0, 0}; | ||
| 1675 | u32 cfg; | ||
| 1676 | |||
| 1677 | DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); | ||
| 1678 | |||
| 1679 | switch (cmd) { | ||
| 1680 | case IPP_CMD_M2M: | ||
| 1681 | /* Source clear */ | ||
| 1682 | cfg = fimc_read(EXYNOS_MSCTRL); | ||
| 1683 | cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; | ||
| 1684 | cfg &= ~EXYNOS_MSCTRL_ENVID; | ||
| 1685 | fimc_write(cfg, EXYNOS_MSCTRL); | ||
| 1686 | break; | ||
| 1687 | case IPP_CMD_WB: | ||
| 1688 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1689 | break; | ||
| 1690 | case IPP_CMD_OUTPUT: | ||
| 1691 | default: | ||
| 1692 | dev_err(dev, "invalid operations.\n"); | ||
| 1693 | break; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | fimc_handle_irq(ctx, false, false, true); | ||
| 1697 | |||
| 1698 | /* reset sequence */ | ||
| 1699 | fimc_write(0x0, EXYNOS_CIFCNTSEQ); | ||
| 1700 | |||
| 1701 | /* Scaler disable */ | ||
| 1702 | cfg = fimc_read(EXYNOS_CISCCTRL); | ||
| 1703 | cfg &= ~EXYNOS_CISCCTRL_SCALERSTART; | ||
| 1704 | fimc_write(cfg, EXYNOS_CISCCTRL); | ||
| 1705 | |||
| 1706 | /* Disable image capture */ | ||
| 1707 | cfg = fimc_read(EXYNOS_CIIMGCPT); | ||
| 1708 | cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); | ||
| 1709 | fimc_write(cfg, EXYNOS_CIIMGCPT); | ||
| 1710 | |||
| 1711 | /* Enable frame end irq */ | ||
| 1712 | cfg = fimc_read(EXYNOS_CIGCTRL); | ||
| 1713 | cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE; | ||
| 1714 | fimc_write(cfg, EXYNOS_CIGCTRL); | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | static int __devinit fimc_probe(struct platform_device *pdev) | ||
| 1718 | { | ||
| 1719 | struct device *dev = &pdev->dev; | ||
| 1720 | struct fimc_context *ctx; | ||
| 1721 | struct clk *parent_clk; | ||
| 1722 | struct resource *res; | ||
| 1723 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1724 | struct exynos_drm_fimc_pdata *pdata; | ||
| 1725 | struct fimc_driverdata *ddata; | ||
| 1726 | int ret; | ||
| 1727 | |||
| 1728 | pdata = pdev->dev.platform_data; | ||
| 1729 | if (!pdata) { | ||
| 1730 | dev_err(dev, "no platform data specified.\n"); | ||
| 1731 | return -EINVAL; | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | ||
| 1735 | if (!ctx) | ||
| 1736 | return -ENOMEM; | ||
| 1737 | |||
| 1738 | ddata = (struct fimc_driverdata *) | ||
| 1739 | platform_get_device_id(pdev)->driver_data; | ||
| 1740 | |||
| 1741 | /* clock control */ | ||
| 1742 | ctx->sclk_fimc_clk = clk_get(dev, "sclk_fimc"); | ||
| 1743 | if (IS_ERR(ctx->sclk_fimc_clk)) { | ||
| 1744 | dev_err(dev, "failed to get src fimc clock.\n"); | ||
| 1745 | ret = PTR_ERR(ctx->sclk_fimc_clk); | ||
| 1746 | goto err_ctx; | ||
| 1747 | } | ||
| 1748 | clk_enable(ctx->sclk_fimc_clk); | ||
| 1749 | |||
| 1750 | ctx->fimc_clk = clk_get(dev, "fimc"); | ||
| 1751 | if (IS_ERR(ctx->fimc_clk)) { | ||
| 1752 | dev_err(dev, "failed to get fimc clock.\n"); | ||
| 1753 | ret = PTR_ERR(ctx->fimc_clk); | ||
| 1754 | clk_disable(ctx->sclk_fimc_clk); | ||
| 1755 | clk_put(ctx->sclk_fimc_clk); | ||
| 1756 | goto err_ctx; | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | ctx->wb_clk = clk_get(dev, "pxl_async0"); | ||
| 1760 | if (IS_ERR(ctx->wb_clk)) { | ||
| 1761 | dev_err(dev, "failed to get writeback a clock.\n"); | ||
| 1762 | ret = PTR_ERR(ctx->wb_clk); | ||
| 1763 | clk_disable(ctx->sclk_fimc_clk); | ||
| 1764 | clk_put(ctx->sclk_fimc_clk); | ||
| 1765 | clk_put(ctx->fimc_clk); | ||
| 1766 | goto err_ctx; | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | ctx->wb_b_clk = clk_get(dev, "pxl_async1"); | ||
| 1770 | if (IS_ERR(ctx->wb_b_clk)) { | ||
| 1771 | dev_err(dev, "failed to get writeback b clock.\n"); | ||
| 1772 | ret = PTR_ERR(ctx->wb_b_clk); | ||
| 1773 | clk_disable(ctx->sclk_fimc_clk); | ||
| 1774 | clk_put(ctx->sclk_fimc_clk); | ||
| 1775 | clk_put(ctx->fimc_clk); | ||
| 1776 | clk_put(ctx->wb_clk); | ||
| 1777 | goto err_ctx; | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | parent_clk = clk_get(dev, ddata->parent_clk); | ||
| 1781 | |||
| 1782 | if (IS_ERR(parent_clk)) { | ||
| 1783 | dev_err(dev, "failed to get parent clock.\n"); | ||
| 1784 | ret = PTR_ERR(parent_clk); | ||
| 1785 | clk_disable(ctx->sclk_fimc_clk); | ||
| 1786 | clk_put(ctx->sclk_fimc_clk); | ||
| 1787 | clk_put(ctx->fimc_clk); | ||
| 1788 | clk_put(ctx->wb_clk); | ||
| 1789 | clk_put(ctx->wb_b_clk); | ||
| 1790 | goto err_ctx; | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | if (clk_set_parent(ctx->sclk_fimc_clk, parent_clk)) { | ||
| 1794 | dev_err(dev, "failed to set parent.\n"); | ||
| 1795 | ret = -EINVAL; | ||
| 1796 | clk_put(parent_clk); | ||
| 1797 | clk_disable(ctx->sclk_fimc_clk); | ||
| 1798 | clk_put(ctx->sclk_fimc_clk); | ||
| 1799 | clk_put(ctx->fimc_clk); | ||
| 1800 | clk_put(ctx->wb_clk); | ||
| 1801 | clk_put(ctx->wb_b_clk); | ||
| 1802 | goto err_ctx; | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | clk_put(parent_clk); | ||
| 1806 | clk_set_rate(ctx->sclk_fimc_clk, pdata->clk_rate); | ||
| 1807 | |||
| 1808 | /* resource memory */ | ||
| 1809 | ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 1810 | if (!ctx->regs_res) { | ||
| 1811 | dev_err(dev, "failed to find registers.\n"); | ||
| 1812 | ret = -ENOENT; | ||
| 1813 | goto err_clk; | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); | ||
| 1817 | if (!ctx->regs) { | ||
| 1818 | dev_err(dev, "failed to map registers.\n"); | ||
| 1819 | ret = -ENXIO; | ||
| 1820 | goto err_clk; | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | /* resource irq */ | ||
| 1824 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
| 1825 | if (!res) { | ||
| 1826 | dev_err(dev, "failed to request irq resource.\n"); | ||
| 1827 | ret = -ENOENT; | ||
| 1828 | goto err_get_regs; | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | ctx->irq = res->start; | ||
| 1832 | ret = request_threaded_irq(ctx->irq, NULL, fimc_irq_handler, | ||
| 1833 | IRQF_ONESHOT, "drm_fimc", ctx); | ||
| 1834 | if (ret < 0) { | ||
| 1835 | dev_err(dev, "failed to request irq.\n"); | ||
| 1836 | goto err_get_regs; | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | /* context initailization */ | ||
| 1840 | ctx->id = pdev->id; | ||
| 1841 | ctx->pol = pdata->pol; | ||
| 1842 | ctx->ddata = ddata; | ||
| 1843 | |||
| 1844 | ippdrv = &ctx->ippdrv; | ||
| 1845 | ippdrv->dev = dev; | ||
| 1846 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops; | ||
| 1847 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops; | ||
| 1848 | ippdrv->check_property = fimc_ippdrv_check_property; | ||
| 1849 | ippdrv->reset = fimc_ippdrv_reset; | ||
| 1850 | ippdrv->start = fimc_ippdrv_start; | ||
| 1851 | ippdrv->stop = fimc_ippdrv_stop; | ||
| 1852 | ret = fimc_init_prop_list(ippdrv); | ||
| 1853 | if (ret < 0) { | ||
| 1854 | dev_err(dev, "failed to init property list.\n"); | ||
| 1855 | goto err_get_irq; | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, | ||
| 1859 | (int)ippdrv); | ||
| 1860 | |||
| 1861 | mutex_init(&ctx->lock); | ||
| 1862 | platform_set_drvdata(pdev, ctx); | ||
| 1863 | |||
| 1864 | pm_runtime_set_active(dev); | ||
| 1865 | pm_runtime_enable(dev); | ||
| 1866 | |||
| 1867 | ret = exynos_drm_ippdrv_register(ippdrv); | ||
| 1868 | if (ret < 0) { | ||
| 1869 | dev_err(dev, "failed to register drm fimc device.\n"); | ||
| 1870 | goto err_ippdrv_register; | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | dev_info(&pdev->dev, "drm fimc registered successfully.\n"); | ||
| 1874 | |||
| 1875 | return 0; | ||
| 1876 | |||
| 1877 | err_ippdrv_register: | ||
| 1878 | devm_kfree(dev, ippdrv->prop_list); | ||
| 1879 | pm_runtime_disable(dev); | ||
| 1880 | err_get_irq: | ||
| 1881 | free_irq(ctx->irq, ctx); | ||
| 1882 | err_get_regs: | ||
| 1883 | devm_iounmap(dev, ctx->regs); | ||
| 1884 | err_clk: | ||
| 1885 | clk_put(ctx->sclk_fimc_clk); | ||
| 1886 | clk_put(ctx->fimc_clk); | ||
| 1887 | clk_put(ctx->wb_clk); | ||
| 1888 | clk_put(ctx->wb_b_clk); | ||
| 1889 | err_ctx: | ||
| 1890 | devm_kfree(dev, ctx); | ||
| 1891 | return ret; | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | static int __devexit fimc_remove(struct platform_device *pdev) | ||
| 1895 | { | ||
| 1896 | struct device *dev = &pdev->dev; | ||
| 1897 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1898 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1899 | |||
| 1900 | devm_kfree(dev, ippdrv->prop_list); | ||
| 1901 | exynos_drm_ippdrv_unregister(ippdrv); | ||
| 1902 | mutex_destroy(&ctx->lock); | ||
| 1903 | |||
| 1904 | pm_runtime_set_suspended(dev); | ||
| 1905 | pm_runtime_disable(dev); | ||
| 1906 | |||
| 1907 | free_irq(ctx->irq, ctx); | ||
| 1908 | devm_iounmap(dev, ctx->regs); | ||
| 1909 | |||
| 1910 | clk_put(ctx->sclk_fimc_clk); | ||
| 1911 | clk_put(ctx->fimc_clk); | ||
| 1912 | clk_put(ctx->wb_clk); | ||
| 1913 | clk_put(ctx->wb_b_clk); | ||
| 1914 | |||
| 1915 | devm_kfree(dev, ctx); | ||
| 1916 | |||
| 1917 | return 0; | ||
| 1918 | } | ||
| 1919 | |||
| 1920 | #ifdef CONFIG_PM_SLEEP | ||
| 1921 | static int fimc_suspend(struct device *dev) | ||
| 1922 | { | ||
| 1923 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1924 | |||
| 1925 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1926 | |||
| 1927 | if (pm_runtime_suspended(dev)) | ||
| 1928 | return 0; | ||
| 1929 | |||
| 1930 | return fimc_clk_ctrl(ctx, false); | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | static int fimc_resume(struct device *dev) | ||
| 1934 | { | ||
| 1935 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1936 | |||
| 1937 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1938 | |||
| 1939 | if (!pm_runtime_suspended(dev)) | ||
| 1940 | return fimc_clk_ctrl(ctx, true); | ||
| 1941 | |||
| 1942 | return 0; | ||
| 1943 | } | ||
| 1944 | #endif | ||
| 1945 | |||
| 1946 | #ifdef CONFIG_PM_RUNTIME | ||
| 1947 | static int fimc_runtime_suspend(struct device *dev) | ||
| 1948 | { | ||
| 1949 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1950 | |||
| 1951 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1952 | |||
| 1953 | return fimc_clk_ctrl(ctx, false); | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | static int fimc_runtime_resume(struct device *dev) | ||
| 1957 | { | ||
| 1958 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1959 | |||
| 1960 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1961 | |||
| 1962 | return fimc_clk_ctrl(ctx, true); | ||
| 1963 | } | ||
| 1964 | #endif | ||
| 1965 | |||
| 1966 | static struct fimc_driverdata exynos4210_fimc_data = { | ||
| 1967 | .parent_clk = "mout_mpll", | ||
| 1968 | }; | ||
| 1969 | |||
| 1970 | static struct fimc_driverdata exynos4410_fimc_data = { | ||
| 1971 | .parent_clk = "mout_mpll_user", | ||
| 1972 | }; | ||
| 1973 | |||
| 1974 | static struct platform_device_id fimc_driver_ids[] = { | ||
| 1975 | { | ||
| 1976 | .name = "exynos4210-fimc", | ||
| 1977 | .driver_data = (unsigned long)&exynos4210_fimc_data, | ||
| 1978 | }, { | ||
| 1979 | .name = "exynos4412-fimc", | ||
| 1980 | .driver_data = (unsigned long)&exynos4410_fimc_data, | ||
| 1981 | }, | ||
| 1982 | {}, | ||
| 1983 | }; | ||
| 1984 | MODULE_DEVICE_TABLE(platform, fimc_driver_ids); | ||
| 1985 | |||
| 1986 | static const struct dev_pm_ops fimc_pm_ops = { | ||
| 1987 | SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) | ||
| 1988 | SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) | ||
| 1989 | }; | ||
| 1990 | |||
| 1991 | struct platform_driver fimc_driver = { | ||
| 1992 | .probe = fimc_probe, | ||
| 1993 | .remove = __devexit_p(fimc_remove), | ||
| 1994 | .id_table = fimc_driver_ids, | ||
| 1995 | .driver = { | ||
| 1996 | .name = "exynos-drm-fimc", | ||
| 1997 | .owner = THIS_MODULE, | ||
| 1998 | .pm = &fimc_pm_ops, | ||
| 1999 | }, | ||
| 2000 | }; | ||
| 2001 | |||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.h b/drivers/gpu/drm/exynos/exynos_drm_fimc.h new file mode 100644 index 000000000000..dc970fa0d888 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 6 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 7 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 8 | * | ||
| 9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 10 | * copy of this software and associated documentation files (the "Software"), | ||
| 11 | * to deal in the Software without restriction, including without limitation | ||
| 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
| 13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
| 14 | * Software is furnished to do so, subject to the following conditions: | ||
| 15 | * | ||
| 16 | * The above copyright notice and this permission notice (including the next | ||
| 17 | * paragraph) shall be included in all copies or substantial portions of the | ||
| 18 | * Software. | ||
| 19 | * | ||
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
| 24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
| 25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
| 26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #ifndef _EXYNOS_DRM_FIMC_H_ | ||
| 30 | #define _EXYNOS_DRM_FIMC_H_ | ||
| 31 | |||
| 32 | /* | ||
| 33 | * TODO | ||
| 34 | * FIMD output interface notifier callback. | ||
| 35 | */ | ||
| 36 | |||
| 37 | #endif /* _EXYNOS_DRM_FIMC_H_ */ | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 00bd266a31bb..bf0d9baca2bc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
| 18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
| 19 | #include <linux/clk.h> | 19 | #include <linux/clk.h> |
| 20 | #include <linux/of_device.h> | ||
| 20 | #include <linux/pm_runtime.h> | 21 | #include <linux/pm_runtime.h> |
| 21 | 22 | ||
| 22 | #include <video/samsung_fimd.h> | 23 | #include <video/samsung_fimd.h> |
| @@ -79,10 +80,10 @@ struct fimd_win_data { | |||
| 79 | unsigned int fb_height; | 80 | unsigned int fb_height; |
| 80 | unsigned int bpp; | 81 | unsigned int bpp; |
| 81 | dma_addr_t dma_addr; | 82 | dma_addr_t dma_addr; |
| 82 | void __iomem *vaddr; | ||
| 83 | unsigned int buf_offsize; | 83 | unsigned int buf_offsize; |
| 84 | unsigned int line_size; /* bytes */ | 84 | unsigned int line_size; /* bytes */ |
| 85 | bool enabled; | 85 | bool enabled; |
| 86 | bool resume; | ||
| 86 | }; | 87 | }; |
| 87 | 88 | ||
| 88 | struct fimd_context { | 89 | struct fimd_context { |
| @@ -100,13 +101,34 @@ struct fimd_context { | |||
| 100 | u32 vidcon1; | 101 | u32 vidcon1; |
| 101 | bool suspended; | 102 | bool suspended; |
| 102 | struct mutex lock; | 103 | struct mutex lock; |
| 104 | wait_queue_head_t wait_vsync_queue; | ||
| 105 | atomic_t wait_vsync_event; | ||
| 103 | 106 | ||
| 104 | struct exynos_drm_panel_info *panel; | 107 | struct exynos_drm_panel_info *panel; |
| 105 | }; | 108 | }; |
| 106 | 109 | ||
| 110 | #ifdef CONFIG_OF | ||
| 111 | static const struct of_device_id fimd_driver_dt_match[] = { | ||
| 112 | { .compatible = "samsung,exynos4-fimd", | ||
| 113 | .data = &exynos4_fimd_driver_data }, | ||
| 114 | { .compatible = "samsung,exynos5-fimd", | ||
| 115 | .data = &exynos5_fimd_driver_data }, | ||
| 116 | {}, | ||
| 117 | }; | ||
| 118 | MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); | ||
| 119 | #endif | ||
| 120 | |||
| 107 | static inline struct fimd_driver_data *drm_fimd_get_driver_data( | 121 | static inline struct fimd_driver_data *drm_fimd_get_driver_data( |
| 108 | struct platform_device *pdev) | 122 | struct platform_device *pdev) |
| 109 | { | 123 | { |
| 124 | #ifdef CONFIG_OF | ||
| 125 | const struct of_device_id *of_id = | ||
| 126 | of_match_device(fimd_driver_dt_match, &pdev->dev); | ||
| 127 | |||
| 128 | if (of_id) | ||
| 129 | return (struct fimd_driver_data *)of_id->data; | ||
| 130 | #endif | ||
| 131 | |||
| 110 | return (struct fimd_driver_data *) | 132 | return (struct fimd_driver_data *) |
| 111 | platform_get_device_id(pdev)->driver_data; | 133 | platform_get_device_id(pdev)->driver_data; |
| 112 | } | 134 | } |
| @@ -241,7 +263,9 @@ static void fimd_commit(struct device *dev) | |||
| 241 | 263 | ||
| 242 | /* setup horizontal and vertical display size. */ | 264 | /* setup horizontal and vertical display size. */ |
| 243 | val = VIDTCON2_LINEVAL(timing->yres - 1) | | 265 | val = VIDTCON2_LINEVAL(timing->yres - 1) | |
| 244 | VIDTCON2_HOZVAL(timing->xres - 1); | 266 | VIDTCON2_HOZVAL(timing->xres - 1) | |
| 267 | VIDTCON2_LINEVAL_E(timing->yres - 1) | | ||
| 268 | VIDTCON2_HOZVAL_E(timing->xres - 1); | ||
| 245 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); | 269 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); |
| 246 | 270 | ||
| 247 | /* setup clock source, clock divider, enable dma. */ | 271 | /* setup clock source, clock divider, enable dma. */ |
| @@ -308,12 +332,32 @@ static void fimd_disable_vblank(struct device *dev) | |||
| 308 | } | 332 | } |
| 309 | } | 333 | } |
| 310 | 334 | ||
| 335 | static void fimd_wait_for_vblank(struct device *dev) | ||
| 336 | { | ||
| 337 | struct fimd_context *ctx = get_fimd_context(dev); | ||
| 338 | |||
| 339 | if (ctx->suspended) | ||
| 340 | return; | ||
| 341 | |||
| 342 | atomic_set(&ctx->wait_vsync_event, 1); | ||
| 343 | |||
| 344 | /* | ||
| 345 | * wait for FIMD to signal VSYNC interrupt or return after | ||
| 346 | * timeout which is set to 50ms (refresh rate of 20). | ||
| 347 | */ | ||
| 348 | if (!wait_event_timeout(ctx->wait_vsync_queue, | ||
| 349 | !atomic_read(&ctx->wait_vsync_event), | ||
| 350 | DRM_HZ/20)) | ||
| 351 | DRM_DEBUG_KMS("vblank wait timed out.\n"); | ||
| 352 | } | ||
| 353 | |||
| 311 | static struct exynos_drm_manager_ops fimd_manager_ops = { | 354 | static struct exynos_drm_manager_ops fimd_manager_ops = { |
| 312 | .dpms = fimd_dpms, | 355 | .dpms = fimd_dpms, |
| 313 | .apply = fimd_apply, | 356 | .apply = fimd_apply, |
| 314 | .commit = fimd_commit, | 357 | .commit = fimd_commit, |
| 315 | .enable_vblank = fimd_enable_vblank, | 358 | .enable_vblank = fimd_enable_vblank, |
| 316 | .disable_vblank = fimd_disable_vblank, | 359 | .disable_vblank = fimd_disable_vblank, |
| 360 | .wait_for_vblank = fimd_wait_for_vblank, | ||
| 317 | }; | 361 | }; |
| 318 | 362 | ||
| 319 | static void fimd_win_mode_set(struct device *dev, | 363 | static void fimd_win_mode_set(struct device *dev, |
| @@ -352,7 +396,6 @@ static void fimd_win_mode_set(struct device *dev, | |||
| 352 | win_data->fb_width = overlay->fb_width; | 396 | win_data->fb_width = overlay->fb_width; |
| 353 | win_data->fb_height = overlay->fb_height; | 397 | win_data->fb_height = overlay->fb_height; |
| 354 | win_data->dma_addr = overlay->dma_addr[0] + offset; | 398 | win_data->dma_addr = overlay->dma_addr[0] + offset; |
| 355 | win_data->vaddr = overlay->vaddr[0] + offset; | ||
| 356 | win_data->bpp = overlay->bpp; | 399 | win_data->bpp = overlay->bpp; |
| 357 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * | 400 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * |
| 358 | (overlay->bpp >> 3); | 401 | (overlay->bpp >> 3); |
| @@ -362,9 +405,7 @@ static void fimd_win_mode_set(struct device *dev, | |||
| 362 | win_data->offset_x, win_data->offset_y); | 405 | win_data->offset_x, win_data->offset_y); |
| 363 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", | 406 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", |
| 364 | win_data->ovl_width, win_data->ovl_height); | 407 | win_data->ovl_width, win_data->ovl_height); |
| 365 | DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", | 408 | DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); |
| 366 | (unsigned long)win_data->dma_addr, | ||
| 367 | (unsigned long)win_data->vaddr); | ||
| 368 | DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", | 409 | DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", |
| 369 | overlay->fb_width, overlay->crtc_width); | 410 | overlay->fb_width, overlay->crtc_width); |
| 370 | } | 411 | } |
| @@ -452,6 +493,8 @@ static void fimd_win_commit(struct device *dev, int zpos) | |||
| 452 | struct fimd_win_data *win_data; | 493 | struct fimd_win_data *win_data; |
| 453 | int win = zpos; | 494 | int win = zpos; |
| 454 | unsigned long val, alpha, size; | 495 | unsigned long val, alpha, size; |
| 496 | unsigned int last_x; | ||
| 497 | unsigned int last_y; | ||
| 455 | 498 | ||
| 456 | DRM_DEBUG_KMS("%s\n", __FILE__); | 499 | DRM_DEBUG_KMS("%s\n", __FILE__); |
| 457 | 500 | ||
| @@ -497,24 +540,32 @@ static void fimd_win_commit(struct device *dev, int zpos) | |||
| 497 | 540 | ||
| 498 | /* buffer size */ | 541 | /* buffer size */ |
| 499 | val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | | 542 | val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | |
| 500 | VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size); | 543 | VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) | |
| 544 | VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) | | ||
| 545 | VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size); | ||
| 501 | writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); | 546 | writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); |
| 502 | 547 | ||
| 503 | /* OSD position */ | 548 | /* OSD position */ |
| 504 | val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | | 549 | val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | |
| 505 | VIDOSDxA_TOPLEFT_Y(win_data->offset_y); | 550 | VIDOSDxA_TOPLEFT_Y(win_data->offset_y) | |
| 551 | VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) | | ||
| 552 | VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y); | ||
| 506 | writel(val, ctx->regs + VIDOSD_A(win)); | 553 | writel(val, ctx->regs + VIDOSD_A(win)); |
| 507 | 554 | ||
| 508 | val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x + | 555 | last_x = win_data->offset_x + win_data->ovl_width; |
| 509 | win_data->ovl_width - 1) | | 556 | if (last_x) |
| 510 | VIDOSDxB_BOTRIGHT_Y(win_data->offset_y + | 557 | last_x--; |
| 511 | win_data->ovl_height - 1); | 558 | last_y = win_data->offset_y + win_data->ovl_height; |
| 559 | if (last_y) | ||
| 560 | last_y--; | ||
| 561 | |||
| 562 | val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) | | ||
| 563 | VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y); | ||
| 564 | |||
| 512 | writel(val, ctx->regs + VIDOSD_B(win)); | 565 | writel(val, ctx->regs + VIDOSD_B(win)); |
| 513 | 566 | ||
| 514 | DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", | 567 | DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", |
| 515 | win_data->offset_x, win_data->offset_y, | 568 | win_data->offset_x, win_data->offset_y, last_x, last_y); |
| 516 | win_data->offset_x + win_data->ovl_width - 1, | ||
| 517 | win_data->offset_y + win_data->ovl_height - 1); | ||
| 518 | 569 | ||
| 519 | /* hardware window 0 doesn't support alpha channel. */ | 570 | /* hardware window 0 doesn't support alpha channel. */ |
| 520 | if (win != 0) { | 571 | if (win != 0) { |
| @@ -574,6 +625,12 @@ static void fimd_win_disable(struct device *dev, int zpos) | |||
| 574 | 625 | ||
| 575 | win_data = &ctx->win_data[win]; | 626 | win_data = &ctx->win_data[win]; |
| 576 | 627 | ||
| 628 | if (ctx->suspended) { | ||
| 629 | /* do not resume this window*/ | ||
| 630 | win_data->resume = false; | ||
| 631 | return; | ||
| 632 | } | ||
| 633 | |||
| 577 | /* protect windows */ | 634 | /* protect windows */ |
| 578 | val = readl(ctx->regs + SHADOWCON); | 635 | val = readl(ctx->regs + SHADOWCON); |
| 579 | val |= SHADOWCON_WINx_PROTECT(win); | 636 | val |= SHADOWCON_WINx_PROTECT(win); |
| @@ -593,22 +650,10 @@ static void fimd_win_disable(struct device *dev, int zpos) | |||
| 593 | win_data->enabled = false; | 650 | win_data->enabled = false; |
| 594 | } | 651 | } |
| 595 | 652 | ||
| 596 | static void fimd_wait_for_vblank(struct device *dev) | ||
| 597 | { | ||
| 598 | struct fimd_context *ctx = get_fimd_context(dev); | ||
| 599 | int ret; | ||
| 600 | |||
| 601 | ret = wait_for((__raw_readl(ctx->regs + VIDCON1) & | ||
| 602 | VIDCON1_VSTATUS_VSYNC), 50); | ||
| 603 | if (ret < 0) | ||
| 604 | DRM_DEBUG_KMS("vblank wait timed out.\n"); | ||
| 605 | } | ||
| 606 | |||
| 607 | static struct exynos_drm_overlay_ops fimd_overlay_ops = { | 653 | static struct exynos_drm_overlay_ops fimd_overlay_ops = { |
| 608 | .mode_set = fimd_win_mode_set, | 654 | .mode_set = fimd_win_mode_set, |
| 609 | .commit = fimd_win_commit, | 655 | .commit = fimd_win_commit, |
| 610 | .disable = fimd_win_disable, | 656 | .disable = fimd_win_disable, |
| 611 | .wait_for_vblank = fimd_wait_for_vblank, | ||
| 612 | }; | 657 | }; |
| 613 | 658 | ||
| 614 | static struct exynos_drm_manager fimd_manager = { | 659 | static struct exynos_drm_manager fimd_manager = { |
| @@ -667,6 +712,11 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) | |||
| 667 | drm_handle_vblank(drm_dev, manager->pipe); | 712 | drm_handle_vblank(drm_dev, manager->pipe); |
| 668 | fimd_finish_pageflip(drm_dev, manager->pipe); | 713 | fimd_finish_pageflip(drm_dev, manager->pipe); |
| 669 | 714 | ||
| 715 | /* set wait vsync event to zero and wake up queue. */ | ||
| 716 | if (atomic_read(&ctx->wait_vsync_event)) { | ||
| 717 | atomic_set(&ctx->wait_vsync_event, 0); | ||
| 718 | DRM_WAKEUP(&ctx->wait_vsync_queue); | ||
| 719 | } | ||
| 670 | out: | 720 | out: |
| 671 | return IRQ_HANDLED; | 721 | return IRQ_HANDLED; |
| 672 | } | 722 | } |
| @@ -794,11 +844,38 @@ static int fimd_clock(struct fimd_context *ctx, bool enable) | |||
| 794 | return 0; | 844 | return 0; |
| 795 | } | 845 | } |
| 796 | 846 | ||
| 847 | static void fimd_window_suspend(struct device *dev) | ||
| 848 | { | ||
| 849 | struct fimd_context *ctx = get_fimd_context(dev); | ||
| 850 | struct fimd_win_data *win_data; | ||
| 851 | int i; | ||
| 852 | |||
| 853 | for (i = 0; i < WINDOWS_NR; i++) { | ||
| 854 | win_data = &ctx->win_data[i]; | ||
| 855 | win_data->resume = win_data->enabled; | ||
| 856 | fimd_win_disable(dev, i); | ||
| 857 | } | ||
| 858 | fimd_wait_for_vblank(dev); | ||
| 859 | } | ||
| 860 | |||
| 861 | static void fimd_window_resume(struct device *dev) | ||
| 862 | { | ||
| 863 | struct fimd_context *ctx = get_fimd_context(dev); | ||
| 864 | struct fimd_win_data *win_data; | ||
| 865 | int i; | ||
| 866 | |||
| 867 | for (i = 0; i < WINDOWS_NR; i++) { | ||
| 868 | win_data = &ctx->win_data[i]; | ||
| 869 | win_data->enabled = win_data->resume; | ||
| 870 | win_data->resume = false; | ||
| 871 | } | ||
| 872 | } | ||
| 873 | |||
| 797 | static int fimd_activate(struct fimd_context *ctx, bool enable) | 874 | static int fimd_activate(struct fimd_context *ctx, bool enable) |
| 798 | { | 875 | { |
| 876 | struct device *dev = ctx->subdrv.dev; | ||
| 799 | if (enable) { | 877 | if (enable) { |
| 800 | int ret; | 878 | int ret; |
| 801 | struct device *dev = ctx->subdrv.dev; | ||
| 802 | 879 | ||
| 803 | ret = fimd_clock(ctx, true); | 880 | ret = fimd_clock(ctx, true); |
| 804 | if (ret < 0) | 881 | if (ret < 0) |
| @@ -809,7 +886,11 @@ static int fimd_activate(struct fimd_context *ctx, bool enable) | |||
| 809 | /* if vblank was enabled status, enable it again. */ | 886 | /* if vblank was enabled status, enable it again. */ |
| 810 | if (test_and_clear_bit(0, &ctx->irq_flags)) | 887 | if (test_and_clear_bit(0, &ctx->irq_flags)) |
| 811 | fimd_enable_vblank(dev); | 888 | fimd_enable_vblank(dev); |
| 889 | |||
| 890 | fimd_window_resume(dev); | ||
| 812 | } else { | 891 | } else { |
| 892 | fimd_window_suspend(dev); | ||
| 893 | |||
| 813 | fimd_clock(ctx, false); | 894 | fimd_clock(ctx, false); |
| 814 | ctx->suspended = true; | 895 | ctx->suspended = true; |
| 815 | } | 896 | } |
| @@ -885,6 +966,8 @@ static int __devinit fimd_probe(struct platform_device *pdev) | |||
| 885 | ctx->vidcon1 = pdata->vidcon1; | 966 | ctx->vidcon1 = pdata->vidcon1; |
| 886 | ctx->default_win = pdata->default_win; | 967 | ctx->default_win = pdata->default_win; |
| 887 | ctx->panel = panel; | 968 | ctx->panel = panel; |
| 969 | DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue); | ||
| 970 | atomic_set(&ctx->wait_vsync_event, 0); | ||
| 888 | 971 | ||
| 889 | subdrv = &ctx->subdrv; | 972 | subdrv = &ctx->subdrv; |
| 890 | 973 | ||
| @@ -1028,5 +1111,6 @@ struct platform_driver fimd_driver = { | |||
| 1028 | .name = "exynos4-fb", | 1111 | .name = "exynos4-fb", |
| 1029 | .owner = THIS_MODULE, | 1112 | .owner = THIS_MODULE, |
| 1030 | .pm = &fimd_pm_ops, | 1113 | .pm = &fimd_pm_ops, |
| 1114 | .of_match_table = of_match_ptr(fimd_driver_dt_match), | ||
| 1031 | }, | 1115 | }, |
| 1032 | }; | 1116 | }; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 99227246ce82..d48183e7e056 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c | |||
| @@ -400,7 +400,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, | |||
| 400 | if (vm_size > buffer->size) | 400 | if (vm_size > buffer->size) |
| 401 | return -EINVAL; | 401 | return -EINVAL; |
| 402 | 402 | ||
| 403 | ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->kvaddr, | 403 | ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages, |
| 404 | buffer->dma_addr, buffer->size, | 404 | buffer->dma_addr, buffer->size, |
| 405 | &buffer->dma_attrs); | 405 | &buffer->dma_attrs); |
| 406 | if (ret < 0) { | 406 | if (ret < 0) { |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index d3ea106a9a77..f11f2afd5bfc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | * - this address could be physical address without IOMMU and | 40 | * - this address could be physical address without IOMMU and |
| 41 | * device address with IOMMU. | 41 | * device address with IOMMU. |
| 42 | * @write: whether pages will be written to by the caller. | 42 | * @write: whether pages will be written to by the caller. |
| 43 | * @pages: Array of backing pages. | ||
| 43 | * @sgt: sg table to transfer page data. | 44 | * @sgt: sg table to transfer page data. |
| 44 | * @size: size of allocated memory region. | 45 | * @size: size of allocated memory region. |
| 45 | * @pfnmap: indicate whether memory region from userptr is mmaped with | 46 | * @pfnmap: indicate whether memory region from userptr is mmaped with |
| @@ -51,6 +52,7 @@ struct exynos_drm_gem_buf { | |||
| 51 | dma_addr_t dma_addr; | 52 | dma_addr_t dma_addr; |
| 52 | struct dma_attrs dma_attrs; | 53 | struct dma_attrs dma_attrs; |
| 53 | unsigned int write; | 54 | unsigned int write; |
| 55 | struct page **pages; | ||
| 54 | struct sg_table *sgt; | 56 | struct sg_table *sgt; |
| 55 | unsigned long size; | 57 | unsigned long size; |
| 56 | bool pfnmap; | 58 | bool pfnmap; |
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 000000000000..5639353d47b9 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c | |||
| @@ -0,0 +1,1870 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
| 3 | * Authors: | ||
| 4 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 5 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 6 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/pm_runtime.h> | ||
| 19 | #include <plat/map-base.h> | ||
| 20 | |||
| 21 | #include <drm/drmP.h> | ||
| 22 | #include <drm/exynos_drm.h> | ||
| 23 | #include "regs-gsc.h" | ||
| 24 | #include "exynos_drm_ipp.h" | ||
| 25 | #include "exynos_drm_gsc.h" | ||
| 26 | |||
| 27 | /* | ||
| 28 | * GSC is stand for General SCaler and | ||
| 29 | * supports image scaler/rotator and input/output DMA operations. | ||
| 30 | * input DMA reads image data from the memory. | ||
| 31 | * output DMA writes image data to memory. | ||
| 32 | * GSC supports image rotation and image effect functions. | ||
| 33 | * | ||
| 34 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
| 35 | * Memory ----> GSC H/W ----> Memory. | ||
| 36 | * Writeback operation : supports cloned screen with FIMD. | ||
| 37 | * FIMD ----> GSC H/W ----> Memory. | ||
| 38 | * Output operation : supports direct display using local path. | ||
| 39 | * Memory ----> GSC H/W ----> FIMD, Mixer. | ||
| 40 | */ | ||
| 41 | |||
| 42 | /* | ||
| 43 | * TODO | ||
| 44 | * 1. check suspend/resume api if needed. | ||
| 45 | * 2. need to check use case platform_device_id. | ||
| 46 | * 3. check src/dst size with, height. | ||
| 47 | * 4. added check_prepare api for right register. | ||
| 48 | * 5. need to add supported list in prop_list. | ||
| 49 | * 6. check prescaler/scaler optimization. | ||
| 50 | */ | ||
| 51 | |||
| 52 | #define GSC_MAX_DEVS 4 | ||
| 53 | #define GSC_MAX_SRC 4 | ||
| 54 | #define GSC_MAX_DST 16 | ||
| 55 | #define GSC_RESET_TIMEOUT 50 | ||
| 56 | #define GSC_BUF_STOP 1 | ||
| 57 | #define GSC_BUF_START 2 | ||
| 58 | #define GSC_REG_SZ 16 | ||
| 59 | #define GSC_WIDTH_ITU_709 1280 | ||
| 60 | #define GSC_SC_UP_MAX_RATIO 65536 | ||
| 61 | #define GSC_SC_DOWN_RATIO_7_8 74898 | ||
| 62 | #define GSC_SC_DOWN_RATIO_6_8 87381 | ||
| 63 | #define GSC_SC_DOWN_RATIO_5_8 104857 | ||
| 64 | #define GSC_SC_DOWN_RATIO_4_8 131072 | ||
| 65 | #define GSC_SC_DOWN_RATIO_3_8 174762 | ||
| 66 | #define GSC_SC_DOWN_RATIO_2_8 262144 | ||
| 67 | #define GSC_REFRESH_MIN 12 | ||
| 68 | #define GSC_REFRESH_MAX 60 | ||
| 69 | #define GSC_CROP_MAX 8192 | ||
| 70 | #define GSC_CROP_MIN 32 | ||
| 71 | #define GSC_SCALE_MAX 4224 | ||
| 72 | #define GSC_SCALE_MIN 32 | ||
| 73 | #define GSC_COEF_RATIO 7 | ||
| 74 | #define GSC_COEF_PHASE 9 | ||
| 75 | #define GSC_COEF_ATTR 16 | ||
| 76 | #define GSC_COEF_H_8T 8 | ||
| 77 | #define GSC_COEF_V_4T 4 | ||
| 78 | #define GSC_COEF_DEPTH 3 | ||
| 79 | |||
| 80 | #define get_gsc_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
| 81 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
| 82 | struct gsc_context, ippdrv); | ||
| 83 | #define gsc_read(offset) readl(ctx->regs + (offset)) | ||
| 84 | #define gsc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) | ||
| 85 | |||
| 86 | /* | ||
| 87 | * A structure of scaler. | ||
| 88 | * | ||
| 89 | * @range: narrow, wide. | ||
| 90 | * @pre_shfactor: pre sclaer shift factor. | ||
| 91 | * @pre_hratio: horizontal ratio of the prescaler. | ||
| 92 | * @pre_vratio: vertical ratio of the prescaler. | ||
| 93 | * @main_hratio: the main scaler's horizontal ratio. | ||
| 94 | * @main_vratio: the main scaler's vertical ratio. | ||
| 95 | */ | ||
| 96 | struct gsc_scaler { | ||
| 97 | bool range; | ||
| 98 | u32 pre_shfactor; | ||
| 99 | u32 pre_hratio; | ||
| 100 | u32 pre_vratio; | ||
| 101 | unsigned long main_hratio; | ||
| 102 | unsigned long main_vratio; | ||
| 103 | }; | ||
| 104 | |||
| 105 | /* | ||
| 106 | * A structure of scaler capability. | ||
| 107 | * | ||
| 108 | * find user manual 49.2 features. | ||
| 109 | * @tile_w: tile mode or rotation width. | ||
| 110 | * @tile_h: tile mode or rotation height. | ||
| 111 | * @w: other cases width. | ||
| 112 | * @h: other cases height. | ||
| 113 | */ | ||
| 114 | struct gsc_capability { | ||
| 115 | /* tile or rotation */ | ||
| 116 | u32 tile_w; | ||
| 117 | u32 tile_h; | ||
| 118 | /* other cases */ | ||
| 119 | u32 w; | ||
| 120 | u32 h; | ||
| 121 | }; | ||
| 122 | |||
| 123 | /* | ||
| 124 | * A structure of gsc context. | ||
| 125 | * | ||
| 126 | * @ippdrv: prepare initialization using ippdrv. | ||
| 127 | * @regs_res: register resources. | ||
| 128 | * @regs: memory mapped io registers. | ||
| 129 | * @lock: locking of operations. | ||
| 130 | * @gsc_clk: gsc gate clock. | ||
| 131 | * @sc: scaler infomations. | ||
| 132 | * @id: gsc id. | ||
| 133 | * @irq: irq number. | ||
| 134 | * @rotation: supports rotation of src. | ||
| 135 | * @suspended: qos operations. | ||
| 136 | */ | ||
| 137 | struct gsc_context { | ||
| 138 | struct exynos_drm_ippdrv ippdrv; | ||
| 139 | struct resource *regs_res; | ||
| 140 | void __iomem *regs; | ||
| 141 | struct mutex lock; | ||
| 142 | struct clk *gsc_clk; | ||
| 143 | struct gsc_scaler sc; | ||
| 144 | int id; | ||
| 145 | int irq; | ||
| 146 | bool rotation; | ||
| 147 | bool suspended; | ||
| 148 | }; | ||
| 149 | |||
| 150 | /* 8-tap Filter Coefficient */ | ||
| 151 | static const int h_coef_8t[GSC_COEF_RATIO][GSC_COEF_ATTR][GSC_COEF_H_8T] = { | ||
| 152 | { /* Ratio <= 65536 (~8:8) */ | ||
| 153 | { 0, 0, 0, 128, 0, 0, 0, 0 }, | ||
| 154 | { -1, 2, -6, 127, 7, -2, 1, 0 }, | ||
| 155 | { -1, 4, -12, 125, 16, -5, 1, 0 }, | ||
| 156 | { -1, 5, -15, 120, 25, -8, 2, 0 }, | ||
| 157 | { -1, 6, -18, 114, 35, -10, 3, -1 }, | ||
| 158 | { -1, 6, -20, 107, 46, -13, 4, -1 }, | ||
| 159 | { -2, 7, -21, 99, 57, -16, 5, -1 }, | ||
| 160 | { -1, 6, -20, 89, 68, -18, 5, -1 }, | ||
| 161 | { -1, 6, -20, 79, 79, -20, 6, -1 }, | ||
| 162 | { -1, 5, -18, 68, 89, -20, 6, -1 }, | ||
| 163 | { -1, 5, -16, 57, 99, -21, 7, -2 }, | ||
| 164 | { -1, 4, -13, 46, 107, -20, 6, -1 }, | ||
| 165 | { -1, 3, -10, 35, 114, -18, 6, -1 }, | ||
| 166 | { 0, 2, -8, 25, 120, -15, 5, -1 }, | ||
| 167 | { 0, 1, -5, 16, 125, -12, 4, -1 }, | ||
| 168 | { 0, 1, -2, 7, 127, -6, 2, -1 } | ||
| 169 | }, { /* 65536 < Ratio <= 74898 (~8:7) */ | ||
| 170 | { 3, -8, 14, 111, 13, -8, 3, 0 }, | ||
| 171 | { 2, -6, 7, 112, 21, -10, 3, -1 }, | ||
| 172 | { 2, -4, 1, 110, 28, -12, 4, -1 }, | ||
| 173 | { 1, -2, -3, 106, 36, -13, 4, -1 }, | ||
| 174 | { 1, -1, -7, 103, 44, -15, 4, -1 }, | ||
| 175 | { 1, 1, -11, 97, 53, -16, 4, -1 }, | ||
| 176 | { 0, 2, -13, 91, 61, -16, 4, -1 }, | ||
| 177 | { 0, 3, -15, 85, 69, -17, 4, -1 }, | ||
| 178 | { 0, 3, -16, 77, 77, -16, 3, 0 }, | ||
| 179 | { -1, 4, -17, 69, 85, -15, 3, 0 }, | ||
| 180 | { -1, 4, -16, 61, 91, -13, 2, 0 }, | ||
| 181 | { -1, 4, -16, 53, 97, -11, 1, 1 }, | ||
| 182 | { -1, 4, -15, 44, 103, -7, -1, 1 }, | ||
| 183 | { -1, 4, -13, 36, 106, -3, -2, 1 }, | ||
| 184 | { -1, 4, -12, 28, 110, 1, -4, 2 }, | ||
| 185 | { -1, 3, -10, 21, 112, 7, -6, 2 } | ||
| 186 | }, { /* 74898 < Ratio <= 87381 (~8:6) */ | ||
| 187 | { 2, -11, 25, 96, 25, -11, 2, 0 }, | ||
| 188 | { 2, -10, 19, 96, 31, -12, 2, 0 }, | ||
| 189 | { 2, -9, 14, 94, 37, -12, 2, 0 }, | ||
| 190 | { 2, -8, 10, 92, 43, -12, 1, 0 }, | ||
| 191 | { 2, -7, 5, 90, 49, -12, 1, 0 }, | ||
| 192 | { 2, -5, 1, 86, 55, -12, 0, 1 }, | ||
| 193 | { 2, -4, -2, 82, 61, -11, -1, 1 }, | ||
| 194 | { 1, -3, -5, 77, 67, -9, -1, 1 }, | ||
| 195 | { 1, -2, -7, 72, 72, -7, -2, 1 }, | ||
| 196 | { 1, -1, -9, 67, 77, -5, -3, 1 }, | ||
| 197 | { 1, -1, -11, 61, 82, -2, -4, 2 }, | ||
| 198 | { 1, 0, -12, 55, 86, 1, -5, 2 }, | ||
| 199 | { 0, 1, -12, 49, 90, 5, -7, 2 }, | ||
| 200 | { 0, 1, -12, 43, 92, 10, -8, 2 }, | ||
| 201 | { 0, 2, -12, 37, 94, 14, -9, 2 }, | ||
| 202 | { 0, 2, -12, 31, 96, 19, -10, 2 } | ||
| 203 | }, { /* 87381 < Ratio <= 104857 (~8:5) */ | ||
| 204 | { -1, -8, 33, 80, 33, -8, -1, 0 }, | ||
| 205 | { -1, -8, 28, 80, 37, -7, -2, 1 }, | ||
| 206 | { 0, -8, 24, 79, 41, -7, -2, 1 }, | ||
| 207 | { 0, -8, 20, 78, 46, -6, -3, 1 }, | ||
| 208 | { 0, -8, 16, 76, 50, -4, -3, 1 }, | ||
| 209 | { 0, -7, 13, 74, 54, -3, -4, 1 }, | ||
| 210 | { 1, -7, 10, 71, 58, -1, -5, 1 }, | ||
| 211 | { 1, -6, 6, 68, 62, 1, -5, 1 }, | ||
| 212 | { 1, -6, 4, 65, 65, 4, -6, 1 }, | ||
| 213 | { 1, -5, 1, 62, 68, 6, -6, 1 }, | ||
| 214 | { 1, -5, -1, 58, 71, 10, -7, 1 }, | ||
| 215 | { 1, -4, -3, 54, 74, 13, -7, 0 }, | ||
| 216 | { 1, -3, -4, 50, 76, 16, -8, 0 }, | ||
| 217 | { 1, -3, -6, 46, 78, 20, -8, 0 }, | ||
| 218 | { 1, -2, -7, 41, 79, 24, -8, 0 }, | ||
| 219 | { 1, -2, -7, 37, 80, 28, -8, -1 } | ||
| 220 | }, { /* 104857 < Ratio <= 131072 (~8:4) */ | ||
| 221 | { -3, 0, 35, 64, 35, 0, -3, 0 }, | ||
| 222 | { -3, -1, 32, 64, 38, 1, -3, 0 }, | ||
| 223 | { -2, -2, 29, 63, 41, 2, -3, 0 }, | ||
| 224 | { -2, -3, 27, 63, 43, 4, -4, 0 }, | ||
| 225 | { -2, -3, 24, 61, 46, 6, -4, 0 }, | ||
| 226 | { -2, -3, 21, 60, 49, 7, -4, 0 }, | ||
| 227 | { -1, -4, 19, 59, 51, 9, -4, -1 }, | ||
| 228 | { -1, -4, 16, 57, 53, 12, -4, -1 }, | ||
| 229 | { -1, -4, 14, 55, 55, 14, -4, -1 }, | ||
| 230 | { -1, -4, 12, 53, 57, 16, -4, -1 }, | ||
| 231 | { -1, -4, 9, 51, 59, 19, -4, -1 }, | ||
| 232 | { 0, -4, 7, 49, 60, 21, -3, -2 }, | ||
| 233 | { 0, -4, 6, 46, 61, 24, -3, -2 }, | ||
| 234 | { 0, -4, 4, 43, 63, 27, -3, -2 }, | ||
| 235 | { 0, -3, 2, 41, 63, 29, -2, -2 }, | ||
| 236 | { 0, -3, 1, 38, 64, 32, -1, -3 } | ||
| 237 | }, { /* 131072 < Ratio <= 174762 (~8:3) */ | ||
| 238 | { -1, 8, 33, 48, 33, 8, -1, 0 }, | ||
| 239 | { -1, 7, 31, 49, 35, 9, -1, -1 }, | ||
| 240 | { -1, 6, 30, 49, 36, 10, -1, -1 }, | ||
| 241 | { -1, 5, 28, 48, 38, 12, -1, -1 }, | ||
| 242 | { -1, 4, 26, 48, 39, 13, 0, -1 }, | ||
| 243 | { -1, 3, 24, 47, 41, 15, 0, -1 }, | ||
| 244 | { -1, 2, 23, 47, 42, 16, 0, -1 }, | ||
| 245 | { -1, 2, 21, 45, 43, 18, 1, -1 }, | ||
| 246 | { -1, 1, 19, 45, 45, 19, 1, -1 }, | ||
| 247 | { -1, 1, 18, 43, 45, 21, 2, -1 }, | ||
| 248 | { -1, 0, 16, 42, 47, 23, 2, -1 }, | ||
| 249 | { -1, 0, 15, 41, 47, 24, 3, -1 }, | ||
| 250 | { -1, 0, 13, 39, 48, 26, 4, -1 }, | ||
| 251 | { -1, -1, 12, 38, 48, 28, 5, -1 }, | ||
| 252 | { -1, -1, 10, 36, 49, 30, 6, -1 }, | ||
| 253 | { -1, -1, 9, 35, 49, 31, 7, -1 } | ||
| 254 | }, { /* 174762 < Ratio <= 262144 (~8:2) */ | ||
| 255 | { 2, 13, 30, 38, 30, 13, 2, 0 }, | ||
| 256 | { 2, 12, 29, 38, 30, 14, 3, 0 }, | ||
| 257 | { 2, 11, 28, 38, 31, 15, 3, 0 }, | ||
| 258 | { 2, 10, 26, 38, 32, 16, 4, 0 }, | ||
| 259 | { 1, 10, 26, 37, 33, 17, 4, 0 }, | ||
| 260 | { 1, 9, 24, 37, 34, 18, 5, 0 }, | ||
| 261 | { 1, 8, 24, 37, 34, 19, 5, 0 }, | ||
| 262 | { 1, 7, 22, 36, 35, 20, 6, 1 }, | ||
| 263 | { 1, 6, 21, 36, 36, 21, 6, 1 }, | ||
| 264 | { 1, 6, 20, 35, 36, 22, 7, 1 }, | ||
| 265 | { 0, 5, 19, 34, 37, 24, 8, 1 }, | ||
| 266 | { 0, 5, 18, 34, 37, 24, 9, 1 }, | ||
| 267 | { 0, 4, 17, 33, 37, 26, 10, 1 }, | ||
| 268 | { 0, 4, 16, 32, 38, 26, 10, 2 }, | ||
| 269 | { 0, 3, 15, 31, 38, 28, 11, 2 }, | ||
| 270 | { 0, 3, 14, 30, 38, 29, 12, 2 } | ||
| 271 | } | ||
| 272 | }; | ||
| 273 | |||
| 274 | /* 4-tap Filter Coefficient */ | ||
| 275 | static const int v_coef_4t[GSC_COEF_RATIO][GSC_COEF_ATTR][GSC_COEF_V_4T] = { | ||
| 276 | { /* Ratio <= 65536 (~8:8) */ | ||
| 277 | { 0, 128, 0, 0 }, | ||
| 278 | { -4, 127, 5, 0 }, | ||
| 279 | { -6, 124, 11, -1 }, | ||
| 280 | { -8, 118, 19, -1 }, | ||
| 281 | { -8, 111, 27, -2 }, | ||
| 282 | { -8, 102, 37, -3 }, | ||
| 283 | { -8, 92, 48, -4 }, | ||
| 284 | { -7, 81, 59, -5 }, | ||
| 285 | { -6, 70, 70, -6 }, | ||
| 286 | { -5, 59, 81, -7 }, | ||
| 287 | { -4, 48, 92, -8 }, | ||
| 288 | { -3, 37, 102, -8 }, | ||
| 289 | { -2, 27, 111, -8 }, | ||
| 290 | { -1, 19, 118, -8 }, | ||
| 291 | { -1, 11, 124, -6 }, | ||
| 292 | { 0, 5, 127, -4 } | ||
| 293 | }, { /* 65536 < Ratio <= 74898 (~8:7) */ | ||
| 294 | { 8, 112, 8, 0 }, | ||
| 295 | { 4, 111, 14, -1 }, | ||
| 296 | { 1, 109, 20, -2 }, | ||
| 297 | { -2, 105, 27, -2 }, | ||
| 298 | { -3, 100, 34, -3 }, | ||
| 299 | { -5, 93, 43, -3 }, | ||
| 300 | { -5, 86, 51, -4 }, | ||
| 301 | { -5, 77, 60, -4 }, | ||
| 302 | { -5, 69, 69, -5 }, | ||
| 303 | { -4, 60, 77, -5 }, | ||
| 304 | { -4, 51, 86, -5 }, | ||
| 305 | { -3, 43, 93, -5 }, | ||
| 306 | { -3, 34, 100, -3 }, | ||
| 307 | { -2, 27, 105, -2 }, | ||
| 308 | { -2, 20, 109, 1 }, | ||
| 309 | { -1, 14, 111, 4 } | ||
| 310 | }, { /* 74898 < Ratio <= 87381 (~8:6) */ | ||
| 311 | { 16, 96, 16, 0 }, | ||
| 312 | { 12, 97, 21, -2 }, | ||
| 313 | { 8, 96, 26, -2 }, | ||
| 314 | { 5, 93, 32, -2 }, | ||
| 315 | { 2, 89, 39, -2 }, | ||
| 316 | { 0, 84, 46, -2 }, | ||
| 317 | { -1, 79, 53, -3 }, | ||
| 318 | { -2, 73, 59, -2 }, | ||
| 319 | { -2, 66, 66, -2 }, | ||
| 320 | { -2, 59, 73, -2 }, | ||
| 321 | { -3, 53, 79, -1 }, | ||
| 322 | { -2, 46, 84, 0 }, | ||
| 323 | { -2, 39, 89, 2 }, | ||
| 324 | { -2, 32, 93, 5 }, | ||
| 325 | { -2, 26, 96, 8 }, | ||
| 326 | { -2, 21, 97, 12 } | ||
| 327 | }, { /* 87381 < Ratio <= 104857 (~8:5) */ | ||
| 328 | { 22, 84, 22, 0 }, | ||
| 329 | { 18, 85, 26, -1 }, | ||
| 330 | { 14, 84, 31, -1 }, | ||
| 331 | { 11, 82, 36, -1 }, | ||
| 332 | { 8, 79, 42, -1 }, | ||
| 333 | { 6, 76, 47, -1 }, | ||
| 334 | { 4, 72, 52, 0 }, | ||
| 335 | { 2, 68, 58, 0 }, | ||
| 336 | { 1, 63, 63, 1 }, | ||
| 337 | { 0, 58, 68, 2 }, | ||
| 338 | { 0, 52, 72, 4 }, | ||
| 339 | { -1, 47, 76, 6 }, | ||
| 340 | { -1, 42, 79, 8 }, | ||
| 341 | { -1, 36, 82, 11 }, | ||
| 342 | { -1, 31, 84, 14 }, | ||
| 343 | { -1, 26, 85, 18 } | ||
| 344 | }, { /* 104857 < Ratio <= 131072 (~8:4) */ | ||
| 345 | { 26, 76, 26, 0 }, | ||
| 346 | { 22, 76, 30, 0 }, | ||
| 347 | { 19, 75, 34, 0 }, | ||
| 348 | { 16, 73, 38, 1 }, | ||
| 349 | { 13, 71, 43, 1 }, | ||
| 350 | { 10, 69, 47, 2 }, | ||
| 351 | { 8, 66, 51, 3 }, | ||
| 352 | { 6, 63, 55, 4 }, | ||
| 353 | { 5, 59, 59, 5 }, | ||
| 354 | { 4, 55, 63, 6 }, | ||
| 355 | { 3, 51, 66, 8 }, | ||
| 356 | { 2, 47, 69, 10 }, | ||
| 357 | { 1, 43, 71, 13 }, | ||
| 358 | { 1, 38, 73, 16 }, | ||
| 359 | { 0, 34, 75, 19 }, | ||
| 360 | { 0, 30, 76, 22 } | ||
| 361 | }, { /* 131072 < Ratio <= 174762 (~8:3) */ | ||
| 362 | { 29, 70, 29, 0 }, | ||
| 363 | { 26, 68, 32, 2 }, | ||
| 364 | { 23, 67, 36, 2 }, | ||
| 365 | { 20, 66, 39, 3 }, | ||
| 366 | { 17, 65, 43, 3 }, | ||
| 367 | { 15, 63, 46, 4 }, | ||
| 368 | { 12, 61, 50, 5 }, | ||
| 369 | { 10, 58, 53, 7 }, | ||
| 370 | { 8, 56, 56, 8 }, | ||
| 371 | { 7, 53, 58, 10 }, | ||
| 372 | { 5, 50, 61, 12 }, | ||
| 373 | { 4, 46, 63, 15 }, | ||
| 374 | { 3, 43, 65, 17 }, | ||
| 375 | { 3, 39, 66, 20 }, | ||
| 376 | { 2, 36, 67, 23 }, | ||
| 377 | { 2, 32, 68, 26 } | ||
| 378 | }, { /* 174762 < Ratio <= 262144 (~8:2) */ | ||
| 379 | { 32, 64, 32, 0 }, | ||
| 380 | { 28, 63, 34, 3 }, | ||
| 381 | { 25, 62, 37, 4 }, | ||
| 382 | { 22, 62, 40, 4 }, | ||
| 383 | { 19, 61, 43, 5 }, | ||
| 384 | { 17, 59, 46, 6 }, | ||
| 385 | { 15, 58, 48, 7 }, | ||
| 386 | { 13, 55, 51, 9 }, | ||
| 387 | { 11, 53, 53, 11 }, | ||
| 388 | { 9, 51, 55, 13 }, | ||
| 389 | { 7, 48, 58, 15 }, | ||
| 390 | { 6, 46, 59, 17 }, | ||
| 391 | { 5, 43, 61, 19 }, | ||
| 392 | { 4, 40, 62, 22 }, | ||
| 393 | { 4, 37, 62, 25 }, | ||
| 394 | { 3, 34, 63, 28 } | ||
| 395 | } | ||
| 396 | }; | ||
| 397 | |||
| 398 | static int gsc_sw_reset(struct gsc_context *ctx) | ||
| 399 | { | ||
| 400 | u32 cfg; | ||
| 401 | int count = GSC_RESET_TIMEOUT; | ||
| 402 | |||
| 403 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 404 | |||
| 405 | /* s/w reset */ | ||
| 406 | cfg = (GSC_SW_RESET_SRESET); | ||
| 407 | gsc_write(cfg, GSC_SW_RESET); | ||
| 408 | |||
| 409 | /* wait s/w reset complete */ | ||
| 410 | while (count--) { | ||
| 411 | cfg = gsc_read(GSC_SW_RESET); | ||
| 412 | if (!cfg) | ||
| 413 | break; | ||
| 414 | usleep_range(1000, 2000); | ||
| 415 | } | ||
| 416 | |||
| 417 | if (cfg) { | ||
| 418 | DRM_ERROR("failed to reset gsc h/w.\n"); | ||
| 419 | return -EBUSY; | ||
| 420 | } | ||
| 421 | |||
| 422 | /* reset sequence */ | ||
| 423 | cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); | ||
| 424 | cfg |= (GSC_IN_BASE_ADDR_MASK | | ||
| 425 | GSC_IN_BASE_ADDR_PINGPONG(0)); | ||
| 426 | gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK); | ||
| 427 | gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK); | ||
| 428 | gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK); | ||
| 429 | |||
| 430 | cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 431 | cfg |= (GSC_OUT_BASE_ADDR_MASK | | ||
| 432 | GSC_OUT_BASE_ADDR_PINGPONG(0)); | ||
| 433 | gsc_write(cfg, GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 434 | gsc_write(cfg, GSC_OUT_BASE_ADDR_CB_MASK); | ||
| 435 | gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); | ||
| 436 | |||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) | ||
| 441 | { | ||
| 442 | u32 gscblk_cfg; | ||
| 443 | |||
| 444 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 445 | |||
| 446 | gscblk_cfg = readl(SYSREG_GSCBLK_CFG1); | ||
| 447 | |||
| 448 | if (enable) | ||
| 449 | gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) | | ||
| 450 | GSC_BLK_GSCL_WB_IN_SRC_SEL(ctx->id) | | ||
| 451 | GSC_BLK_SW_RESET_WB_DEST(ctx->id); | ||
| 452 | else | ||
| 453 | gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id); | ||
| 454 | |||
| 455 | writel(gscblk_cfg, SYSREG_GSCBLK_CFG1); | ||
| 456 | } | ||
| 457 | |||
| 458 | static void gsc_handle_irq(struct gsc_context *ctx, bool enable, | ||
| 459 | bool overflow, bool done) | ||
| 460 | { | ||
| 461 | u32 cfg; | ||
| 462 | |||
| 463 | DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, | ||
| 464 | enable, overflow, done); | ||
| 465 | |||
| 466 | cfg = gsc_read(GSC_IRQ); | ||
| 467 | cfg |= (GSC_IRQ_OR_MASK | GSC_IRQ_FRMDONE_MASK); | ||
| 468 | |||
| 469 | if (enable) | ||
| 470 | cfg |= GSC_IRQ_ENABLE; | ||
| 471 | else | ||
| 472 | cfg &= ~GSC_IRQ_ENABLE; | ||
| 473 | |||
| 474 | if (overflow) | ||
| 475 | cfg &= ~GSC_IRQ_OR_MASK; | ||
| 476 | else | ||
| 477 | cfg |= GSC_IRQ_OR_MASK; | ||
| 478 | |||
| 479 | if (done) | ||
| 480 | cfg &= ~GSC_IRQ_FRMDONE_MASK; | ||
| 481 | else | ||
| 482 | cfg |= GSC_IRQ_FRMDONE_MASK; | ||
| 483 | |||
| 484 | gsc_write(cfg, GSC_IRQ); | ||
| 485 | } | ||
| 486 | |||
| 487 | |||
| 488 | static int gsc_src_set_fmt(struct device *dev, u32 fmt) | ||
| 489 | { | ||
| 490 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 491 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 492 | u32 cfg; | ||
| 493 | |||
| 494 | DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); | ||
| 495 | |||
| 496 | cfg = gsc_read(GSC_IN_CON); | ||
| 497 | cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK | | ||
| 498 | GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK | | ||
| 499 | GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE | | ||
| 500 | GSC_IN_CHROM_STRIDE_SEL_MASK | GSC_IN_RB_SWAP_MASK); | ||
| 501 | |||
| 502 | switch (fmt) { | ||
| 503 | case DRM_FORMAT_RGB565: | ||
| 504 | cfg |= GSC_IN_RGB565; | ||
| 505 | break; | ||
| 506 | case DRM_FORMAT_XRGB8888: | ||
| 507 | cfg |= GSC_IN_XRGB8888; | ||
| 508 | break; | ||
| 509 | case DRM_FORMAT_BGRX8888: | ||
| 510 | cfg |= (GSC_IN_XRGB8888 | GSC_IN_RB_SWAP); | ||
| 511 | break; | ||
| 512 | case DRM_FORMAT_YUYV: | ||
| 513 | cfg |= (GSC_IN_YUV422_1P | | ||
| 514 | GSC_IN_YUV422_1P_ORDER_LSB_Y | | ||
| 515 | GSC_IN_CHROMA_ORDER_CBCR); | ||
| 516 | break; | ||
| 517 | case DRM_FORMAT_YVYU: | ||
| 518 | cfg |= (GSC_IN_YUV422_1P | | ||
| 519 | GSC_IN_YUV422_1P_ORDER_LSB_Y | | ||
| 520 | GSC_IN_CHROMA_ORDER_CRCB); | ||
| 521 | break; | ||
| 522 | case DRM_FORMAT_UYVY: | ||
| 523 | cfg |= (GSC_IN_YUV422_1P | | ||
| 524 | GSC_IN_YUV422_1P_OEDER_LSB_C | | ||
| 525 | GSC_IN_CHROMA_ORDER_CBCR); | ||
| 526 | break; | ||
| 527 | case DRM_FORMAT_VYUY: | ||
| 528 | cfg |= (GSC_IN_YUV422_1P | | ||
| 529 | GSC_IN_YUV422_1P_OEDER_LSB_C | | ||
| 530 | GSC_IN_CHROMA_ORDER_CRCB); | ||
| 531 | break; | ||
| 532 | case DRM_FORMAT_NV21: | ||
| 533 | case DRM_FORMAT_NV61: | ||
| 534 | cfg |= (GSC_IN_CHROMA_ORDER_CRCB | | ||
| 535 | GSC_IN_YUV420_2P); | ||
| 536 | break; | ||
| 537 | case DRM_FORMAT_YUV422: | ||
| 538 | cfg |= GSC_IN_YUV422_3P; | ||
| 539 | break; | ||
| 540 | case DRM_FORMAT_YUV420: | ||
| 541 | case DRM_FORMAT_YVU420: | ||
| 542 | cfg |= GSC_IN_YUV420_3P; | ||
| 543 | break; | ||
| 544 | case DRM_FORMAT_NV12: | ||
| 545 | case DRM_FORMAT_NV16: | ||
| 546 | cfg |= (GSC_IN_CHROMA_ORDER_CBCR | | ||
| 547 | GSC_IN_YUV420_2P); | ||
| 548 | break; | ||
| 549 | case DRM_FORMAT_NV12MT: | ||
| 550 | cfg |= (GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE); | ||
| 551 | break; | ||
| 552 | default: | ||
| 553 | dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); | ||
| 554 | return -EINVAL; | ||
| 555 | } | ||
| 556 | |||
| 557 | gsc_write(cfg, GSC_IN_CON); | ||
| 558 | |||
| 559 | return 0; | ||
| 560 | } | ||
| 561 | |||
| 562 | static int gsc_src_set_transf(struct device *dev, | ||
| 563 | enum drm_exynos_degree degree, | ||
| 564 | enum drm_exynos_flip flip, bool *swap) | ||
| 565 | { | ||
| 566 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 567 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 568 | u32 cfg; | ||
| 569 | |||
| 570 | DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, | ||
| 571 | degree, flip); | ||
| 572 | |||
| 573 | cfg = gsc_read(GSC_IN_CON); | ||
| 574 | cfg &= ~GSC_IN_ROT_MASK; | ||
| 575 | |||
| 576 | switch (degree) { | ||
| 577 | case EXYNOS_DRM_DEGREE_0: | ||
| 578 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 579 | cfg |= GSC_IN_ROT_XFLIP; | ||
| 580 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 581 | cfg |= GSC_IN_ROT_YFLIP; | ||
| 582 | break; | ||
| 583 | case EXYNOS_DRM_DEGREE_90: | ||
| 584 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 585 | cfg |= GSC_IN_ROT_90_XFLIP; | ||
| 586 | else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 587 | cfg |= GSC_IN_ROT_90_YFLIP; | ||
| 588 | else | ||
| 589 | cfg |= GSC_IN_ROT_90; | ||
| 590 | break; | ||
| 591 | case EXYNOS_DRM_DEGREE_180: | ||
| 592 | cfg |= GSC_IN_ROT_180; | ||
| 593 | break; | ||
| 594 | case EXYNOS_DRM_DEGREE_270: | ||
| 595 | cfg |= GSC_IN_ROT_270; | ||
| 596 | break; | ||
| 597 | default: | ||
| 598 | dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); | ||
| 599 | return -EINVAL; | ||
| 600 | } | ||
| 601 | |||
| 602 | gsc_write(cfg, GSC_IN_CON); | ||
| 603 | |||
| 604 | ctx->rotation = cfg & | ||
| 605 | (GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0; | ||
| 606 | *swap = ctx->rotation; | ||
| 607 | |||
| 608 | return 0; | ||
| 609 | } | ||
| 610 | |||
| 611 | static int gsc_src_set_size(struct device *dev, int swap, | ||
| 612 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | ||
| 613 | { | ||
| 614 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 615 | struct drm_exynos_pos img_pos = *pos; | ||
| 616 | struct gsc_scaler *sc = &ctx->sc; | ||
| 617 | u32 cfg; | ||
| 618 | |||
| 619 | DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", | ||
| 620 | __func__, swap, pos->x, pos->y, pos->w, pos->h); | ||
| 621 | |||
| 622 | if (swap) { | ||
| 623 | img_pos.w = pos->h; | ||
| 624 | img_pos.h = pos->w; | ||
| 625 | } | ||
| 626 | |||
| 627 | /* pixel offset */ | ||
| 628 | cfg = (GSC_SRCIMG_OFFSET_X(img_pos.x) | | ||
| 629 | GSC_SRCIMG_OFFSET_Y(img_pos.y)); | ||
| 630 | gsc_write(cfg, GSC_SRCIMG_OFFSET); | ||
| 631 | |||
| 632 | /* cropped size */ | ||
| 633 | cfg = (GSC_CROPPED_WIDTH(img_pos.w) | | ||
| 634 | GSC_CROPPED_HEIGHT(img_pos.h)); | ||
| 635 | gsc_write(cfg, GSC_CROPPED_SIZE); | ||
| 636 | |||
| 637 | DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n", | ||
| 638 | __func__, sz->hsize, sz->vsize); | ||
| 639 | |||
| 640 | /* original size */ | ||
| 641 | cfg = gsc_read(GSC_SRCIMG_SIZE); | ||
| 642 | cfg &= ~(GSC_SRCIMG_HEIGHT_MASK | | ||
| 643 | GSC_SRCIMG_WIDTH_MASK); | ||
| 644 | |||
| 645 | cfg |= (GSC_SRCIMG_WIDTH(sz->hsize) | | ||
| 646 | GSC_SRCIMG_HEIGHT(sz->vsize)); | ||
| 647 | |||
| 648 | gsc_write(cfg, GSC_SRCIMG_SIZE); | ||
| 649 | |||
| 650 | cfg = gsc_read(GSC_IN_CON); | ||
| 651 | cfg &= ~GSC_IN_RGB_TYPE_MASK; | ||
| 652 | |||
| 653 | DRM_DEBUG_KMS("%s:width[%d]range[%d]\n", | ||
| 654 | __func__, pos->w, sc->range); | ||
| 655 | |||
| 656 | if (pos->w >= GSC_WIDTH_ITU_709) | ||
| 657 | if (sc->range) | ||
| 658 | cfg |= GSC_IN_RGB_HD_WIDE; | ||
| 659 | else | ||
| 660 | cfg |= GSC_IN_RGB_HD_NARROW; | ||
| 661 | else | ||
| 662 | if (sc->range) | ||
| 663 | cfg |= GSC_IN_RGB_SD_WIDE; | ||
| 664 | else | ||
| 665 | cfg |= GSC_IN_RGB_SD_NARROW; | ||
| 666 | |||
| 667 | gsc_write(cfg, GSC_IN_CON); | ||
| 668 | |||
| 669 | return 0; | ||
| 670 | } | ||
| 671 | |||
| 672 | static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, | ||
| 673 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 674 | { | ||
| 675 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 676 | bool masked; | ||
| 677 | u32 cfg; | ||
| 678 | u32 mask = 0x00000001 << buf_id; | ||
| 679 | |||
| 680 | DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, | ||
| 681 | buf_id, buf_type); | ||
| 682 | |||
| 683 | /* mask register set */ | ||
| 684 | cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); | ||
| 685 | |||
| 686 | switch (buf_type) { | ||
| 687 | case IPP_BUF_ENQUEUE: | ||
| 688 | masked = false; | ||
| 689 | break; | ||
| 690 | case IPP_BUF_DEQUEUE: | ||
| 691 | masked = true; | ||
| 692 | break; | ||
| 693 | default: | ||
| 694 | dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); | ||
| 695 | return -EINVAL; | ||
| 696 | } | ||
| 697 | |||
| 698 | /* sequence id */ | ||
| 699 | cfg &= ~mask; | ||
| 700 | cfg |= masked << buf_id; | ||
| 701 | gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK); | ||
| 702 | gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK); | ||
| 703 | gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK); | ||
| 704 | |||
| 705 | return 0; | ||
| 706 | } | ||
| 707 | |||
| 708 | static int gsc_src_set_addr(struct device *dev, | ||
| 709 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | ||
| 710 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 711 | { | ||
| 712 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 713 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 714 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 715 | struct drm_exynos_ipp_property *property; | ||
| 716 | |||
| 717 | if (!c_node) { | ||
| 718 | DRM_ERROR("failed to get c_node.\n"); | ||
| 719 | return -EFAULT; | ||
| 720 | } | ||
| 721 | |||
| 722 | property = &c_node->property; | ||
| 723 | if (!property) { | ||
| 724 | DRM_ERROR("failed to get property.\n"); | ||
| 725 | return -EFAULT; | ||
| 726 | } | ||
| 727 | |||
| 728 | DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, | ||
| 729 | property->prop_id, buf_id, buf_type); | ||
| 730 | |||
| 731 | if (buf_id > GSC_MAX_SRC) { | ||
| 732 | dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); | ||
| 733 | return -EINVAL; | ||
| 734 | } | ||
| 735 | |||
| 736 | /* address register set */ | ||
| 737 | switch (buf_type) { | ||
| 738 | case IPP_BUF_ENQUEUE: | ||
| 739 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], | ||
| 740 | GSC_IN_BASE_ADDR_Y(buf_id)); | ||
| 741 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 742 | GSC_IN_BASE_ADDR_CB(buf_id)); | ||
| 743 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 744 | GSC_IN_BASE_ADDR_CR(buf_id)); | ||
| 745 | break; | ||
| 746 | case IPP_BUF_DEQUEUE: | ||
| 747 | gsc_write(0x0, GSC_IN_BASE_ADDR_Y(buf_id)); | ||
| 748 | gsc_write(0x0, GSC_IN_BASE_ADDR_CB(buf_id)); | ||
| 749 | gsc_write(0x0, GSC_IN_BASE_ADDR_CR(buf_id)); | ||
| 750 | break; | ||
| 751 | default: | ||
| 752 | /* bypass */ | ||
| 753 | break; | ||
| 754 | } | ||
| 755 | |||
| 756 | return gsc_src_set_buf_seq(ctx, buf_id, buf_type); | ||
| 757 | } | ||
| 758 | |||
| 759 | static struct exynos_drm_ipp_ops gsc_src_ops = { | ||
| 760 | .set_fmt = gsc_src_set_fmt, | ||
| 761 | .set_transf = gsc_src_set_transf, | ||
| 762 | .set_size = gsc_src_set_size, | ||
| 763 | .set_addr = gsc_src_set_addr, | ||
| 764 | }; | ||
| 765 | |||
| 766 | static int gsc_dst_set_fmt(struct device *dev, u32 fmt) | ||
| 767 | { | ||
| 768 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 769 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 770 | u32 cfg; | ||
| 771 | |||
| 772 | DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); | ||
| 773 | |||
| 774 | cfg = gsc_read(GSC_OUT_CON); | ||
| 775 | cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK | | ||
| 776 | GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK | | ||
| 777 | GSC_OUT_CHROM_STRIDE_SEL_MASK | GSC_OUT_RB_SWAP_MASK | | ||
| 778 | GSC_OUT_GLOBAL_ALPHA_MASK); | ||
| 779 | |||
| 780 | switch (fmt) { | ||
| 781 | case DRM_FORMAT_RGB565: | ||
| 782 | cfg |= GSC_OUT_RGB565; | ||
| 783 | break; | ||
| 784 | case DRM_FORMAT_XRGB8888: | ||
| 785 | cfg |= GSC_OUT_XRGB8888; | ||
| 786 | break; | ||
| 787 | case DRM_FORMAT_BGRX8888: | ||
| 788 | cfg |= (GSC_OUT_XRGB8888 | GSC_OUT_RB_SWAP); | ||
| 789 | break; | ||
| 790 | case DRM_FORMAT_YUYV: | ||
| 791 | cfg |= (GSC_OUT_YUV422_1P | | ||
| 792 | GSC_OUT_YUV422_1P_ORDER_LSB_Y | | ||
| 793 | GSC_OUT_CHROMA_ORDER_CBCR); | ||
| 794 | break; | ||
| 795 | case DRM_FORMAT_YVYU: | ||
| 796 | cfg |= (GSC_OUT_YUV422_1P | | ||
| 797 | GSC_OUT_YUV422_1P_ORDER_LSB_Y | | ||
| 798 | GSC_OUT_CHROMA_ORDER_CRCB); | ||
| 799 | break; | ||
| 800 | case DRM_FORMAT_UYVY: | ||
| 801 | cfg |= (GSC_OUT_YUV422_1P | | ||
| 802 | GSC_OUT_YUV422_1P_OEDER_LSB_C | | ||
| 803 | GSC_OUT_CHROMA_ORDER_CBCR); | ||
| 804 | break; | ||
| 805 | case DRM_FORMAT_VYUY: | ||
| 806 | cfg |= (GSC_OUT_YUV422_1P | | ||
| 807 | GSC_OUT_YUV422_1P_OEDER_LSB_C | | ||
| 808 | GSC_OUT_CHROMA_ORDER_CRCB); | ||
| 809 | break; | ||
| 810 | case DRM_FORMAT_NV21: | ||
| 811 | case DRM_FORMAT_NV61: | ||
| 812 | cfg |= (GSC_OUT_CHROMA_ORDER_CRCB | GSC_OUT_YUV420_2P); | ||
| 813 | break; | ||
| 814 | case DRM_FORMAT_YUV422: | ||
| 815 | case DRM_FORMAT_YUV420: | ||
| 816 | case DRM_FORMAT_YVU420: | ||
| 817 | cfg |= GSC_OUT_YUV420_3P; | ||
| 818 | break; | ||
| 819 | case DRM_FORMAT_NV12: | ||
| 820 | case DRM_FORMAT_NV16: | ||
| 821 | cfg |= (GSC_OUT_CHROMA_ORDER_CBCR | | ||
| 822 | GSC_OUT_YUV420_2P); | ||
| 823 | break; | ||
| 824 | case DRM_FORMAT_NV12MT: | ||
| 825 | cfg |= (GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE); | ||
| 826 | break; | ||
| 827 | default: | ||
| 828 | dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); | ||
| 829 | return -EINVAL; | ||
| 830 | } | ||
| 831 | |||
| 832 | gsc_write(cfg, GSC_OUT_CON); | ||
| 833 | |||
| 834 | return 0; | ||
| 835 | } | ||
| 836 | |||
| 837 | static int gsc_dst_set_transf(struct device *dev, | ||
| 838 | enum drm_exynos_degree degree, | ||
| 839 | enum drm_exynos_flip flip, bool *swap) | ||
| 840 | { | ||
| 841 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 842 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 843 | u32 cfg; | ||
| 844 | |||
| 845 | DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, | ||
| 846 | degree, flip); | ||
| 847 | |||
| 848 | cfg = gsc_read(GSC_IN_CON); | ||
| 849 | cfg &= ~GSC_IN_ROT_MASK; | ||
| 850 | |||
| 851 | switch (degree) { | ||
| 852 | case EXYNOS_DRM_DEGREE_0: | ||
| 853 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 854 | cfg |= GSC_IN_ROT_XFLIP; | ||
| 855 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 856 | cfg |= GSC_IN_ROT_YFLIP; | ||
| 857 | break; | ||
| 858 | case EXYNOS_DRM_DEGREE_90: | ||
| 859 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 860 | cfg |= GSC_IN_ROT_90_XFLIP; | ||
| 861 | else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 862 | cfg |= GSC_IN_ROT_90_YFLIP; | ||
| 863 | else | ||
| 864 | cfg |= GSC_IN_ROT_90; | ||
| 865 | break; | ||
| 866 | case EXYNOS_DRM_DEGREE_180: | ||
| 867 | cfg |= GSC_IN_ROT_180; | ||
| 868 | break; | ||
| 869 | case EXYNOS_DRM_DEGREE_270: | ||
| 870 | cfg |= GSC_IN_ROT_270; | ||
| 871 | break; | ||
| 872 | default: | ||
| 873 | dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); | ||
| 874 | return -EINVAL; | ||
| 875 | } | ||
| 876 | |||
| 877 | gsc_write(cfg, GSC_IN_CON); | ||
| 878 | |||
| 879 | ctx->rotation = cfg & | ||
| 880 | (GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0; | ||
| 881 | *swap = ctx->rotation; | ||
| 882 | |||
| 883 | return 0; | ||
| 884 | } | ||
| 885 | |||
| 886 | static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio) | ||
| 887 | { | ||
| 888 | DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); | ||
| 889 | |||
| 890 | if (src >= dst * 8) { | ||
| 891 | DRM_ERROR("failed to make ratio and shift.\n"); | ||
| 892 | return -EINVAL; | ||
| 893 | } else if (src >= dst * 4) | ||
| 894 | *ratio = 4; | ||
| 895 | else if (src >= dst * 2) | ||
| 896 | *ratio = 2; | ||
| 897 | else | ||
| 898 | *ratio = 1; | ||
| 899 | |||
| 900 | return 0; | ||
| 901 | } | ||
| 902 | |||
| 903 | static void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *shfactor) | ||
| 904 | { | ||
| 905 | if (hratio == 4 && vratio == 4) | ||
| 906 | *shfactor = 4; | ||
| 907 | else if ((hratio == 4 && vratio == 2) || | ||
| 908 | (hratio == 2 && vratio == 4)) | ||
| 909 | *shfactor = 3; | ||
| 910 | else if ((hratio == 4 && vratio == 1) || | ||
| 911 | (hratio == 1 && vratio == 4) || | ||
| 912 | (hratio == 2 && vratio == 2)) | ||
| 913 | *shfactor = 2; | ||
| 914 | else if (hratio == 1 && vratio == 1) | ||
| 915 | *shfactor = 0; | ||
| 916 | else | ||
| 917 | *shfactor = 1; | ||
| 918 | } | ||
| 919 | |||
| 920 | static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, | ||
| 921 | struct drm_exynos_pos *src, struct drm_exynos_pos *dst) | ||
| 922 | { | ||
| 923 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 924 | u32 cfg; | ||
| 925 | u32 src_w, src_h, dst_w, dst_h; | ||
| 926 | int ret = 0; | ||
| 927 | |||
| 928 | src_w = src->w; | ||
| 929 | src_h = src->h; | ||
| 930 | |||
| 931 | if (ctx->rotation) { | ||
| 932 | dst_w = dst->h; | ||
| 933 | dst_h = dst->w; | ||
| 934 | } else { | ||
| 935 | dst_w = dst->w; | ||
| 936 | dst_h = dst->h; | ||
| 937 | } | ||
| 938 | |||
| 939 | ret = gsc_get_ratio_shift(src_w, dst_w, &sc->pre_hratio); | ||
| 940 | if (ret) { | ||
| 941 | dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); | ||
| 942 | return ret; | ||
| 943 | } | ||
| 944 | |||
| 945 | ret = gsc_get_ratio_shift(src_h, dst_h, &sc->pre_vratio); | ||
| 946 | if (ret) { | ||
| 947 | dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); | ||
| 948 | return ret; | ||
| 949 | } | ||
| 950 | |||
| 951 | DRM_DEBUG_KMS("%s:pre_hratio[%d]pre_vratio[%d]\n", | ||
| 952 | __func__, sc->pre_hratio, sc->pre_vratio); | ||
| 953 | |||
| 954 | sc->main_hratio = (src_w << 16) / dst_w; | ||
| 955 | sc->main_vratio = (src_h << 16) / dst_h; | ||
| 956 | |||
| 957 | DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n", | ||
| 958 | __func__, sc->main_hratio, sc->main_vratio); | ||
| 959 | |||
| 960 | gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio, | ||
| 961 | &sc->pre_shfactor); | ||
| 962 | |||
| 963 | DRM_DEBUG_KMS("%s:pre_shfactor[%d]\n", __func__, | ||
| 964 | sc->pre_shfactor); | ||
| 965 | |||
| 966 | cfg = (GSC_PRESC_SHFACTOR(sc->pre_shfactor) | | ||
| 967 | GSC_PRESC_H_RATIO(sc->pre_hratio) | | ||
| 968 | GSC_PRESC_V_RATIO(sc->pre_vratio)); | ||
| 969 | gsc_write(cfg, GSC_PRE_SCALE_RATIO); | ||
| 970 | |||
| 971 | return ret; | ||
| 972 | } | ||
| 973 | |||
| 974 | static void gsc_set_h_coef(struct gsc_context *ctx, unsigned long main_hratio) | ||
| 975 | { | ||
| 976 | int i, j, k, sc_ratio; | ||
| 977 | |||
| 978 | if (main_hratio <= GSC_SC_UP_MAX_RATIO) | ||
| 979 | sc_ratio = 0; | ||
| 980 | else if (main_hratio <= GSC_SC_DOWN_RATIO_7_8) | ||
| 981 | sc_ratio = 1; | ||
| 982 | else if (main_hratio <= GSC_SC_DOWN_RATIO_6_8) | ||
| 983 | sc_ratio = 2; | ||
| 984 | else if (main_hratio <= GSC_SC_DOWN_RATIO_5_8) | ||
| 985 | sc_ratio = 3; | ||
| 986 | else if (main_hratio <= GSC_SC_DOWN_RATIO_4_8) | ||
| 987 | sc_ratio = 4; | ||
| 988 | else if (main_hratio <= GSC_SC_DOWN_RATIO_3_8) | ||
| 989 | sc_ratio = 5; | ||
| 990 | else | ||
| 991 | sc_ratio = 6; | ||
| 992 | |||
| 993 | for (i = 0; i < GSC_COEF_PHASE; i++) | ||
| 994 | for (j = 0; j < GSC_COEF_H_8T; j++) | ||
| 995 | for (k = 0; k < GSC_COEF_DEPTH; k++) | ||
| 996 | gsc_write(h_coef_8t[sc_ratio][i][j], | ||
| 997 | GSC_HCOEF(i, j, k)); | ||
| 998 | } | ||
| 999 | |||
| 1000 | static void gsc_set_v_coef(struct gsc_context *ctx, unsigned long main_vratio) | ||
| 1001 | { | ||
| 1002 | int i, j, k, sc_ratio; | ||
| 1003 | |||
| 1004 | if (main_vratio <= GSC_SC_UP_MAX_RATIO) | ||
| 1005 | sc_ratio = 0; | ||
| 1006 | else if (main_vratio <= GSC_SC_DOWN_RATIO_7_8) | ||
| 1007 | sc_ratio = 1; | ||
| 1008 | else if (main_vratio <= GSC_SC_DOWN_RATIO_6_8) | ||
| 1009 | sc_ratio = 2; | ||
| 1010 | else if (main_vratio <= GSC_SC_DOWN_RATIO_5_8) | ||
| 1011 | sc_ratio = 3; | ||
| 1012 | else if (main_vratio <= GSC_SC_DOWN_RATIO_4_8) | ||
| 1013 | sc_ratio = 4; | ||
| 1014 | else if (main_vratio <= GSC_SC_DOWN_RATIO_3_8) | ||
| 1015 | sc_ratio = 5; | ||
| 1016 | else | ||
| 1017 | sc_ratio = 6; | ||
| 1018 | |||
| 1019 | for (i = 0; i < GSC_COEF_PHASE; i++) | ||
| 1020 | for (j = 0; j < GSC_COEF_V_4T; j++) | ||
| 1021 | for (k = 0; k < GSC_COEF_DEPTH; k++) | ||
| 1022 | gsc_write(v_coef_4t[sc_ratio][i][j], | ||
| 1023 | GSC_VCOEF(i, j, k)); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc) | ||
| 1027 | { | ||
| 1028 | u32 cfg; | ||
| 1029 | |||
| 1030 | DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n", | ||
| 1031 | __func__, sc->main_hratio, sc->main_vratio); | ||
| 1032 | |||
| 1033 | gsc_set_h_coef(ctx, sc->main_hratio); | ||
| 1034 | cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio); | ||
| 1035 | gsc_write(cfg, GSC_MAIN_H_RATIO); | ||
| 1036 | |||
| 1037 | gsc_set_v_coef(ctx, sc->main_vratio); | ||
| 1038 | cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio); | ||
| 1039 | gsc_write(cfg, GSC_MAIN_V_RATIO); | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | static int gsc_dst_set_size(struct device *dev, int swap, | ||
| 1043 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | ||
| 1044 | { | ||
| 1045 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1046 | struct drm_exynos_pos img_pos = *pos; | ||
| 1047 | struct gsc_scaler *sc = &ctx->sc; | ||
| 1048 | u32 cfg; | ||
| 1049 | |||
| 1050 | DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", | ||
| 1051 | __func__, swap, pos->x, pos->y, pos->w, pos->h); | ||
| 1052 | |||
| 1053 | if (swap) { | ||
| 1054 | img_pos.w = pos->h; | ||
| 1055 | img_pos.h = pos->w; | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /* pixel offset */ | ||
| 1059 | cfg = (GSC_DSTIMG_OFFSET_X(pos->x) | | ||
| 1060 | GSC_DSTIMG_OFFSET_Y(pos->y)); | ||
| 1061 | gsc_write(cfg, GSC_DSTIMG_OFFSET); | ||
| 1062 | |||
| 1063 | /* scaled size */ | ||
| 1064 | cfg = (GSC_SCALED_WIDTH(img_pos.w) | GSC_SCALED_HEIGHT(img_pos.h)); | ||
| 1065 | gsc_write(cfg, GSC_SCALED_SIZE); | ||
| 1066 | |||
| 1067 | DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n", | ||
| 1068 | __func__, sz->hsize, sz->vsize); | ||
| 1069 | |||
| 1070 | /* original size */ | ||
| 1071 | cfg = gsc_read(GSC_DSTIMG_SIZE); | ||
| 1072 | cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | | ||
| 1073 | GSC_DSTIMG_WIDTH_MASK); | ||
| 1074 | cfg |= (GSC_DSTIMG_WIDTH(sz->hsize) | | ||
| 1075 | GSC_DSTIMG_HEIGHT(sz->vsize)); | ||
| 1076 | gsc_write(cfg, GSC_DSTIMG_SIZE); | ||
| 1077 | |||
| 1078 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1079 | cfg &= ~GSC_OUT_RGB_TYPE_MASK; | ||
| 1080 | |||
| 1081 | DRM_DEBUG_KMS("%s:width[%d]range[%d]\n", | ||
| 1082 | __func__, pos->w, sc->range); | ||
| 1083 | |||
| 1084 | if (pos->w >= GSC_WIDTH_ITU_709) | ||
| 1085 | if (sc->range) | ||
| 1086 | cfg |= GSC_OUT_RGB_HD_WIDE; | ||
| 1087 | else | ||
| 1088 | cfg |= GSC_OUT_RGB_HD_NARROW; | ||
| 1089 | else | ||
| 1090 | if (sc->range) | ||
| 1091 | cfg |= GSC_OUT_RGB_SD_WIDE; | ||
| 1092 | else | ||
| 1093 | cfg |= GSC_OUT_RGB_SD_NARROW; | ||
| 1094 | |||
| 1095 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1096 | |||
| 1097 | return 0; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | static int gsc_dst_get_buf_seq(struct gsc_context *ctx) | ||
| 1101 | { | ||
| 1102 | u32 cfg, i, buf_num = GSC_REG_SZ; | ||
| 1103 | u32 mask = 0x00000001; | ||
| 1104 | |||
| 1105 | cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 1106 | |||
| 1107 | for (i = 0; i < GSC_REG_SZ; i++) | ||
| 1108 | if (cfg & (mask << i)) | ||
| 1109 | buf_num--; | ||
| 1110 | |||
| 1111 | DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); | ||
| 1112 | |||
| 1113 | return buf_num; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, | ||
| 1117 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 1118 | { | ||
| 1119 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1120 | bool masked; | ||
| 1121 | u32 cfg; | ||
| 1122 | u32 mask = 0x00000001 << buf_id; | ||
| 1123 | int ret = 0; | ||
| 1124 | |||
| 1125 | DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, | ||
| 1126 | buf_id, buf_type); | ||
| 1127 | |||
| 1128 | mutex_lock(&ctx->lock); | ||
| 1129 | |||
| 1130 | /* mask register set */ | ||
| 1131 | cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 1132 | |||
| 1133 | switch (buf_type) { | ||
| 1134 | case IPP_BUF_ENQUEUE: | ||
| 1135 | masked = false; | ||
| 1136 | break; | ||
| 1137 | case IPP_BUF_DEQUEUE: | ||
| 1138 | masked = true; | ||
| 1139 | break; | ||
| 1140 | default: | ||
| 1141 | dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); | ||
| 1142 | ret = -EINVAL; | ||
| 1143 | goto err_unlock; | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | /* sequence id */ | ||
| 1147 | cfg &= ~mask; | ||
| 1148 | cfg |= masked << buf_id; | ||
| 1149 | gsc_write(cfg, GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 1150 | gsc_write(cfg, GSC_OUT_BASE_ADDR_CB_MASK); | ||
| 1151 | gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); | ||
| 1152 | |||
| 1153 | /* interrupt enable */ | ||
| 1154 | if (buf_type == IPP_BUF_ENQUEUE && | ||
| 1155 | gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START) | ||
| 1156 | gsc_handle_irq(ctx, true, false, true); | ||
| 1157 | |||
| 1158 | /* interrupt disable */ | ||
| 1159 | if (buf_type == IPP_BUF_DEQUEUE && | ||
| 1160 | gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP) | ||
| 1161 | gsc_handle_irq(ctx, false, false, true); | ||
| 1162 | |||
| 1163 | err_unlock: | ||
| 1164 | mutex_unlock(&ctx->lock); | ||
| 1165 | return ret; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | static int gsc_dst_set_addr(struct device *dev, | ||
| 1169 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | ||
| 1170 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 1171 | { | ||
| 1172 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1173 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1174 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 1175 | struct drm_exynos_ipp_property *property; | ||
| 1176 | |||
| 1177 | if (!c_node) { | ||
| 1178 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1179 | return -EFAULT; | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | property = &c_node->property; | ||
| 1183 | if (!property) { | ||
| 1184 | DRM_ERROR("failed to get property.\n"); | ||
| 1185 | return -EFAULT; | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, | ||
| 1189 | property->prop_id, buf_id, buf_type); | ||
| 1190 | |||
| 1191 | if (buf_id > GSC_MAX_DST) { | ||
| 1192 | dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); | ||
| 1193 | return -EINVAL; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | /* address register set */ | ||
| 1197 | switch (buf_type) { | ||
| 1198 | case IPP_BUF_ENQUEUE: | ||
| 1199 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], | ||
| 1200 | GSC_OUT_BASE_ADDR_Y(buf_id)); | ||
| 1201 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 1202 | GSC_OUT_BASE_ADDR_CB(buf_id)); | ||
| 1203 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 1204 | GSC_OUT_BASE_ADDR_CR(buf_id)); | ||
| 1205 | break; | ||
| 1206 | case IPP_BUF_DEQUEUE: | ||
| 1207 | gsc_write(0x0, GSC_OUT_BASE_ADDR_Y(buf_id)); | ||
| 1208 | gsc_write(0x0, GSC_OUT_BASE_ADDR_CB(buf_id)); | ||
| 1209 | gsc_write(0x0, GSC_OUT_BASE_ADDR_CR(buf_id)); | ||
| 1210 | break; | ||
| 1211 | default: | ||
| 1212 | /* bypass */ | ||
| 1213 | break; | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | return gsc_dst_set_buf_seq(ctx, buf_id, buf_type); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | static struct exynos_drm_ipp_ops gsc_dst_ops = { | ||
| 1220 | .set_fmt = gsc_dst_set_fmt, | ||
| 1221 | .set_transf = gsc_dst_set_transf, | ||
| 1222 | .set_size = gsc_dst_set_size, | ||
| 1223 | .set_addr = gsc_dst_set_addr, | ||
| 1224 | }; | ||
| 1225 | |||
| 1226 | static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable) | ||
| 1227 | { | ||
| 1228 | DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); | ||
| 1229 | |||
| 1230 | if (enable) { | ||
| 1231 | clk_enable(ctx->gsc_clk); | ||
| 1232 | ctx->suspended = false; | ||
| 1233 | } else { | ||
| 1234 | clk_disable(ctx->gsc_clk); | ||
| 1235 | ctx->suspended = true; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | return 0; | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | static int gsc_get_src_buf_index(struct gsc_context *ctx) | ||
| 1242 | { | ||
| 1243 | u32 cfg, curr_index, i; | ||
| 1244 | u32 buf_id = GSC_MAX_SRC; | ||
| 1245 | int ret; | ||
| 1246 | |||
| 1247 | DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); | ||
| 1248 | |||
| 1249 | cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); | ||
| 1250 | curr_index = GSC_IN_CURR_GET_INDEX(cfg); | ||
| 1251 | |||
| 1252 | for (i = curr_index; i < GSC_MAX_SRC; i++) { | ||
| 1253 | if (!((cfg >> i) & 0x1)) { | ||
| 1254 | buf_id = i; | ||
| 1255 | break; | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | if (buf_id == GSC_MAX_SRC) { | ||
| 1260 | DRM_ERROR("failed to get in buffer index.\n"); | ||
| 1261 | return -EINVAL; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | ret = gsc_src_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); | ||
| 1265 | if (ret < 0) { | ||
| 1266 | DRM_ERROR("failed to dequeue.\n"); | ||
| 1267 | return ret; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | DRM_DEBUG_KMS("%s:cfg[0x%x]curr_index[%d]buf_id[%d]\n", __func__, cfg, | ||
| 1271 | curr_index, buf_id); | ||
| 1272 | |||
| 1273 | return buf_id; | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | static int gsc_get_dst_buf_index(struct gsc_context *ctx) | ||
| 1277 | { | ||
| 1278 | u32 cfg, curr_index, i; | ||
| 1279 | u32 buf_id = GSC_MAX_DST; | ||
| 1280 | int ret; | ||
| 1281 | |||
| 1282 | DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); | ||
| 1283 | |||
| 1284 | cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 1285 | curr_index = GSC_OUT_CURR_GET_INDEX(cfg); | ||
| 1286 | |||
| 1287 | for (i = curr_index; i < GSC_MAX_DST; i++) { | ||
| 1288 | if (!((cfg >> i) & 0x1)) { | ||
| 1289 | buf_id = i; | ||
| 1290 | break; | ||
| 1291 | } | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | if (buf_id == GSC_MAX_DST) { | ||
| 1295 | DRM_ERROR("failed to get out buffer index.\n"); | ||
| 1296 | return -EINVAL; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | ret = gsc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); | ||
| 1300 | if (ret < 0) { | ||
| 1301 | DRM_ERROR("failed to dequeue.\n"); | ||
| 1302 | return ret; | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | DRM_DEBUG_KMS("%s:cfg[0x%x]curr_index[%d]buf_id[%d]\n", __func__, cfg, | ||
| 1306 | curr_index, buf_id); | ||
| 1307 | |||
| 1308 | return buf_id; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | static irqreturn_t gsc_irq_handler(int irq, void *dev_id) | ||
| 1312 | { | ||
| 1313 | struct gsc_context *ctx = dev_id; | ||
| 1314 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1315 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 1316 | struct drm_exynos_ipp_event_work *event_work = | ||
| 1317 | c_node->event_work; | ||
| 1318 | u32 status; | ||
| 1319 | int buf_id[EXYNOS_DRM_OPS_MAX]; | ||
| 1320 | |||
| 1321 | DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); | ||
| 1322 | |||
| 1323 | status = gsc_read(GSC_IRQ); | ||
| 1324 | if (status & GSC_IRQ_STATUS_OR_IRQ) { | ||
| 1325 | dev_err(ippdrv->dev, "occured overflow at %d, status 0x%x.\n", | ||
| 1326 | ctx->id, status); | ||
| 1327 | return IRQ_NONE; | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | if (status & GSC_IRQ_STATUS_OR_FRM_DONE) { | ||
| 1331 | dev_dbg(ippdrv->dev, "occured frame done at %d, status 0x%x.\n", | ||
| 1332 | ctx->id, status); | ||
| 1333 | |||
| 1334 | buf_id[EXYNOS_DRM_OPS_SRC] = gsc_get_src_buf_index(ctx); | ||
| 1335 | if (buf_id[EXYNOS_DRM_OPS_SRC] < 0) | ||
| 1336 | return IRQ_HANDLED; | ||
| 1337 | |||
| 1338 | buf_id[EXYNOS_DRM_OPS_DST] = gsc_get_dst_buf_index(ctx); | ||
| 1339 | if (buf_id[EXYNOS_DRM_OPS_DST] < 0) | ||
| 1340 | return IRQ_HANDLED; | ||
| 1341 | |||
| 1342 | DRM_DEBUG_KMS("%s:buf_id_src[%d]buf_id_dst[%d]\n", __func__, | ||
| 1343 | buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]); | ||
| 1344 | |||
| 1345 | event_work->ippdrv = ippdrv; | ||
| 1346 | event_work->buf_id[EXYNOS_DRM_OPS_SRC] = | ||
| 1347 | buf_id[EXYNOS_DRM_OPS_SRC]; | ||
| 1348 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = | ||
| 1349 | buf_id[EXYNOS_DRM_OPS_DST]; | ||
| 1350 | queue_work(ippdrv->event_workq, | ||
| 1351 | (struct work_struct *)event_work); | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | return IRQ_HANDLED; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | ||
| 1358 | { | ||
| 1359 | struct drm_exynos_ipp_prop_list *prop_list; | ||
| 1360 | |||
| 1361 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1362 | |||
| 1363 | prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); | ||
| 1364 | if (!prop_list) { | ||
| 1365 | DRM_ERROR("failed to alloc property list.\n"); | ||
| 1366 | return -ENOMEM; | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | prop_list->version = 1; | ||
| 1370 | prop_list->writeback = 1; | ||
| 1371 | prop_list->refresh_min = GSC_REFRESH_MIN; | ||
| 1372 | prop_list->refresh_max = GSC_REFRESH_MAX; | ||
| 1373 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
| 1374 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
| 1375 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
| 1376 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
| 1377 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
| 1378 | (1 << EXYNOS_DRM_DEGREE_270); | ||
| 1379 | prop_list->csc = 1; | ||
| 1380 | prop_list->crop = 1; | ||
| 1381 | prop_list->crop_max.hsize = GSC_CROP_MAX; | ||
| 1382 | prop_list->crop_max.vsize = GSC_CROP_MAX; | ||
| 1383 | prop_list->crop_min.hsize = GSC_CROP_MIN; | ||
| 1384 | prop_list->crop_min.vsize = GSC_CROP_MIN; | ||
| 1385 | prop_list->scale = 1; | ||
| 1386 | prop_list->scale_max.hsize = GSC_SCALE_MAX; | ||
| 1387 | prop_list->scale_max.vsize = GSC_SCALE_MAX; | ||
| 1388 | prop_list->scale_min.hsize = GSC_SCALE_MIN; | ||
| 1389 | prop_list->scale_min.vsize = GSC_SCALE_MIN; | ||
| 1390 | |||
| 1391 | ippdrv->prop_list = prop_list; | ||
| 1392 | |||
| 1393 | return 0; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | static inline bool gsc_check_drm_flip(enum drm_exynos_flip flip) | ||
| 1397 | { | ||
| 1398 | switch (flip) { | ||
| 1399 | case EXYNOS_DRM_FLIP_NONE: | ||
| 1400 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 1401 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 1402 | case EXYNOS_DRM_FLIP_VERTICAL | EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 1403 | return true; | ||
| 1404 | default: | ||
| 1405 | DRM_DEBUG_KMS("%s:invalid flip\n", __func__); | ||
| 1406 | return false; | ||
| 1407 | } | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static int gsc_ippdrv_check_property(struct device *dev, | ||
| 1411 | struct drm_exynos_ipp_property *property) | ||
| 1412 | { | ||
| 1413 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1414 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1415 | struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list; | ||
| 1416 | struct drm_exynos_ipp_config *config; | ||
| 1417 | struct drm_exynos_pos *pos; | ||
| 1418 | struct drm_exynos_sz *sz; | ||
| 1419 | bool swap; | ||
| 1420 | int i; | ||
| 1421 | |||
| 1422 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1423 | |||
| 1424 | for_each_ipp_ops(i) { | ||
| 1425 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1426 | (property->cmd == IPP_CMD_WB)) | ||
| 1427 | continue; | ||
| 1428 | |||
| 1429 | config = &property->config[i]; | ||
| 1430 | pos = &config->pos; | ||
| 1431 | sz = &config->sz; | ||
| 1432 | |||
| 1433 | /* check for flip */ | ||
| 1434 | if (!gsc_check_drm_flip(config->flip)) { | ||
| 1435 | DRM_ERROR("invalid flip.\n"); | ||
| 1436 | goto err_property; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | /* check for degree */ | ||
| 1440 | switch (config->degree) { | ||
| 1441 | case EXYNOS_DRM_DEGREE_90: | ||
| 1442 | case EXYNOS_DRM_DEGREE_270: | ||
| 1443 | swap = true; | ||
| 1444 | break; | ||
| 1445 | case EXYNOS_DRM_DEGREE_0: | ||
| 1446 | case EXYNOS_DRM_DEGREE_180: | ||
| 1447 | swap = false; | ||
| 1448 | break; | ||
| 1449 | default: | ||
| 1450 | DRM_ERROR("invalid degree.\n"); | ||
| 1451 | goto err_property; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | /* check for buffer bound */ | ||
| 1455 | if ((pos->x + pos->w > sz->hsize) || | ||
| 1456 | (pos->y + pos->h > sz->vsize)) { | ||
| 1457 | DRM_ERROR("out of buf bound.\n"); | ||
| 1458 | goto err_property; | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | /* check for crop */ | ||
| 1462 | if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { | ||
| 1463 | if (swap) { | ||
| 1464 | if ((pos->h < pp->crop_min.hsize) || | ||
| 1465 | (sz->vsize > pp->crop_max.hsize) || | ||
| 1466 | (pos->w < pp->crop_min.vsize) || | ||
| 1467 | (sz->hsize > pp->crop_max.vsize)) { | ||
| 1468 | DRM_ERROR("out of crop size.\n"); | ||
| 1469 | goto err_property; | ||
| 1470 | } | ||
| 1471 | } else { | ||
| 1472 | if ((pos->w < pp->crop_min.hsize) || | ||
| 1473 | (sz->hsize > pp->crop_max.hsize) || | ||
| 1474 | (pos->h < pp->crop_min.vsize) || | ||
| 1475 | (sz->vsize > pp->crop_max.vsize)) { | ||
| 1476 | DRM_ERROR("out of crop size.\n"); | ||
| 1477 | goto err_property; | ||
| 1478 | } | ||
| 1479 | } | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | /* check for scale */ | ||
| 1483 | if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { | ||
| 1484 | if (swap) { | ||
| 1485 | if ((pos->h < pp->scale_min.hsize) || | ||
| 1486 | (sz->vsize > pp->scale_max.hsize) || | ||
| 1487 | (pos->w < pp->scale_min.vsize) || | ||
| 1488 | (sz->hsize > pp->scale_max.vsize)) { | ||
| 1489 | DRM_ERROR("out of scale size.\n"); | ||
| 1490 | goto err_property; | ||
| 1491 | } | ||
| 1492 | } else { | ||
| 1493 | if ((pos->w < pp->scale_min.hsize) || | ||
| 1494 | (sz->hsize > pp->scale_max.hsize) || | ||
| 1495 | (pos->h < pp->scale_min.vsize) || | ||
| 1496 | (sz->vsize > pp->scale_max.vsize)) { | ||
| 1497 | DRM_ERROR("out of scale size.\n"); | ||
| 1498 | goto err_property; | ||
| 1499 | } | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | return 0; | ||
| 1505 | |||
| 1506 | err_property: | ||
| 1507 | for_each_ipp_ops(i) { | ||
| 1508 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1509 | (property->cmd == IPP_CMD_WB)) | ||
| 1510 | continue; | ||
| 1511 | |||
| 1512 | config = &property->config[i]; | ||
| 1513 | pos = &config->pos; | ||
| 1514 | sz = &config->sz; | ||
| 1515 | |||
| 1516 | DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", | ||
| 1517 | i ? "dst" : "src", config->flip, config->degree, | ||
| 1518 | pos->x, pos->y, pos->w, pos->h, | ||
| 1519 | sz->hsize, sz->vsize); | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | return -EINVAL; | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | |||
| 1526 | static int gsc_ippdrv_reset(struct device *dev) | ||
| 1527 | { | ||
| 1528 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1529 | struct gsc_scaler *sc = &ctx->sc; | ||
| 1530 | int ret; | ||
| 1531 | |||
| 1532 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1533 | |||
| 1534 | /* reset h/w block */ | ||
| 1535 | ret = gsc_sw_reset(ctx); | ||
| 1536 | if (ret < 0) { | ||
| 1537 | dev_err(dev, "failed to reset hardware.\n"); | ||
| 1538 | return ret; | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | /* scaler setting */ | ||
| 1542 | memset(&ctx->sc, 0x0, sizeof(ctx->sc)); | ||
| 1543 | sc->range = true; | ||
| 1544 | |||
| 1545 | return 0; | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | ||
| 1549 | { | ||
| 1550 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1551 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1552 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 1553 | struct drm_exynos_ipp_property *property; | ||
| 1554 | struct drm_exynos_ipp_config *config; | ||
| 1555 | struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; | ||
| 1556 | struct drm_exynos_ipp_set_wb set_wb; | ||
| 1557 | u32 cfg; | ||
| 1558 | int ret, i; | ||
| 1559 | |||
| 1560 | DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); | ||
| 1561 | |||
| 1562 | if (!c_node) { | ||
| 1563 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1564 | return -EINVAL; | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | property = &c_node->property; | ||
| 1568 | if (!property) { | ||
| 1569 | DRM_ERROR("failed to get property.\n"); | ||
| 1570 | return -EINVAL; | ||
| 1571 | } | ||
| 1572 | |||
| 1573 | gsc_handle_irq(ctx, true, false, true); | ||
| 1574 | |||
| 1575 | for_each_ipp_ops(i) { | ||
| 1576 | config = &property->config[i]; | ||
| 1577 | img_pos[i] = config->pos; | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | switch (cmd) { | ||
| 1581 | case IPP_CMD_M2M: | ||
| 1582 | /* enable one shot */ | ||
| 1583 | cfg = gsc_read(GSC_ENABLE); | ||
| 1584 | cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK | | ||
| 1585 | GSC_ENABLE_CLK_GATE_MODE_MASK); | ||
| 1586 | cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT; | ||
| 1587 | gsc_write(cfg, GSC_ENABLE); | ||
| 1588 | |||
| 1589 | /* src dma memory */ | ||
| 1590 | cfg = gsc_read(GSC_IN_CON); | ||
| 1591 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); | ||
| 1592 | cfg |= GSC_IN_PATH_MEMORY; | ||
| 1593 | gsc_write(cfg, GSC_IN_CON); | ||
| 1594 | |||
| 1595 | /* dst dma memory */ | ||
| 1596 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1597 | cfg |= GSC_OUT_PATH_MEMORY; | ||
| 1598 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1599 | break; | ||
| 1600 | case IPP_CMD_WB: | ||
| 1601 | set_wb.enable = 1; | ||
| 1602 | set_wb.refresh = property->refresh_rate; | ||
| 1603 | gsc_set_gscblk_fimd_wb(ctx, set_wb.enable); | ||
| 1604 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1605 | |||
| 1606 | /* src local path */ | ||
| 1607 | cfg = readl(GSC_IN_CON); | ||
| 1608 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); | ||
| 1609 | cfg |= (GSC_IN_PATH_LOCAL | GSC_IN_LOCAL_FIMD_WB); | ||
| 1610 | gsc_write(cfg, GSC_IN_CON); | ||
| 1611 | |||
| 1612 | /* dst dma memory */ | ||
| 1613 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1614 | cfg |= GSC_OUT_PATH_MEMORY; | ||
| 1615 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1616 | break; | ||
| 1617 | case IPP_CMD_OUTPUT: | ||
| 1618 | /* src dma memory */ | ||
| 1619 | cfg = gsc_read(GSC_IN_CON); | ||
| 1620 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); | ||
| 1621 | cfg |= GSC_IN_PATH_MEMORY; | ||
| 1622 | gsc_write(cfg, GSC_IN_CON); | ||
| 1623 | |||
| 1624 | /* dst local path */ | ||
| 1625 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1626 | cfg |= GSC_OUT_PATH_MEMORY; | ||
| 1627 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1628 | break; | ||
| 1629 | default: | ||
| 1630 | ret = -EINVAL; | ||
| 1631 | dev_err(dev, "invalid operations.\n"); | ||
| 1632 | return ret; | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | ret = gsc_set_prescaler(ctx, &ctx->sc, | ||
| 1636 | &img_pos[EXYNOS_DRM_OPS_SRC], | ||
| 1637 | &img_pos[EXYNOS_DRM_OPS_DST]); | ||
| 1638 | if (ret) { | ||
| 1639 | dev_err(dev, "failed to set precalser.\n"); | ||
| 1640 | return ret; | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | gsc_set_scaler(ctx, &ctx->sc); | ||
| 1644 | |||
| 1645 | cfg = gsc_read(GSC_ENABLE); | ||
| 1646 | cfg |= GSC_ENABLE_ON; | ||
| 1647 | gsc_write(cfg, GSC_ENABLE); | ||
| 1648 | |||
| 1649 | return 0; | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) | ||
| 1653 | { | ||
| 1654 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1655 | struct drm_exynos_ipp_set_wb set_wb = {0, 0}; | ||
| 1656 | u32 cfg; | ||
| 1657 | |||
| 1658 | DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); | ||
| 1659 | |||
| 1660 | switch (cmd) { | ||
| 1661 | case IPP_CMD_M2M: | ||
| 1662 | /* bypass */ | ||
| 1663 | break; | ||
| 1664 | case IPP_CMD_WB: | ||
| 1665 | gsc_set_gscblk_fimd_wb(ctx, set_wb.enable); | ||
| 1666 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1667 | break; | ||
| 1668 | case IPP_CMD_OUTPUT: | ||
| 1669 | default: | ||
| 1670 | dev_err(dev, "invalid operations.\n"); | ||
| 1671 | break; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | gsc_handle_irq(ctx, false, false, true); | ||
| 1675 | |||
| 1676 | /* reset sequence */ | ||
| 1677 | gsc_write(0xff, GSC_OUT_BASE_ADDR_Y_MASK); | ||
| 1678 | gsc_write(0xff, GSC_OUT_BASE_ADDR_CB_MASK); | ||
| 1679 | gsc_write(0xff, GSC_OUT_BASE_ADDR_CR_MASK); | ||
| 1680 | |||
| 1681 | cfg = gsc_read(GSC_ENABLE); | ||
| 1682 | cfg &= ~GSC_ENABLE_ON; | ||
| 1683 | gsc_write(cfg, GSC_ENABLE); | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | static int __devinit gsc_probe(struct platform_device *pdev) | ||
| 1687 | { | ||
| 1688 | struct device *dev = &pdev->dev; | ||
| 1689 | struct gsc_context *ctx; | ||
| 1690 | struct resource *res; | ||
| 1691 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1692 | int ret; | ||
| 1693 | |||
| 1694 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | ||
| 1695 | if (!ctx) | ||
| 1696 | return -ENOMEM; | ||
| 1697 | |||
| 1698 | /* clock control */ | ||
| 1699 | ctx->gsc_clk = clk_get(dev, "gscl"); | ||
| 1700 | if (IS_ERR(ctx->gsc_clk)) { | ||
| 1701 | dev_err(dev, "failed to get gsc clock.\n"); | ||
| 1702 | ret = PTR_ERR(ctx->gsc_clk); | ||
| 1703 | goto err_ctx; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | /* resource memory */ | ||
| 1707 | ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 1708 | if (!ctx->regs_res) { | ||
| 1709 | dev_err(dev, "failed to find registers.\n"); | ||
| 1710 | ret = -ENOENT; | ||
| 1711 | goto err_clk; | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); | ||
| 1715 | if (!ctx->regs) { | ||
| 1716 | dev_err(dev, "failed to map registers.\n"); | ||
| 1717 | ret = -ENXIO; | ||
| 1718 | goto err_clk; | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | /* resource irq */ | ||
| 1722 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
| 1723 | if (!res) { | ||
| 1724 | dev_err(dev, "failed to request irq resource.\n"); | ||
| 1725 | ret = -ENOENT; | ||
| 1726 | goto err_get_regs; | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | ctx->irq = res->start; | ||
| 1730 | ret = request_threaded_irq(ctx->irq, NULL, gsc_irq_handler, | ||
| 1731 | IRQF_ONESHOT, "drm_gsc", ctx); | ||
| 1732 | if (ret < 0) { | ||
| 1733 | dev_err(dev, "failed to request irq.\n"); | ||
| 1734 | goto err_get_regs; | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | /* context initailization */ | ||
| 1738 | ctx->id = pdev->id; | ||
| 1739 | |||
| 1740 | ippdrv = &ctx->ippdrv; | ||
| 1741 | ippdrv->dev = dev; | ||
| 1742 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &gsc_src_ops; | ||
| 1743 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &gsc_dst_ops; | ||
| 1744 | ippdrv->check_property = gsc_ippdrv_check_property; | ||
| 1745 | ippdrv->reset = gsc_ippdrv_reset; | ||
| 1746 | ippdrv->start = gsc_ippdrv_start; | ||
| 1747 | ippdrv->stop = gsc_ippdrv_stop; | ||
| 1748 | ret = gsc_init_prop_list(ippdrv); | ||
| 1749 | if (ret < 0) { | ||
| 1750 | dev_err(dev, "failed to init property list.\n"); | ||
| 1751 | goto err_get_irq; | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, | ||
| 1755 | (int)ippdrv); | ||
| 1756 | |||
| 1757 | mutex_init(&ctx->lock); | ||
| 1758 | platform_set_drvdata(pdev, ctx); | ||
| 1759 | |||
| 1760 | pm_runtime_set_active(dev); | ||
| 1761 | pm_runtime_enable(dev); | ||
| 1762 | |||
| 1763 | ret = exynos_drm_ippdrv_register(ippdrv); | ||
| 1764 | if (ret < 0) { | ||
| 1765 | dev_err(dev, "failed to register drm gsc device.\n"); | ||
| 1766 | goto err_ippdrv_register; | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | dev_info(&pdev->dev, "drm gsc registered successfully.\n"); | ||
| 1770 | |||
| 1771 | return 0; | ||
| 1772 | |||
| 1773 | err_ippdrv_register: | ||
| 1774 | devm_kfree(dev, ippdrv->prop_list); | ||
| 1775 | pm_runtime_disable(dev); | ||
| 1776 | err_get_irq: | ||
| 1777 | free_irq(ctx->irq, ctx); | ||
| 1778 | err_get_regs: | ||
| 1779 | devm_iounmap(dev, ctx->regs); | ||
| 1780 | err_clk: | ||
| 1781 | clk_put(ctx->gsc_clk); | ||
| 1782 | err_ctx: | ||
| 1783 | devm_kfree(dev, ctx); | ||
| 1784 | return ret; | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | static int __devexit gsc_remove(struct platform_device *pdev) | ||
| 1788 | { | ||
| 1789 | struct device *dev = &pdev->dev; | ||
| 1790 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1791 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1792 | |||
| 1793 | devm_kfree(dev, ippdrv->prop_list); | ||
| 1794 | exynos_drm_ippdrv_unregister(ippdrv); | ||
| 1795 | mutex_destroy(&ctx->lock); | ||
| 1796 | |||
| 1797 | pm_runtime_set_suspended(dev); | ||
| 1798 | pm_runtime_disable(dev); | ||
| 1799 | |||
| 1800 | free_irq(ctx->irq, ctx); | ||
| 1801 | devm_iounmap(dev, ctx->regs); | ||
| 1802 | |||
| 1803 | clk_put(ctx->gsc_clk); | ||
| 1804 | |||
| 1805 | devm_kfree(dev, ctx); | ||
| 1806 | |||
| 1807 | return 0; | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | #ifdef CONFIG_PM_SLEEP | ||
| 1811 | static int gsc_suspend(struct device *dev) | ||
| 1812 | { | ||
| 1813 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1814 | |||
| 1815 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1816 | |||
| 1817 | if (pm_runtime_suspended(dev)) | ||
| 1818 | return 0; | ||
| 1819 | |||
| 1820 | return gsc_clk_ctrl(ctx, false); | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | static int gsc_resume(struct device *dev) | ||
| 1824 | { | ||
| 1825 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1826 | |||
| 1827 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1828 | |||
| 1829 | if (!pm_runtime_suspended(dev)) | ||
| 1830 | return gsc_clk_ctrl(ctx, true); | ||
| 1831 | |||
| 1832 | return 0; | ||
| 1833 | } | ||
| 1834 | #endif | ||
| 1835 | |||
| 1836 | #ifdef CONFIG_PM_RUNTIME | ||
| 1837 | static int gsc_runtime_suspend(struct device *dev) | ||
| 1838 | { | ||
| 1839 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1840 | |||
| 1841 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); | ||
| 1842 | |||
| 1843 | return gsc_clk_ctrl(ctx, false); | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | static int gsc_runtime_resume(struct device *dev) | ||
| 1847 | { | ||
| 1848 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1849 | |||
| 1850 | DRM_DEBUG_KMS("%s:id[%d]\n", __FILE__, ctx->id); | ||
| 1851 | |||
| 1852 | return gsc_clk_ctrl(ctx, true); | ||
| 1853 | } | ||
| 1854 | #endif | ||
| 1855 | |||
| 1856 | static const struct dev_pm_ops gsc_pm_ops = { | ||
| 1857 | SET_SYSTEM_SLEEP_PM_OPS(gsc_suspend, gsc_resume) | ||
| 1858 | SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) | ||
| 1859 | }; | ||
| 1860 | |||
| 1861 | struct platform_driver gsc_driver = { | ||
| 1862 | .probe = gsc_probe, | ||
| 1863 | .remove = __devexit_p(gsc_remove), | ||
| 1864 | .driver = { | ||
| 1865 | .name = "exynos-drm-gsc", | ||
| 1866 | .owner = THIS_MODULE, | ||
| 1867 | .pm = &gsc_pm_ops, | ||
| 1868 | }, | ||
| 1869 | }; | ||
| 1870 | |||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.h b/drivers/gpu/drm/exynos/exynos_drm_gsc.h new file mode 100644 index 000000000000..b3c3bc618c0f --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 6 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 7 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 8 | * | ||
| 9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 10 | * copy of this software and associated documentation files (the "Software"), | ||
| 11 | * to deal in the Software without restriction, including without limitation | ||
| 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
| 13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
| 14 | * Software is furnished to do so, subject to the following conditions: | ||
| 15 | * | ||
| 16 | * The above copyright notice and this permission notice (including the next | ||
| 17 | * paragraph) shall be included in all copies or substantial portions of the | ||
| 18 | * Software. | ||
| 19 | * | ||
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
| 24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
| 25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
| 26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #ifndef _EXYNOS_DRM_GSC_H_ | ||
| 30 | #define _EXYNOS_DRM_GSC_H_ | ||
| 31 | |||
| 32 | /* | ||
| 33 | * TODO | ||
| 34 | * FIMD output interface notifier callback. | ||
| 35 | * Mixer output interface notifier callback. | ||
| 36 | */ | ||
| 37 | |||
| 38 | #endif /* _EXYNOS_DRM_GSC_H_ */ | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index 2d11e70b601a..55793c46e3c2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c | |||
| @@ -29,6 +29,9 @@ | |||
| 29 | #define get_ctx_from_subdrv(subdrv) container_of(subdrv,\ | 29 | #define get_ctx_from_subdrv(subdrv) container_of(subdrv,\ |
| 30 | struct drm_hdmi_context, subdrv); | 30 | struct drm_hdmi_context, subdrv); |
| 31 | 31 | ||
| 32 | /* platform device pointer for common drm hdmi device. */ | ||
| 33 | static struct platform_device *exynos_drm_hdmi_pdev; | ||
| 34 | |||
| 32 | /* Common hdmi subdrv needs to access the hdmi and mixer though context. | 35 | /* Common hdmi subdrv needs to access the hdmi and mixer though context. |
| 33 | * These should be initialied by the repective drivers */ | 36 | * These should be initialied by the repective drivers */ |
| 34 | static struct exynos_drm_hdmi_context *hdmi_ctx; | 37 | static struct exynos_drm_hdmi_context *hdmi_ctx; |
| @@ -46,6 +49,25 @@ struct drm_hdmi_context { | |||
| 46 | bool enabled[MIXER_WIN_NR]; | 49 | bool enabled[MIXER_WIN_NR]; |
| 47 | }; | 50 | }; |
| 48 | 51 | ||
| 52 | int exynos_platform_device_hdmi_register(void) | ||
| 53 | { | ||
| 54 | if (exynos_drm_hdmi_pdev) | ||
| 55 | return -EEXIST; | ||
| 56 | |||
| 57 | exynos_drm_hdmi_pdev = platform_device_register_simple( | ||
| 58 | "exynos-drm-hdmi", -1, NULL, 0); | ||
| 59 | if (IS_ERR_OR_NULL(exynos_drm_hdmi_pdev)) | ||
| 60 | return PTR_ERR(exynos_drm_hdmi_pdev); | ||
| 61 | |||
| 62 | return 0; | ||
| 63 | } | ||
| 64 | |||
| 65 | void exynos_platform_device_hdmi_unregister(void) | ||
| 66 | { | ||
| 67 | if (exynos_drm_hdmi_pdev) | ||
| 68 | platform_device_unregister(exynos_drm_hdmi_pdev); | ||
| 69 | } | ||
| 70 | |||
| 49 | void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx) | 71 | void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx) |
| 50 | { | 72 | { |
| 51 | if (ctx) | 73 | if (ctx) |
| @@ -157,6 +179,16 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev) | |||
| 157 | return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); | 179 | return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); |
| 158 | } | 180 | } |
| 159 | 181 | ||
| 182 | static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev) | ||
| 183 | { | ||
| 184 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
| 185 | |||
| 186 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
| 187 | |||
| 188 | if (mixer_ops && mixer_ops->wait_for_vblank) | ||
| 189 | mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); | ||
| 190 | } | ||
| 191 | |||
| 160 | static void drm_hdmi_mode_fixup(struct device *subdrv_dev, | 192 | static void drm_hdmi_mode_fixup(struct device *subdrv_dev, |
| 161 | struct drm_connector *connector, | 193 | struct drm_connector *connector, |
| 162 | const struct drm_display_mode *mode, | 194 | const struct drm_display_mode *mode, |
| @@ -238,6 +270,7 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { | |||
| 238 | .apply = drm_hdmi_apply, | 270 | .apply = drm_hdmi_apply, |
| 239 | .enable_vblank = drm_hdmi_enable_vblank, | 271 | .enable_vblank = drm_hdmi_enable_vblank, |
| 240 | .disable_vblank = drm_hdmi_disable_vblank, | 272 | .disable_vblank = drm_hdmi_disable_vblank, |
| 273 | .wait_for_vblank = drm_hdmi_wait_for_vblank, | ||
| 241 | .mode_fixup = drm_hdmi_mode_fixup, | 274 | .mode_fixup = drm_hdmi_mode_fixup, |
| 242 | .mode_set = drm_hdmi_mode_set, | 275 | .mode_set = drm_hdmi_mode_set, |
| 243 | .get_max_resol = drm_hdmi_get_max_resol, | 276 | .get_max_resol = drm_hdmi_get_max_resol, |
| @@ -291,21 +324,10 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos) | |||
| 291 | ctx->enabled[win] = false; | 324 | ctx->enabled[win] = false; |
| 292 | } | 325 | } |
| 293 | 326 | ||
| 294 | static void drm_mixer_wait_for_vblank(struct device *subdrv_dev) | ||
| 295 | { | ||
| 296 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
| 297 | |||
| 298 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
| 299 | |||
| 300 | if (mixer_ops && mixer_ops->wait_for_vblank) | ||
| 301 | mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); | ||
| 302 | } | ||
| 303 | |||
| 304 | static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { | 327 | static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { |
| 305 | .mode_set = drm_mixer_mode_set, | 328 | .mode_set = drm_mixer_mode_set, |
| 306 | .commit = drm_mixer_commit, | 329 | .commit = drm_mixer_commit, |
| 307 | .disable = drm_mixer_disable, | 330 | .disable = drm_mixer_disable, |
| 308 | .wait_for_vblank = drm_mixer_wait_for_vblank, | ||
| 309 | }; | 331 | }; |
| 310 | 332 | ||
| 311 | static struct exynos_drm_manager hdmi_manager = { | 333 | static struct exynos_drm_manager hdmi_manager = { |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index 54b522353e48..fcc3093ec8fe 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h | |||
| @@ -65,10 +65,10 @@ struct exynos_mixer_ops { | |||
| 65 | int (*iommu_on)(void *ctx, bool enable); | 65 | int (*iommu_on)(void *ctx, bool enable); |
| 66 | int (*enable_vblank)(void *ctx, int pipe); | 66 | int (*enable_vblank)(void *ctx, int pipe); |
| 67 | void (*disable_vblank)(void *ctx); | 67 | void (*disable_vblank)(void *ctx); |
| 68 | void (*wait_for_vblank)(void *ctx); | ||
| 68 | void (*dpms)(void *ctx, int mode); | 69 | void (*dpms)(void *ctx, int mode); |
| 69 | 70 | ||
| 70 | /* overlay */ | 71 | /* overlay */ |
| 71 | void (*wait_for_vblank)(void *ctx); | ||
| 72 | void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); | 72 | void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); |
| 73 | void (*win_commit)(void *ctx, int zpos); | 73 | void (*win_commit)(void *ctx, int zpos); |
| 74 | void (*win_disable)(void *ctx, int zpos); | 74 | void (*win_disable)(void *ctx, int zpos); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index 09db1983eb1a..2482b7f96341 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c | |||
| @@ -56,8 +56,8 @@ int drm_create_iommu_mapping(struct drm_device *drm_dev) | |||
| 56 | mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start, | 56 | mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start, |
| 57 | priv->da_space_size, | 57 | priv->da_space_size, |
| 58 | priv->da_space_order); | 58 | priv->da_space_order); |
| 59 | if (!mapping) | 59 | if (IS_ERR(mapping)) |
| 60 | return -ENOMEM; | 60 | return PTR_ERR(mapping); |
| 61 | 61 | ||
| 62 | dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), | 62 | dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), |
| 63 | GFP_KERNEL); | 63 | GFP_KERNEL); |
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 000000000000..49eebe948ed2 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c | |||
| @@ -0,0 +1,2060 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
| 3 | * Authors: | ||
| 4 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 5 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 6 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/types.h> | ||
| 18 | #include <linux/clk.h> | ||
| 19 | #include <linux/pm_runtime.h> | ||
| 20 | #include <plat/map-base.h> | ||
| 21 | |||
| 22 | #include <drm/drmP.h> | ||
| 23 | #include <drm/exynos_drm.h> | ||
| 24 | #include "exynos_drm_drv.h" | ||
| 25 | #include "exynos_drm_gem.h" | ||
| 26 | #include "exynos_drm_ipp.h" | ||
| 27 | #include "exynos_drm_iommu.h" | ||
| 28 | |||
| 29 | /* | ||
| 30 | * IPP is stand for Image Post Processing and | ||
| 31 | * supports image scaler/rotator and input/output DMA operations. | ||
| 32 | * using FIMC, GSC, Rotator, so on. | ||
| 33 | * IPP is integration device driver of same attribute h/w | ||
| 34 | */ | ||
| 35 | |||
| 36 | /* | ||
| 37 | * TODO | ||
| 38 | * 1. expand command control id. | ||
| 39 | * 2. integrate property and config. | ||
| 40 | * 3. removed send_event id check routine. | ||
| 41 | * 4. compare send_event id if needed. | ||
| 42 | * 5. free subdrv_remove notifier callback list if needed. | ||
| 43 | * 6. need to check subdrv_open about multi-open. | ||
| 44 | * 7. need to power_on implement power and sysmmu ctrl. | ||
| 45 | */ | ||
| 46 | |||
| 47 | #define get_ipp_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
| 48 | #define ipp_is_m2m_cmd(c) (c == IPP_CMD_M2M) | ||
| 49 | |||
| 50 | /* | ||
| 51 | * A structure of event. | ||
| 52 | * | ||
| 53 | * @base: base of event. | ||
| 54 | * @event: ipp event. | ||
| 55 | */ | ||
| 56 | struct drm_exynos_ipp_send_event { | ||
| 57 | struct drm_pending_event base; | ||
| 58 | struct drm_exynos_ipp_event event; | ||
| 59 | }; | ||
| 60 | |||
| 61 | /* | ||
| 62 | * A structure of memory node. | ||
| 63 | * | ||
| 64 | * @list: list head to memory queue information. | ||
| 65 | * @ops_id: id of operations. | ||
| 66 | * @prop_id: id of property. | ||
| 67 | * @buf_id: id of buffer. | ||
| 68 | * @buf_info: gem objects and dma address, size. | ||
| 69 | * @filp: a pointer to drm_file. | ||
| 70 | */ | ||
| 71 | struct drm_exynos_ipp_mem_node { | ||
| 72 | struct list_head list; | ||
| 73 | enum drm_exynos_ops_id ops_id; | ||
| 74 | u32 prop_id; | ||
| 75 | u32 buf_id; | ||
| 76 | struct drm_exynos_ipp_buf_info buf_info; | ||
| 77 | struct drm_file *filp; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /* | ||
| 81 | * A structure of ipp context. | ||
| 82 | * | ||
| 83 | * @subdrv: prepare initialization using subdrv. | ||
| 84 | * @ipp_lock: lock for synchronization of access to ipp_idr. | ||
| 85 | * @prop_lock: lock for synchronization of access to prop_idr. | ||
| 86 | * @ipp_idr: ipp driver idr. | ||
| 87 | * @prop_idr: property idr. | ||
| 88 | * @event_workq: event work queue. | ||
| 89 | * @cmd_workq: command work queue. | ||
| 90 | */ | ||
| 91 | struct ipp_context { | ||
| 92 | struct exynos_drm_subdrv subdrv; | ||
| 93 | struct mutex ipp_lock; | ||
| 94 | struct mutex prop_lock; | ||
| 95 | struct idr ipp_idr; | ||
| 96 | struct idr prop_idr; | ||
| 97 | struct workqueue_struct *event_workq; | ||
| 98 | struct workqueue_struct *cmd_workq; | ||
| 99 | }; | ||
| 100 | |||
| 101 | static LIST_HEAD(exynos_drm_ippdrv_list); | ||
| 102 | static DEFINE_MUTEX(exynos_drm_ippdrv_lock); | ||
| 103 | static BLOCKING_NOTIFIER_HEAD(exynos_drm_ippnb_list); | ||
| 104 | |||
| 105 | int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) | ||
| 106 | { | ||
| 107 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 108 | |||
| 109 | if (!ippdrv) | ||
| 110 | return -EINVAL; | ||
| 111 | |||
| 112 | mutex_lock(&exynos_drm_ippdrv_lock); | ||
| 113 | list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list); | ||
| 114 | mutex_unlock(&exynos_drm_ippdrv_lock); | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) | ||
| 120 | { | ||
| 121 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 122 | |||
| 123 | if (!ippdrv) | ||
| 124 | return -EINVAL; | ||
| 125 | |||
| 126 | mutex_lock(&exynos_drm_ippdrv_lock); | ||
| 127 | list_del(&ippdrv->drv_list); | ||
| 128 | mutex_unlock(&exynos_drm_ippdrv_lock); | ||
| 129 | |||
| 130 | return 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, | ||
| 134 | u32 *idp) | ||
| 135 | { | ||
| 136 | int ret; | ||
| 137 | |||
| 138 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 139 | |||
| 140 | again: | ||
| 141 | /* ensure there is space available to allocate a handle */ | ||
| 142 | if (idr_pre_get(id_idr, GFP_KERNEL) == 0) { | ||
| 143 | DRM_ERROR("failed to get idr.\n"); | ||
| 144 | return -ENOMEM; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* do the allocation under our mutexlock */ | ||
| 148 | mutex_lock(lock); | ||
| 149 | ret = idr_get_new_above(id_idr, obj, 1, (int *)idp); | ||
| 150 | mutex_unlock(lock); | ||
| 151 | if (ret == -EAGAIN) | ||
| 152 | goto again; | ||
| 153 | |||
| 154 | return ret; | ||
| 155 | } | ||
| 156 | |||
| 157 | static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) | ||
| 158 | { | ||
| 159 | void *obj; | ||
| 160 | |||
| 161 | DRM_DEBUG_KMS("%s:id[%d]\n", __func__, id); | ||
| 162 | |||
| 163 | mutex_lock(lock); | ||
| 164 | |||
| 165 | /* find object using handle */ | ||
| 166 | obj = idr_find(id_idr, id); | ||
| 167 | if (!obj) { | ||
| 168 | DRM_ERROR("failed to find object.\n"); | ||
| 169 | mutex_unlock(lock); | ||
| 170 | return ERR_PTR(-ENODEV); | ||
| 171 | } | ||
| 172 | |||
| 173 | mutex_unlock(lock); | ||
| 174 | |||
| 175 | return obj; | ||
| 176 | } | ||
| 177 | |||
| 178 | static inline bool ipp_check_dedicated(struct exynos_drm_ippdrv *ippdrv, | ||
| 179 | enum drm_exynos_ipp_cmd cmd) | ||
| 180 | { | ||
| 181 | /* | ||
| 182 | * check dedicated flag and WB, OUTPUT operation with | ||
| 183 | * power on state. | ||
| 184 | */ | ||
| 185 | if (ippdrv->dedicated || (!ipp_is_m2m_cmd(cmd) && | ||
| 186 | !pm_runtime_suspended(ippdrv->dev))) | ||
| 187 | return true; | ||
| 188 | |||
| 189 | return false; | ||
| 190 | } | ||
| 191 | |||
| 192 | static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, | ||
| 193 | struct drm_exynos_ipp_property *property) | ||
| 194 | { | ||
| 195 | struct exynos_drm_ippdrv *ippdrv; | ||
| 196 | u32 ipp_id = property->ipp_id; | ||
| 197 | |||
| 198 | DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, ipp_id); | ||
| 199 | |||
| 200 | if (ipp_id) { | ||
| 201 | /* find ipp driver using idr */ | ||
| 202 | ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, | ||
| 203 | ipp_id); | ||
| 204 | if (IS_ERR_OR_NULL(ippdrv)) { | ||
| 205 | DRM_ERROR("not found ipp%d driver.\n", ipp_id); | ||
| 206 | return ippdrv; | ||
| 207 | } | ||
| 208 | |||
| 209 | /* | ||
| 210 | * WB, OUTPUT opertion not supported multi-operation. | ||
| 211 | * so, make dedicated state at set property ioctl. | ||
| 212 | * when ipp driver finished operations, clear dedicated flags. | ||
| 213 | */ | ||
| 214 | if (ipp_check_dedicated(ippdrv, property->cmd)) { | ||
| 215 | DRM_ERROR("already used choose device.\n"); | ||
| 216 | return ERR_PTR(-EBUSY); | ||
| 217 | } | ||
| 218 | |||
| 219 | /* | ||
| 220 | * This is necessary to find correct device in ipp drivers. | ||
| 221 | * ipp drivers have different abilities, | ||
| 222 | * so need to check property. | ||
| 223 | */ | ||
| 224 | if (ippdrv->check_property && | ||
| 225 | ippdrv->check_property(ippdrv->dev, property)) { | ||
| 226 | DRM_ERROR("not support property.\n"); | ||
| 227 | return ERR_PTR(-EINVAL); | ||
| 228 | } | ||
| 229 | |||
| 230 | return ippdrv; | ||
| 231 | } else { | ||
| 232 | /* | ||
| 233 | * This case is search all ipp driver for finding. | ||
| 234 | * user application don't set ipp_id in this case, | ||
| 235 | * so ipp subsystem search correct driver in driver list. | ||
| 236 | */ | ||
| 237 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
| 238 | if (ipp_check_dedicated(ippdrv, property->cmd)) { | ||
| 239 | DRM_DEBUG_KMS("%s:used device.\n", __func__); | ||
| 240 | continue; | ||
| 241 | } | ||
| 242 | |||
| 243 | if (ippdrv->check_property && | ||
| 244 | ippdrv->check_property(ippdrv->dev, property)) { | ||
| 245 | DRM_DEBUG_KMS("%s:not support property.\n", | ||
| 246 | __func__); | ||
| 247 | continue; | ||
| 248 | } | ||
| 249 | |||
| 250 | return ippdrv; | ||
| 251 | } | ||
| 252 | |||
| 253 | DRM_ERROR("not support ipp driver operations.\n"); | ||
| 254 | } | ||
| 255 | |||
| 256 | return ERR_PTR(-ENODEV); | ||
| 257 | } | ||
| 258 | |||
| 259 | static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) | ||
| 260 | { | ||
| 261 | struct exynos_drm_ippdrv *ippdrv; | ||
| 262 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 263 | int count = 0; | ||
| 264 | |||
| 265 | DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); | ||
| 266 | |||
| 267 | if (list_empty(&exynos_drm_ippdrv_list)) { | ||
| 268 | DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__); | ||
| 269 | return ERR_PTR(-ENODEV); | ||
| 270 | } | ||
| 271 | |||
| 272 | /* | ||
| 273 | * This case is search ipp driver by prop_id handle. | ||
| 274 | * sometimes, ipp subsystem find driver by prop_id. | ||
| 275 | * e.g PAUSE state, queue buf, command contro. | ||
| 276 | */ | ||
| 277 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
| 278 | DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", __func__, | ||
| 279 | count++, (int)ippdrv); | ||
| 280 | |||
| 281 | if (!list_empty(&ippdrv->cmd_list)) { | ||
| 282 | list_for_each_entry(c_node, &ippdrv->cmd_list, list) | ||
| 283 | if (c_node->property.prop_id == prop_id) | ||
| 284 | return ippdrv; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | return ERR_PTR(-ENODEV); | ||
| 289 | } | ||
| 290 | |||
| 291 | int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, | ||
| 292 | struct drm_file *file) | ||
| 293 | { | ||
| 294 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
| 295 | struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; | ||
| 296 | struct device *dev = priv->dev; | ||
| 297 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 298 | struct drm_exynos_ipp_prop_list *prop_list = data; | ||
| 299 | struct exynos_drm_ippdrv *ippdrv; | ||
| 300 | int count = 0; | ||
| 301 | |||
| 302 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 303 | |||
| 304 | if (!ctx) { | ||
| 305 | DRM_ERROR("invalid context.\n"); | ||
| 306 | return -EINVAL; | ||
| 307 | } | ||
| 308 | |||
| 309 | if (!prop_list) { | ||
| 310 | DRM_ERROR("invalid property parameter.\n"); | ||
| 311 | return -EINVAL; | ||
| 312 | } | ||
| 313 | |||
| 314 | DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, prop_list->ipp_id); | ||
| 315 | |||
| 316 | if (!prop_list->ipp_id) { | ||
| 317 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) | ||
| 318 | count++; | ||
| 319 | /* | ||
| 320 | * Supports ippdrv list count for user application. | ||
| 321 | * First step user application getting ippdrv count. | ||
| 322 | * and second step getting ippdrv capability using ipp_id. | ||
| 323 | */ | ||
| 324 | prop_list->count = count; | ||
| 325 | } else { | ||
| 326 | /* | ||
| 327 | * Getting ippdrv capability by ipp_id. | ||
| 328 | * some deivce not supported wb, output interface. | ||
| 329 | * so, user application detect correct ipp driver | ||
| 330 | * using this ioctl. | ||
| 331 | */ | ||
| 332 | ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, | ||
| 333 | prop_list->ipp_id); | ||
| 334 | if (!ippdrv) { | ||
| 335 | DRM_ERROR("not found ipp%d driver.\n", | ||
| 336 | prop_list->ipp_id); | ||
| 337 | return -EINVAL; | ||
| 338 | } | ||
| 339 | |||
| 340 | prop_list = ippdrv->prop_list; | ||
| 341 | } | ||
| 342 | |||
| 343 | return 0; | ||
| 344 | } | ||
| 345 | |||
| 346 | static void ipp_print_property(struct drm_exynos_ipp_property *property, | ||
| 347 | int idx) | ||
| 348 | { | ||
| 349 | struct drm_exynos_ipp_config *config = &property->config[idx]; | ||
| 350 | struct drm_exynos_pos *pos = &config->pos; | ||
| 351 | struct drm_exynos_sz *sz = &config->sz; | ||
| 352 | |||
| 353 | DRM_DEBUG_KMS("%s:prop_id[%d]ops[%s]fmt[0x%x]\n", | ||
| 354 | __func__, property->prop_id, idx ? "dst" : "src", config->fmt); | ||
| 355 | |||
| 356 | DRM_DEBUG_KMS("%s:pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n", | ||
| 357 | __func__, pos->x, pos->y, pos->w, pos->h, | ||
| 358 | sz->hsize, sz->vsize, config->flip, config->degree); | ||
| 359 | } | ||
| 360 | |||
| 361 | static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) | ||
| 362 | { | ||
| 363 | struct exynos_drm_ippdrv *ippdrv; | ||
| 364 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 365 | u32 prop_id = property->prop_id; | ||
| 366 | |||
| 367 | DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); | ||
| 368 | |||
| 369 | ippdrv = ipp_find_drv_by_handle(prop_id); | ||
| 370 | if (IS_ERR_OR_NULL(ippdrv)) { | ||
| 371 | DRM_ERROR("failed to get ipp driver.\n"); | ||
| 372 | return -EINVAL; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* | ||
| 376 | * Find command node using command list in ippdrv. | ||
| 377 | * when we find this command no using prop_id. | ||
| 378 | * return property information set in this command node. | ||
| 379 | */ | ||
| 380 | list_for_each_entry(c_node, &ippdrv->cmd_list, list) { | ||
| 381 | if ((c_node->property.prop_id == prop_id) && | ||
| 382 | (c_node->state == IPP_STATE_STOP)) { | ||
| 383 | DRM_DEBUG_KMS("%s:found cmd[%d]ippdrv[0x%x]\n", | ||
| 384 | __func__, property->cmd, (int)ippdrv); | ||
| 385 | |||
| 386 | c_node->property = *property; | ||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | DRM_ERROR("failed to search property.\n"); | ||
| 392 | |||
| 393 | return -EINVAL; | ||
| 394 | } | ||
| 395 | |||
| 396 | static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void) | ||
| 397 | { | ||
| 398 | struct drm_exynos_ipp_cmd_work *cmd_work; | ||
| 399 | |||
| 400 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 401 | |||
| 402 | cmd_work = kzalloc(sizeof(*cmd_work), GFP_KERNEL); | ||
| 403 | if (!cmd_work) { | ||
| 404 | DRM_ERROR("failed to alloc cmd_work.\n"); | ||
| 405 | return ERR_PTR(-ENOMEM); | ||
| 406 | } | ||
| 407 | |||
| 408 | INIT_WORK((struct work_struct *)cmd_work, ipp_sched_cmd); | ||
| 409 | |||
| 410 | return cmd_work; | ||
| 411 | } | ||
| 412 | |||
| 413 | static struct drm_exynos_ipp_event_work *ipp_create_event_work(void) | ||
| 414 | { | ||
| 415 | struct drm_exynos_ipp_event_work *event_work; | ||
| 416 | |||
| 417 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 418 | |||
| 419 | event_work = kzalloc(sizeof(*event_work), GFP_KERNEL); | ||
| 420 | if (!event_work) { | ||
| 421 | DRM_ERROR("failed to alloc event_work.\n"); | ||
| 422 | return ERR_PTR(-ENOMEM); | ||
| 423 | } | ||
| 424 | |||
| 425 | INIT_WORK((struct work_struct *)event_work, ipp_sched_event); | ||
| 426 | |||
| 427 | return event_work; | ||
| 428 | } | ||
| 429 | |||
| 430 | int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, | ||
| 431 | struct drm_file *file) | ||
| 432 | { | ||
| 433 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
| 434 | struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; | ||
| 435 | struct device *dev = priv->dev; | ||
| 436 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 437 | struct drm_exynos_ipp_property *property = data; | ||
| 438 | struct exynos_drm_ippdrv *ippdrv; | ||
| 439 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 440 | int ret, i; | ||
| 441 | |||
| 442 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 443 | |||
| 444 | if (!ctx) { | ||
| 445 | DRM_ERROR("invalid context.\n"); | ||
| 446 | return -EINVAL; | ||
| 447 | } | ||
| 448 | |||
| 449 | if (!property) { | ||
| 450 | DRM_ERROR("invalid property parameter.\n"); | ||
| 451 | return -EINVAL; | ||
| 452 | } | ||
| 453 | |||
| 454 | /* | ||
| 455 | * This is log print for user application property. | ||
| 456 | * user application set various property. | ||
| 457 | */ | ||
| 458 | for_each_ipp_ops(i) | ||
| 459 | ipp_print_property(property, i); | ||
| 460 | |||
| 461 | /* | ||
| 462 | * set property ioctl generated new prop_id. | ||
| 463 | * but in this case already asigned prop_id using old set property. | ||
| 464 | * e.g PAUSE state. this case supports find current prop_id and use it | ||
| 465 | * instead of allocation. | ||
| 466 | */ | ||
| 467 | if (property->prop_id) { | ||
| 468 | DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); | ||
| 469 | return ipp_find_and_set_property(property); | ||
| 470 | } | ||
| 471 | |||
| 472 | /* find ipp driver using ipp id */ | ||
| 473 | ippdrv = ipp_find_driver(ctx, property); | ||
| 474 | if (IS_ERR_OR_NULL(ippdrv)) { | ||
| 475 | DRM_ERROR("failed to get ipp driver.\n"); | ||
| 476 | return -EINVAL; | ||
| 477 | } | ||
| 478 | |||
| 479 | /* allocate command node */ | ||
| 480 | c_node = kzalloc(sizeof(*c_node), GFP_KERNEL); | ||
| 481 | if (!c_node) { | ||
| 482 | DRM_ERROR("failed to allocate map node.\n"); | ||
| 483 | return -ENOMEM; | ||
| 484 | } | ||
| 485 | |||
| 486 | /* create property id */ | ||
| 487 | ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node, | ||
| 488 | &property->prop_id); | ||
| 489 | if (ret) { | ||
| 490 | DRM_ERROR("failed to create id.\n"); | ||
| 491 | goto err_clear; | ||
| 492 | } | ||
| 493 | |||
| 494 | DRM_DEBUG_KMS("%s:created prop_id[%d]cmd[%d]ippdrv[0x%x]\n", | ||
| 495 | __func__, property->prop_id, property->cmd, (int)ippdrv); | ||
| 496 | |||
| 497 | /* stored property information and ippdrv in private data */ | ||
| 498 | c_node->priv = priv; | ||
| 499 | c_node->property = *property; | ||
| 500 | c_node->state = IPP_STATE_IDLE; | ||
| 501 | |||
| 502 | c_node->start_work = ipp_create_cmd_work(); | ||
| 503 | if (IS_ERR_OR_NULL(c_node->start_work)) { | ||
| 504 | DRM_ERROR("failed to create start work.\n"); | ||
| 505 | goto err_clear; | ||
| 506 | } | ||
| 507 | |||
| 508 | c_node->stop_work = ipp_create_cmd_work(); | ||
| 509 | if (IS_ERR_OR_NULL(c_node->stop_work)) { | ||
| 510 | DRM_ERROR("failed to create stop work.\n"); | ||
| 511 | goto err_free_start; | ||
| 512 | } | ||
| 513 | |||
| 514 | c_node->event_work = ipp_create_event_work(); | ||
| 515 | if (IS_ERR_OR_NULL(c_node->event_work)) { | ||
| 516 | DRM_ERROR("failed to create event work.\n"); | ||
| 517 | goto err_free_stop; | ||
| 518 | } | ||
| 519 | |||
| 520 | mutex_init(&c_node->cmd_lock); | ||
| 521 | mutex_init(&c_node->mem_lock); | ||
| 522 | mutex_init(&c_node->event_lock); | ||
| 523 | |||
| 524 | init_completion(&c_node->start_complete); | ||
| 525 | init_completion(&c_node->stop_complete); | ||
| 526 | |||
| 527 | for_each_ipp_ops(i) | ||
| 528 | INIT_LIST_HEAD(&c_node->mem_list[i]); | ||
| 529 | |||
| 530 | INIT_LIST_HEAD(&c_node->event_list); | ||
| 531 | list_splice_init(&priv->event_list, &c_node->event_list); | ||
| 532 | list_add_tail(&c_node->list, &ippdrv->cmd_list); | ||
| 533 | |||
| 534 | /* make dedicated state without m2m */ | ||
| 535 | if (!ipp_is_m2m_cmd(property->cmd)) | ||
| 536 | ippdrv->dedicated = true; | ||
| 537 | |||
| 538 | return 0; | ||
| 539 | |||
| 540 | err_free_stop: | ||
| 541 | kfree(c_node->stop_work); | ||
| 542 | err_free_start: | ||
| 543 | kfree(c_node->start_work); | ||
| 544 | err_clear: | ||
| 545 | kfree(c_node); | ||
| 546 | return ret; | ||
| 547 | } | ||
| 548 | |||
| 549 | static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node) | ||
| 550 | { | ||
| 551 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 552 | |||
| 553 | /* delete list */ | ||
| 554 | list_del(&c_node->list); | ||
| 555 | |||
| 556 | /* destroy mutex */ | ||
| 557 | mutex_destroy(&c_node->cmd_lock); | ||
| 558 | mutex_destroy(&c_node->mem_lock); | ||
| 559 | mutex_destroy(&c_node->event_lock); | ||
| 560 | |||
| 561 | /* free command node */ | ||
| 562 | kfree(c_node->start_work); | ||
| 563 | kfree(c_node->stop_work); | ||
| 564 | kfree(c_node->event_work); | ||
| 565 | kfree(c_node); | ||
| 566 | } | ||
| 567 | |||
| 568 | static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) | ||
| 569 | { | ||
| 570 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
| 571 | struct drm_exynos_ipp_mem_node *m_node; | ||
| 572 | struct list_head *head; | ||
| 573 | int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, }; | ||
| 574 | |||
| 575 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 576 | |||
| 577 | mutex_lock(&c_node->mem_lock); | ||
| 578 | |||
| 579 | for_each_ipp_ops(i) { | ||
| 580 | /* source/destination memory list */ | ||
| 581 | head = &c_node->mem_list[i]; | ||
| 582 | |||
| 583 | if (list_empty(head)) { | ||
| 584 | DRM_DEBUG_KMS("%s:%s memory empty.\n", __func__, | ||
| 585 | i ? "dst" : "src"); | ||
| 586 | continue; | ||
| 587 | } | ||
| 588 | |||
| 589 | /* find memory node entry */ | ||
| 590 | list_for_each_entry(m_node, head, list) { | ||
| 591 | DRM_DEBUG_KMS("%s:%s,count[%d]m_node[0x%x]\n", __func__, | ||
| 592 | i ? "dst" : "src", count[i], (int)m_node); | ||
| 593 | count[i]++; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | DRM_DEBUG_KMS("%s:min[%d]max[%d]\n", __func__, | ||
| 598 | min(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]), | ||
| 599 | max(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST])); | ||
| 600 | |||
| 601 | /* | ||
| 602 | * M2M operations should be need paired memory address. | ||
| 603 | * so, need to check minimum count about src, dst. | ||
| 604 | * other case not use paired memory, so use maximum count | ||
| 605 | */ | ||
| 606 | if (ipp_is_m2m_cmd(property->cmd)) | ||
| 607 | ret = min(count[EXYNOS_DRM_OPS_SRC], | ||
| 608 | count[EXYNOS_DRM_OPS_DST]); | ||
| 609 | else | ||
| 610 | ret = max(count[EXYNOS_DRM_OPS_SRC], | ||
| 611 | count[EXYNOS_DRM_OPS_DST]); | ||
| 612 | |||
| 613 | mutex_unlock(&c_node->mem_lock); | ||
| 614 | |||
| 615 | return ret; | ||
| 616 | } | ||
| 617 | |||
| 618 | static struct drm_exynos_ipp_mem_node | ||
| 619 | *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node, | ||
| 620 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
| 621 | { | ||
| 622 | struct drm_exynos_ipp_mem_node *m_node; | ||
| 623 | struct list_head *head; | ||
| 624 | int count = 0; | ||
| 625 | |||
| 626 | DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, qbuf->buf_id); | ||
| 627 | |||
| 628 | /* source/destination memory list */ | ||
| 629 | head = &c_node->mem_list[qbuf->ops_id]; | ||
| 630 | |||
| 631 | /* find memory node from memory list */ | ||
| 632 | list_for_each_entry(m_node, head, list) { | ||
| 633 | DRM_DEBUG_KMS("%s:count[%d]m_node[0x%x]\n", | ||
| 634 | __func__, count++, (int)m_node); | ||
| 635 | |||
| 636 | /* compare buffer id */ | ||
| 637 | if (m_node->buf_id == qbuf->buf_id) | ||
| 638 | return m_node; | ||
| 639 | } | ||
| 640 | |||
| 641 | return NULL; | ||
| 642 | } | ||
| 643 | |||
| 644 | static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, | ||
| 645 | struct drm_exynos_ipp_cmd_node *c_node, | ||
| 646 | struct drm_exynos_ipp_mem_node *m_node) | ||
| 647 | { | ||
| 648 | struct exynos_drm_ipp_ops *ops = NULL; | ||
| 649 | int ret = 0; | ||
| 650 | |||
| 651 | DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node); | ||
| 652 | |||
| 653 | if (!m_node) { | ||
| 654 | DRM_ERROR("invalid queue node.\n"); | ||
| 655 | return -EFAULT; | ||
| 656 | } | ||
| 657 | |||
| 658 | mutex_lock(&c_node->mem_lock); | ||
| 659 | |||
| 660 | DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id); | ||
| 661 | |||
| 662 | /* get operations callback */ | ||
| 663 | ops = ippdrv->ops[m_node->ops_id]; | ||
| 664 | if (!ops) { | ||
| 665 | DRM_ERROR("not support ops.\n"); | ||
| 666 | ret = -EFAULT; | ||
| 667 | goto err_unlock; | ||
| 668 | } | ||
| 669 | |||
| 670 | /* set address and enable irq */ | ||
| 671 | if (ops->set_addr) { | ||
| 672 | ret = ops->set_addr(ippdrv->dev, &m_node->buf_info, | ||
| 673 | m_node->buf_id, IPP_BUF_ENQUEUE); | ||
| 674 | if (ret) { | ||
| 675 | DRM_ERROR("failed to set addr.\n"); | ||
| 676 | goto err_unlock; | ||
| 677 | } | ||
| 678 | } | ||
| 679 | |||
| 680 | err_unlock: | ||
| 681 | mutex_unlock(&c_node->mem_lock); | ||
| 682 | return ret; | ||
| 683 | } | ||
| 684 | |||
| 685 | static struct drm_exynos_ipp_mem_node | ||
| 686 | *ipp_get_mem_node(struct drm_device *drm_dev, | ||
| 687 | struct drm_file *file, | ||
| 688 | struct drm_exynos_ipp_cmd_node *c_node, | ||
| 689 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
| 690 | { | ||
| 691 | struct drm_exynos_ipp_mem_node *m_node; | ||
| 692 | struct drm_exynos_ipp_buf_info buf_info; | ||
| 693 | void *addr; | ||
| 694 | int i; | ||
| 695 | |||
| 696 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 697 | |||
| 698 | mutex_lock(&c_node->mem_lock); | ||
| 699 | |||
| 700 | m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); | ||
| 701 | if (!m_node) { | ||
| 702 | DRM_ERROR("failed to allocate queue node.\n"); | ||
| 703 | goto err_unlock; | ||
| 704 | } | ||
| 705 | |||
| 706 | /* clear base address for error handling */ | ||
| 707 | memset(&buf_info, 0x0, sizeof(buf_info)); | ||
| 708 | |||
| 709 | /* operations, buffer id */ | ||
| 710 | m_node->ops_id = qbuf->ops_id; | ||
| 711 | m_node->prop_id = qbuf->prop_id; | ||
| 712 | m_node->buf_id = qbuf->buf_id; | ||
| 713 | |||
| 714 | DRM_DEBUG_KMS("%s:m_node[0x%x]ops_id[%d]\n", __func__, | ||
| 715 | (int)m_node, qbuf->ops_id); | ||
| 716 | DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]\n", __func__, | ||
| 717 | qbuf->prop_id, m_node->buf_id); | ||
| 718 | |||
| 719 | for_each_ipp_planar(i) { | ||
| 720 | DRM_DEBUG_KMS("%s:i[%d]handle[0x%x]\n", __func__, | ||
| 721 | i, qbuf->handle[i]); | ||
| 722 | |||
| 723 | /* get dma address by handle */ | ||
| 724 | if (qbuf->handle[i]) { | ||
| 725 | addr = exynos_drm_gem_get_dma_addr(drm_dev, | ||
| 726 | qbuf->handle[i], file); | ||
| 727 | if (IS_ERR(addr)) { | ||
| 728 | DRM_ERROR("failed to get addr.\n"); | ||
| 729 | goto err_clear; | ||
| 730 | } | ||
| 731 | |||
| 732 | buf_info.handles[i] = qbuf->handle[i]; | ||
| 733 | buf_info.base[i] = *(dma_addr_t *) addr; | ||
| 734 | DRM_DEBUG_KMS("%s:i[%d]base[0x%x]hd[0x%x]\n", | ||
| 735 | __func__, i, buf_info.base[i], | ||
| 736 | (int)buf_info.handles[i]); | ||
| 737 | } | ||
| 738 | } | ||
| 739 | |||
| 740 | m_node->filp = file; | ||
| 741 | m_node->buf_info = buf_info; | ||
| 742 | list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); | ||
| 743 | |||
| 744 | mutex_unlock(&c_node->mem_lock); | ||
| 745 | return m_node; | ||
| 746 | |||
| 747 | err_clear: | ||
| 748 | kfree(m_node); | ||
| 749 | err_unlock: | ||
| 750 | mutex_unlock(&c_node->mem_lock); | ||
| 751 | return ERR_PTR(-EFAULT); | ||
| 752 | } | ||
| 753 | |||
| 754 | static int ipp_put_mem_node(struct drm_device *drm_dev, | ||
| 755 | struct drm_exynos_ipp_cmd_node *c_node, | ||
| 756 | struct drm_exynos_ipp_mem_node *m_node) | ||
| 757 | { | ||
| 758 | int i; | ||
| 759 | |||
| 760 | DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node); | ||
| 761 | |||
| 762 | if (!m_node) { | ||
| 763 | DRM_ERROR("invalid dequeue node.\n"); | ||
| 764 | return -EFAULT; | ||
| 765 | } | ||
| 766 | |||
| 767 | if (list_empty(&m_node->list)) { | ||
| 768 | DRM_ERROR("empty memory node.\n"); | ||
| 769 | return -ENOMEM; | ||
| 770 | } | ||
| 771 | |||
| 772 | mutex_lock(&c_node->mem_lock); | ||
| 773 | |||
| 774 | DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id); | ||
| 775 | |||
| 776 | /* put gem buffer */ | ||
| 777 | for_each_ipp_planar(i) { | ||
| 778 | unsigned long handle = m_node->buf_info.handles[i]; | ||
| 779 | if (handle) | ||
| 780 | exynos_drm_gem_put_dma_addr(drm_dev, handle, | ||
| 781 | m_node->filp); | ||
| 782 | } | ||
| 783 | |||
| 784 | /* delete list in queue */ | ||
| 785 | list_del(&m_node->list); | ||
| 786 | kfree(m_node); | ||
| 787 | |||
| 788 | mutex_unlock(&c_node->mem_lock); | ||
| 789 | |||
| 790 | return 0; | ||
| 791 | } | ||
| 792 | |||
| 793 | static void ipp_free_event(struct drm_pending_event *event) | ||
| 794 | { | ||
| 795 | kfree(event); | ||
| 796 | } | ||
| 797 | |||
| 798 | static int ipp_get_event(struct drm_device *drm_dev, | ||
| 799 | struct drm_file *file, | ||
| 800 | struct drm_exynos_ipp_cmd_node *c_node, | ||
| 801 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
| 802 | { | ||
| 803 | struct drm_exynos_ipp_send_event *e; | ||
| 804 | unsigned long flags; | ||
| 805 | |||
| 806 | DRM_DEBUG_KMS("%s:ops_id[%d]buf_id[%d]\n", __func__, | ||
| 807 | qbuf->ops_id, qbuf->buf_id); | ||
| 808 | |||
| 809 | e = kzalloc(sizeof(*e), GFP_KERNEL); | ||
| 810 | |||
| 811 | if (!e) { | ||
| 812 | DRM_ERROR("failed to allocate event.\n"); | ||
| 813 | spin_lock_irqsave(&drm_dev->event_lock, flags); | ||
| 814 | file->event_space += sizeof(e->event); | ||
| 815 | spin_unlock_irqrestore(&drm_dev->event_lock, flags); | ||
| 816 | return -ENOMEM; | ||
| 817 | } | ||
| 818 | |||
| 819 | /* make event */ | ||
| 820 | e->event.base.type = DRM_EXYNOS_IPP_EVENT; | ||
| 821 | e->event.base.length = sizeof(e->event); | ||
| 822 | e->event.user_data = qbuf->user_data; | ||
| 823 | e->event.prop_id = qbuf->prop_id; | ||
| 824 | e->event.buf_id[EXYNOS_DRM_OPS_DST] = qbuf->buf_id; | ||
| 825 | e->base.event = &e->event.base; | ||
| 826 | e->base.file_priv = file; | ||
| 827 | e->base.destroy = ipp_free_event; | ||
| 828 | list_add_tail(&e->base.link, &c_node->event_list); | ||
| 829 | |||
| 830 | return 0; | ||
| 831 | } | ||
| 832 | |||
| 833 | static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, | ||
| 834 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
| 835 | { | ||
| 836 | struct drm_exynos_ipp_send_event *e, *te; | ||
| 837 | int count = 0; | ||
| 838 | |||
| 839 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 840 | |||
| 841 | if (list_empty(&c_node->event_list)) { | ||
| 842 | DRM_DEBUG_KMS("%s:event_list is empty.\n", __func__); | ||
| 843 | return; | ||
| 844 | } | ||
| 845 | |||
| 846 | list_for_each_entry_safe(e, te, &c_node->event_list, base.link) { | ||
| 847 | DRM_DEBUG_KMS("%s:count[%d]e[0x%x]\n", | ||
| 848 | __func__, count++, (int)e); | ||
| 849 | |||
| 850 | /* | ||
| 851 | * quf == NULL condition means all event deletion. | ||
| 852 | * stop operations want to delete all event list. | ||
| 853 | * another case delete only same buf id. | ||
| 854 | */ | ||
| 855 | if (!qbuf) { | ||
| 856 | /* delete list */ | ||
| 857 | list_del(&e->base.link); | ||
| 858 | kfree(e); | ||
| 859 | } | ||
| 860 | |||
| 861 | /* compare buffer id */ | ||
| 862 | if (qbuf && (qbuf->buf_id == | ||
| 863 | e->event.buf_id[EXYNOS_DRM_OPS_DST])) { | ||
| 864 | /* delete list */ | ||
| 865 | list_del(&e->base.link); | ||
| 866 | kfree(e); | ||
| 867 | return; | ||
| 868 | } | ||
| 869 | } | ||
| 870 | } | ||
| 871 | |||
| 872 | void ipp_handle_cmd_work(struct device *dev, | ||
| 873 | struct exynos_drm_ippdrv *ippdrv, | ||
| 874 | struct drm_exynos_ipp_cmd_work *cmd_work, | ||
| 875 | struct drm_exynos_ipp_cmd_node *c_node) | ||
| 876 | { | ||
| 877 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 878 | |||
| 879 | cmd_work->ippdrv = ippdrv; | ||
| 880 | cmd_work->c_node = c_node; | ||
| 881 | queue_work(ctx->cmd_workq, (struct work_struct *)cmd_work); | ||
| 882 | } | ||
| 883 | |||
| 884 | static int ipp_queue_buf_with_run(struct device *dev, | ||
| 885 | struct drm_exynos_ipp_cmd_node *c_node, | ||
| 886 | struct drm_exynos_ipp_mem_node *m_node, | ||
| 887 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
| 888 | { | ||
| 889 | struct exynos_drm_ippdrv *ippdrv; | ||
| 890 | struct drm_exynos_ipp_property *property; | ||
| 891 | struct exynos_drm_ipp_ops *ops; | ||
| 892 | int ret; | ||
| 893 | |||
| 894 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 895 | |||
| 896 | ippdrv = ipp_find_drv_by_handle(qbuf->prop_id); | ||
| 897 | if (IS_ERR_OR_NULL(ippdrv)) { | ||
| 898 | DRM_ERROR("failed to get ipp driver.\n"); | ||
| 899 | return -EFAULT; | ||
| 900 | } | ||
| 901 | |||
| 902 | ops = ippdrv->ops[qbuf->ops_id]; | ||
| 903 | if (!ops) { | ||
| 904 | DRM_ERROR("failed to get ops.\n"); | ||
| 905 | return -EFAULT; | ||
| 906 | } | ||
| 907 | |||
| 908 | property = &c_node->property; | ||
| 909 | |||
| 910 | if (c_node->state != IPP_STATE_START) { | ||
| 911 | DRM_DEBUG_KMS("%s:bypass for invalid state.\n" , __func__); | ||
| 912 | return 0; | ||
| 913 | } | ||
| 914 | |||
| 915 | if (!ipp_check_mem_list(c_node)) { | ||
| 916 | DRM_DEBUG_KMS("%s:empty memory.\n", __func__); | ||
| 917 | return 0; | ||
| 918 | } | ||
| 919 | |||
| 920 | /* | ||
| 921 | * If set destination buffer and enabled clock, | ||
| 922 | * then m2m operations need start operations at queue_buf | ||
| 923 | */ | ||
| 924 | if (ipp_is_m2m_cmd(property->cmd)) { | ||
| 925 | struct drm_exynos_ipp_cmd_work *cmd_work = c_node->start_work; | ||
| 926 | |||
| 927 | cmd_work->ctrl = IPP_CTRL_PLAY; | ||
| 928 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
| 929 | } else { | ||
| 930 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
| 931 | if (ret) { | ||
| 932 | DRM_ERROR("failed to set m node.\n"); | ||
| 933 | return ret; | ||
| 934 | } | ||
| 935 | } | ||
| 936 | |||
| 937 | return 0; | ||
| 938 | } | ||
| 939 | |||
| 940 | static void ipp_clean_queue_buf(struct drm_device *drm_dev, | ||
| 941 | struct drm_exynos_ipp_cmd_node *c_node, | ||
| 942 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
| 943 | { | ||
| 944 | struct drm_exynos_ipp_mem_node *m_node, *tm_node; | ||
| 945 | |||
| 946 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 947 | |||
| 948 | if (!list_empty(&c_node->mem_list[qbuf->ops_id])) { | ||
| 949 | /* delete list */ | ||
| 950 | list_for_each_entry_safe(m_node, tm_node, | ||
| 951 | &c_node->mem_list[qbuf->ops_id], list) { | ||
| 952 | if (m_node->buf_id == qbuf->buf_id && | ||
| 953 | m_node->ops_id == qbuf->ops_id) | ||
| 954 | ipp_put_mem_node(drm_dev, c_node, m_node); | ||
| 955 | } | ||
| 956 | } | ||
| 957 | } | ||
| 958 | |||
| 959 | int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, | ||
| 960 | struct drm_file *file) | ||
| 961 | { | ||
| 962 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
| 963 | struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; | ||
| 964 | struct device *dev = priv->dev; | ||
| 965 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 966 | struct drm_exynos_ipp_queue_buf *qbuf = data; | ||
| 967 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 968 | struct drm_exynos_ipp_mem_node *m_node; | ||
| 969 | int ret; | ||
| 970 | |||
| 971 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 972 | |||
| 973 | if (!qbuf) { | ||
| 974 | DRM_ERROR("invalid buf parameter.\n"); | ||
| 975 | return -EINVAL; | ||
| 976 | } | ||
| 977 | |||
| 978 | if (qbuf->ops_id >= EXYNOS_DRM_OPS_MAX) { | ||
| 979 | DRM_ERROR("invalid ops parameter.\n"); | ||
| 980 | return -EINVAL; | ||
| 981 | } | ||
| 982 | |||
| 983 | DRM_DEBUG_KMS("%s:prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n", | ||
| 984 | __func__, qbuf->prop_id, qbuf->ops_id ? "dst" : "src", | ||
| 985 | qbuf->buf_id, qbuf->buf_type); | ||
| 986 | |||
| 987 | /* find command node */ | ||
| 988 | c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, | ||
| 989 | qbuf->prop_id); | ||
| 990 | if (!c_node) { | ||
| 991 | DRM_ERROR("failed to get command node.\n"); | ||
| 992 | return -EFAULT; | ||
| 993 | } | ||
| 994 | |||
| 995 | /* buffer control */ | ||
| 996 | switch (qbuf->buf_type) { | ||
| 997 | case IPP_BUF_ENQUEUE: | ||
| 998 | /* get memory node */ | ||
| 999 | m_node = ipp_get_mem_node(drm_dev, file, c_node, qbuf); | ||
| 1000 | if (IS_ERR(m_node)) { | ||
| 1001 | DRM_ERROR("failed to get m_node.\n"); | ||
| 1002 | return PTR_ERR(m_node); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | /* | ||
| 1006 | * first step get event for destination buffer. | ||
| 1007 | * and second step when M2M case run with destination buffer | ||
| 1008 | * if needed. | ||
| 1009 | */ | ||
| 1010 | if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) { | ||
| 1011 | /* get event for destination buffer */ | ||
| 1012 | ret = ipp_get_event(drm_dev, file, c_node, qbuf); | ||
| 1013 | if (ret) { | ||
| 1014 | DRM_ERROR("failed to get event.\n"); | ||
| 1015 | goto err_clean_node; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | /* | ||
| 1019 | * M2M case run play control for streaming feature. | ||
| 1020 | * other case set address and waiting. | ||
| 1021 | */ | ||
| 1022 | ret = ipp_queue_buf_with_run(dev, c_node, m_node, qbuf); | ||
| 1023 | if (ret) { | ||
| 1024 | DRM_ERROR("failed to run command.\n"); | ||
| 1025 | goto err_clean_node; | ||
| 1026 | } | ||
| 1027 | } | ||
| 1028 | break; | ||
| 1029 | case IPP_BUF_DEQUEUE: | ||
| 1030 | mutex_lock(&c_node->cmd_lock); | ||
| 1031 | |||
| 1032 | /* put event for destination buffer */ | ||
| 1033 | if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) | ||
| 1034 | ipp_put_event(c_node, qbuf); | ||
| 1035 | |||
| 1036 | ipp_clean_queue_buf(drm_dev, c_node, qbuf); | ||
| 1037 | |||
| 1038 | mutex_unlock(&c_node->cmd_lock); | ||
| 1039 | break; | ||
| 1040 | default: | ||
| 1041 | DRM_ERROR("invalid buffer control.\n"); | ||
| 1042 | return -EINVAL; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | return 0; | ||
| 1046 | |||
| 1047 | err_clean_node: | ||
| 1048 | DRM_ERROR("clean memory nodes.\n"); | ||
| 1049 | |||
| 1050 | ipp_clean_queue_buf(drm_dev, c_node, qbuf); | ||
| 1051 | return ret; | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | static bool exynos_drm_ipp_check_valid(struct device *dev, | ||
| 1055 | enum drm_exynos_ipp_ctrl ctrl, enum drm_exynos_ipp_state state) | ||
| 1056 | { | ||
| 1057 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1058 | |||
| 1059 | if (ctrl != IPP_CTRL_PLAY) { | ||
| 1060 | if (pm_runtime_suspended(dev)) { | ||
| 1061 | DRM_ERROR("pm:runtime_suspended.\n"); | ||
| 1062 | goto err_status; | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | switch (ctrl) { | ||
| 1067 | case IPP_CTRL_PLAY: | ||
| 1068 | if (state != IPP_STATE_IDLE) | ||
| 1069 | goto err_status; | ||
| 1070 | break; | ||
| 1071 | case IPP_CTRL_STOP: | ||
| 1072 | if (state == IPP_STATE_STOP) | ||
| 1073 | goto err_status; | ||
| 1074 | break; | ||
| 1075 | case IPP_CTRL_PAUSE: | ||
| 1076 | if (state != IPP_STATE_START) | ||
| 1077 | goto err_status; | ||
| 1078 | break; | ||
| 1079 | case IPP_CTRL_RESUME: | ||
| 1080 | if (state != IPP_STATE_STOP) | ||
| 1081 | goto err_status; | ||
| 1082 | break; | ||
| 1083 | default: | ||
| 1084 | DRM_ERROR("invalid state.\n"); | ||
| 1085 | goto err_status; | ||
| 1086 | break; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | return true; | ||
| 1090 | |||
| 1091 | err_status: | ||
| 1092 | DRM_ERROR("invalid status:ctrl[%d]state[%d]\n", ctrl, state); | ||
| 1093 | return false; | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, | ||
| 1097 | struct drm_file *file) | ||
| 1098 | { | ||
| 1099 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
| 1100 | struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; | ||
| 1101 | struct exynos_drm_ippdrv *ippdrv = NULL; | ||
| 1102 | struct device *dev = priv->dev; | ||
| 1103 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 1104 | struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data; | ||
| 1105 | struct drm_exynos_ipp_cmd_work *cmd_work; | ||
| 1106 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 1107 | |||
| 1108 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1109 | |||
| 1110 | if (!ctx) { | ||
| 1111 | DRM_ERROR("invalid context.\n"); | ||
| 1112 | return -EINVAL; | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | if (!cmd_ctrl) { | ||
| 1116 | DRM_ERROR("invalid control parameter.\n"); | ||
| 1117 | return -EINVAL; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | DRM_DEBUG_KMS("%s:ctrl[%d]prop_id[%d]\n", __func__, | ||
| 1121 | cmd_ctrl->ctrl, cmd_ctrl->prop_id); | ||
| 1122 | |||
| 1123 | ippdrv = ipp_find_drv_by_handle(cmd_ctrl->prop_id); | ||
| 1124 | if (IS_ERR(ippdrv)) { | ||
| 1125 | DRM_ERROR("failed to get ipp driver.\n"); | ||
| 1126 | return PTR_ERR(ippdrv); | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, | ||
| 1130 | cmd_ctrl->prop_id); | ||
| 1131 | if (!c_node) { | ||
| 1132 | DRM_ERROR("invalid command node list.\n"); | ||
| 1133 | return -EINVAL; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl, | ||
| 1137 | c_node->state)) { | ||
| 1138 | DRM_ERROR("invalid state.\n"); | ||
| 1139 | return -EINVAL; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | switch (cmd_ctrl->ctrl) { | ||
| 1143 | case IPP_CTRL_PLAY: | ||
| 1144 | if (pm_runtime_suspended(ippdrv->dev)) | ||
| 1145 | pm_runtime_get_sync(ippdrv->dev); | ||
| 1146 | c_node->state = IPP_STATE_START; | ||
| 1147 | |||
| 1148 | cmd_work = c_node->start_work; | ||
| 1149 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
| 1150 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
| 1151 | c_node->state = IPP_STATE_START; | ||
| 1152 | break; | ||
| 1153 | case IPP_CTRL_STOP: | ||
| 1154 | cmd_work = c_node->stop_work; | ||
| 1155 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
| 1156 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
| 1157 | |||
| 1158 | if (!wait_for_completion_timeout(&c_node->stop_complete, | ||
| 1159 | msecs_to_jiffies(300))) { | ||
| 1160 | DRM_ERROR("timeout stop:prop_id[%d]\n", | ||
| 1161 | c_node->property.prop_id); | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | c_node->state = IPP_STATE_STOP; | ||
| 1165 | ippdrv->dedicated = false; | ||
| 1166 | ipp_clean_cmd_node(c_node); | ||
| 1167 | |||
| 1168 | if (list_empty(&ippdrv->cmd_list)) | ||
| 1169 | pm_runtime_put_sync(ippdrv->dev); | ||
| 1170 | break; | ||
| 1171 | case IPP_CTRL_PAUSE: | ||
| 1172 | cmd_work = c_node->stop_work; | ||
| 1173 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
| 1174 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
| 1175 | |||
| 1176 | if (!wait_for_completion_timeout(&c_node->stop_complete, | ||
| 1177 | msecs_to_jiffies(200))) { | ||
| 1178 | DRM_ERROR("timeout stop:prop_id[%d]\n", | ||
| 1179 | c_node->property.prop_id); | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | c_node->state = IPP_STATE_STOP; | ||
| 1183 | break; | ||
| 1184 | case IPP_CTRL_RESUME: | ||
| 1185 | c_node->state = IPP_STATE_START; | ||
| 1186 | cmd_work = c_node->start_work; | ||
| 1187 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
| 1188 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
| 1189 | break; | ||
| 1190 | default: | ||
| 1191 | DRM_ERROR("could not support this state currently.\n"); | ||
| 1192 | return -EINVAL; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | DRM_DEBUG_KMS("%s:done ctrl[%d]prop_id[%d]\n", __func__, | ||
| 1196 | cmd_ctrl->ctrl, cmd_ctrl->prop_id); | ||
| 1197 | |||
| 1198 | return 0; | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | int exynos_drm_ippnb_register(struct notifier_block *nb) | ||
| 1202 | { | ||
| 1203 | return blocking_notifier_chain_register( | ||
| 1204 | &exynos_drm_ippnb_list, nb); | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | int exynos_drm_ippnb_unregister(struct notifier_block *nb) | ||
| 1208 | { | ||
| 1209 | return blocking_notifier_chain_unregister( | ||
| 1210 | &exynos_drm_ippnb_list, nb); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | int exynos_drm_ippnb_send_event(unsigned long val, void *v) | ||
| 1214 | { | ||
| 1215 | return blocking_notifier_call_chain( | ||
| 1216 | &exynos_drm_ippnb_list, val, v); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, | ||
| 1220 | struct drm_exynos_ipp_property *property) | ||
| 1221 | { | ||
| 1222 | struct exynos_drm_ipp_ops *ops = NULL; | ||
| 1223 | bool swap = false; | ||
| 1224 | int ret, i; | ||
| 1225 | |||
| 1226 | if (!property) { | ||
| 1227 | DRM_ERROR("invalid property parameter.\n"); | ||
| 1228 | return -EINVAL; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); | ||
| 1232 | |||
| 1233 | /* reset h/w block */ | ||
| 1234 | if (ippdrv->reset && | ||
| 1235 | ippdrv->reset(ippdrv->dev)) { | ||
| 1236 | DRM_ERROR("failed to reset.\n"); | ||
| 1237 | return -EINVAL; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | /* set source,destination operations */ | ||
| 1241 | for_each_ipp_ops(i) { | ||
| 1242 | struct drm_exynos_ipp_config *config = | ||
| 1243 | &property->config[i]; | ||
| 1244 | |||
| 1245 | ops = ippdrv->ops[i]; | ||
| 1246 | if (!ops || !config) { | ||
| 1247 | DRM_ERROR("not support ops and config.\n"); | ||
| 1248 | return -EINVAL; | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | /* set format */ | ||
| 1252 | if (ops->set_fmt) { | ||
| 1253 | ret = ops->set_fmt(ippdrv->dev, config->fmt); | ||
| 1254 | if (ret) { | ||
| 1255 | DRM_ERROR("not support format.\n"); | ||
| 1256 | return ret; | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /* set transform for rotation, flip */ | ||
| 1261 | if (ops->set_transf) { | ||
| 1262 | ret = ops->set_transf(ippdrv->dev, config->degree, | ||
| 1263 | config->flip, &swap); | ||
| 1264 | if (ret) { | ||
| 1265 | DRM_ERROR("not support tranf.\n"); | ||
| 1266 | return -EINVAL; | ||
| 1267 | } | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | /* set size */ | ||
| 1271 | if (ops->set_size) { | ||
| 1272 | ret = ops->set_size(ippdrv->dev, swap, &config->pos, | ||
| 1273 | &config->sz); | ||
| 1274 | if (ret) { | ||
| 1275 | DRM_ERROR("not support size.\n"); | ||
| 1276 | return ret; | ||
| 1277 | } | ||
| 1278 | } | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | return 0; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, | ||
| 1285 | struct drm_exynos_ipp_cmd_node *c_node) | ||
| 1286 | { | ||
| 1287 | struct drm_exynos_ipp_mem_node *m_node; | ||
| 1288 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
| 1289 | struct list_head *head; | ||
| 1290 | int ret, i; | ||
| 1291 | |||
| 1292 | DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); | ||
| 1293 | |||
| 1294 | /* store command info in ippdrv */ | ||
| 1295 | ippdrv->cmd = c_node; | ||
| 1296 | |||
| 1297 | if (!ipp_check_mem_list(c_node)) { | ||
| 1298 | DRM_DEBUG_KMS("%s:empty memory.\n", __func__); | ||
| 1299 | return -ENOMEM; | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | /* set current property in ippdrv */ | ||
| 1303 | ret = ipp_set_property(ippdrv, property); | ||
| 1304 | if (ret) { | ||
| 1305 | DRM_ERROR("failed to set property.\n"); | ||
| 1306 | ippdrv->cmd = NULL; | ||
| 1307 | return ret; | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /* check command */ | ||
| 1311 | switch (property->cmd) { | ||
| 1312 | case IPP_CMD_M2M: | ||
| 1313 | for_each_ipp_ops(i) { | ||
| 1314 | /* source/destination memory list */ | ||
| 1315 | head = &c_node->mem_list[i]; | ||
| 1316 | |||
| 1317 | m_node = list_first_entry(head, | ||
| 1318 | struct drm_exynos_ipp_mem_node, list); | ||
| 1319 | if (!m_node) { | ||
| 1320 | DRM_ERROR("failed to get node.\n"); | ||
| 1321 | ret = -EFAULT; | ||
| 1322 | return ret; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | DRM_DEBUG_KMS("%s:m_node[0x%x]\n", | ||
| 1326 | __func__, (int)m_node); | ||
| 1327 | |||
| 1328 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
| 1329 | if (ret) { | ||
| 1330 | DRM_ERROR("failed to set m node.\n"); | ||
| 1331 | return ret; | ||
| 1332 | } | ||
| 1333 | } | ||
| 1334 | break; | ||
| 1335 | case IPP_CMD_WB: | ||
| 1336 | /* destination memory list */ | ||
| 1337 | head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; | ||
| 1338 | |||
| 1339 | list_for_each_entry(m_node, head, list) { | ||
| 1340 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
| 1341 | if (ret) { | ||
| 1342 | DRM_ERROR("failed to set m node.\n"); | ||
| 1343 | return ret; | ||
| 1344 | } | ||
| 1345 | } | ||
| 1346 | break; | ||
| 1347 | case IPP_CMD_OUTPUT: | ||
| 1348 | /* source memory list */ | ||
| 1349 | head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; | ||
| 1350 | |||
| 1351 | list_for_each_entry(m_node, head, list) { | ||
| 1352 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
| 1353 | if (ret) { | ||
| 1354 | DRM_ERROR("failed to set m node.\n"); | ||
| 1355 | return ret; | ||
| 1356 | } | ||
| 1357 | } | ||
| 1358 | break; | ||
| 1359 | default: | ||
| 1360 | DRM_ERROR("invalid operations.\n"); | ||
| 1361 | return -EINVAL; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, property->cmd); | ||
| 1365 | |||
| 1366 | /* start operations */ | ||
| 1367 | if (ippdrv->start) { | ||
| 1368 | ret = ippdrv->start(ippdrv->dev, property->cmd); | ||
| 1369 | if (ret) { | ||
| 1370 | DRM_ERROR("failed to start ops.\n"); | ||
| 1371 | return ret; | ||
| 1372 | } | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | return 0; | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | static int ipp_stop_property(struct drm_device *drm_dev, | ||
| 1379 | struct exynos_drm_ippdrv *ippdrv, | ||
| 1380 | struct drm_exynos_ipp_cmd_node *c_node) | ||
| 1381 | { | ||
| 1382 | struct drm_exynos_ipp_mem_node *m_node, *tm_node; | ||
| 1383 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
| 1384 | struct list_head *head; | ||
| 1385 | int ret = 0, i; | ||
| 1386 | |||
| 1387 | DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); | ||
| 1388 | |||
| 1389 | /* put event */ | ||
| 1390 | ipp_put_event(c_node, NULL); | ||
| 1391 | |||
| 1392 | /* check command */ | ||
| 1393 | switch (property->cmd) { | ||
| 1394 | case IPP_CMD_M2M: | ||
| 1395 | for_each_ipp_ops(i) { | ||
| 1396 | /* source/destination memory list */ | ||
| 1397 | head = &c_node->mem_list[i]; | ||
| 1398 | |||
| 1399 | if (list_empty(head)) { | ||
| 1400 | DRM_DEBUG_KMS("%s:mem_list is empty.\n", | ||
| 1401 | __func__); | ||
| 1402 | break; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | list_for_each_entry_safe(m_node, tm_node, | ||
| 1406 | head, list) { | ||
| 1407 | ret = ipp_put_mem_node(drm_dev, c_node, | ||
| 1408 | m_node); | ||
| 1409 | if (ret) { | ||
| 1410 | DRM_ERROR("failed to put m_node.\n"); | ||
| 1411 | goto err_clear; | ||
| 1412 | } | ||
| 1413 | } | ||
| 1414 | } | ||
| 1415 | break; | ||
| 1416 | case IPP_CMD_WB: | ||
| 1417 | /* destination memory list */ | ||
| 1418 | head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; | ||
| 1419 | |||
| 1420 | if (list_empty(head)) { | ||
| 1421 | DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__); | ||
| 1422 | break; | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | list_for_each_entry_safe(m_node, tm_node, head, list) { | ||
| 1426 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
| 1427 | if (ret) { | ||
| 1428 | DRM_ERROR("failed to put m_node.\n"); | ||
| 1429 | goto err_clear; | ||
| 1430 | } | ||
| 1431 | } | ||
| 1432 | break; | ||
| 1433 | case IPP_CMD_OUTPUT: | ||
| 1434 | /* source memory list */ | ||
| 1435 | head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; | ||
| 1436 | |||
| 1437 | if (list_empty(head)) { | ||
| 1438 | DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__); | ||
| 1439 | break; | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | list_for_each_entry_safe(m_node, tm_node, head, list) { | ||
| 1443 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
| 1444 | if (ret) { | ||
| 1445 | DRM_ERROR("failed to put m_node.\n"); | ||
| 1446 | goto err_clear; | ||
| 1447 | } | ||
| 1448 | } | ||
| 1449 | break; | ||
| 1450 | default: | ||
| 1451 | DRM_ERROR("invalid operations.\n"); | ||
| 1452 | ret = -EINVAL; | ||
| 1453 | goto err_clear; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | err_clear: | ||
| 1457 | /* stop operations */ | ||
| 1458 | if (ippdrv->stop) | ||
| 1459 | ippdrv->stop(ippdrv->dev, property->cmd); | ||
| 1460 | |||
| 1461 | return ret; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | void ipp_sched_cmd(struct work_struct *work) | ||
| 1465 | { | ||
| 1466 | struct drm_exynos_ipp_cmd_work *cmd_work = | ||
| 1467 | (struct drm_exynos_ipp_cmd_work *)work; | ||
| 1468 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1469 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 1470 | struct drm_exynos_ipp_property *property; | ||
| 1471 | int ret; | ||
| 1472 | |||
| 1473 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1474 | |||
| 1475 | ippdrv = cmd_work->ippdrv; | ||
| 1476 | if (!ippdrv) { | ||
| 1477 | DRM_ERROR("invalid ippdrv list.\n"); | ||
| 1478 | return; | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | c_node = cmd_work->c_node; | ||
| 1482 | if (!c_node) { | ||
| 1483 | DRM_ERROR("invalid command node list.\n"); | ||
| 1484 | return; | ||
| 1485 | } | ||
| 1486 | |||
| 1487 | mutex_lock(&c_node->cmd_lock); | ||
| 1488 | |||
| 1489 | property = &c_node->property; | ||
| 1490 | if (!property) { | ||
| 1491 | DRM_ERROR("failed to get property:prop_id[%d]\n", | ||
| 1492 | c_node->property.prop_id); | ||
| 1493 | goto err_unlock; | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | switch (cmd_work->ctrl) { | ||
| 1497 | case IPP_CTRL_PLAY: | ||
| 1498 | case IPP_CTRL_RESUME: | ||
| 1499 | ret = ipp_start_property(ippdrv, c_node); | ||
| 1500 | if (ret) { | ||
| 1501 | DRM_ERROR("failed to start property:prop_id[%d]\n", | ||
| 1502 | c_node->property.prop_id); | ||
| 1503 | goto err_unlock; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | /* | ||
| 1507 | * M2M case supports wait_completion of transfer. | ||
| 1508 | * because M2M case supports single unit operation | ||
| 1509 | * with multiple queue. | ||
| 1510 | * M2M need to wait completion of data transfer. | ||
| 1511 | */ | ||
| 1512 | if (ipp_is_m2m_cmd(property->cmd)) { | ||
| 1513 | if (!wait_for_completion_timeout | ||
| 1514 | (&c_node->start_complete, msecs_to_jiffies(200))) { | ||
| 1515 | DRM_ERROR("timeout event:prop_id[%d]\n", | ||
| 1516 | c_node->property.prop_id); | ||
| 1517 | goto err_unlock; | ||
| 1518 | } | ||
| 1519 | } | ||
| 1520 | break; | ||
| 1521 | case IPP_CTRL_STOP: | ||
| 1522 | case IPP_CTRL_PAUSE: | ||
| 1523 | ret = ipp_stop_property(ippdrv->drm_dev, ippdrv, | ||
| 1524 | c_node); | ||
| 1525 | if (ret) { | ||
| 1526 | DRM_ERROR("failed to stop property.\n"); | ||
| 1527 | goto err_unlock; | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | complete(&c_node->stop_complete); | ||
| 1531 | break; | ||
| 1532 | default: | ||
| 1533 | DRM_ERROR("unknown control type\n"); | ||
| 1534 | break; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | DRM_DEBUG_KMS("%s:ctrl[%d] done.\n", __func__, cmd_work->ctrl); | ||
| 1538 | |||
| 1539 | err_unlock: | ||
| 1540 | mutex_unlock(&c_node->cmd_lock); | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, | ||
| 1544 | struct drm_exynos_ipp_cmd_node *c_node, int *buf_id) | ||
| 1545 | { | ||
| 1546 | struct drm_device *drm_dev = ippdrv->drm_dev; | ||
| 1547 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
| 1548 | struct drm_exynos_ipp_mem_node *m_node; | ||
| 1549 | struct drm_exynos_ipp_queue_buf qbuf; | ||
| 1550 | struct drm_exynos_ipp_send_event *e; | ||
| 1551 | struct list_head *head; | ||
| 1552 | struct timeval now; | ||
| 1553 | unsigned long flags; | ||
| 1554 | u32 tbuf_id[EXYNOS_DRM_OPS_MAX] = {0, }; | ||
| 1555 | int ret, i; | ||
| 1556 | |||
| 1557 | for_each_ipp_ops(i) | ||
| 1558 | DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__, | ||
| 1559 | i ? "dst" : "src", buf_id[i]); | ||
| 1560 | |||
| 1561 | if (!drm_dev) { | ||
| 1562 | DRM_ERROR("failed to get drm_dev.\n"); | ||
| 1563 | return -EINVAL; | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | if (!property) { | ||
| 1567 | DRM_ERROR("failed to get property.\n"); | ||
| 1568 | return -EINVAL; | ||
| 1569 | } | ||
| 1570 | |||
| 1571 | if (list_empty(&c_node->event_list)) { | ||
| 1572 | DRM_DEBUG_KMS("%s:event list is empty.\n", __func__); | ||
| 1573 | return 0; | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | if (!ipp_check_mem_list(c_node)) { | ||
| 1577 | DRM_DEBUG_KMS("%s:empty memory.\n", __func__); | ||
| 1578 | return 0; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /* check command */ | ||
| 1582 | switch (property->cmd) { | ||
| 1583 | case IPP_CMD_M2M: | ||
| 1584 | for_each_ipp_ops(i) { | ||
| 1585 | /* source/destination memory list */ | ||
| 1586 | head = &c_node->mem_list[i]; | ||
| 1587 | |||
| 1588 | m_node = list_first_entry(head, | ||
| 1589 | struct drm_exynos_ipp_mem_node, list); | ||
| 1590 | if (!m_node) { | ||
| 1591 | DRM_ERROR("empty memory node.\n"); | ||
| 1592 | return -ENOMEM; | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | tbuf_id[i] = m_node->buf_id; | ||
| 1596 | DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__, | ||
| 1597 | i ? "dst" : "src", tbuf_id[i]); | ||
| 1598 | |||
| 1599 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
| 1600 | if (ret) | ||
| 1601 | DRM_ERROR("failed to put m_node.\n"); | ||
| 1602 | } | ||
| 1603 | break; | ||
| 1604 | case IPP_CMD_WB: | ||
| 1605 | /* clear buf for finding */ | ||
| 1606 | memset(&qbuf, 0x0, sizeof(qbuf)); | ||
| 1607 | qbuf.ops_id = EXYNOS_DRM_OPS_DST; | ||
| 1608 | qbuf.buf_id = buf_id[EXYNOS_DRM_OPS_DST]; | ||
| 1609 | |||
| 1610 | /* get memory node entry */ | ||
| 1611 | m_node = ipp_find_mem_node(c_node, &qbuf); | ||
| 1612 | if (!m_node) { | ||
| 1613 | DRM_ERROR("empty memory node.\n"); | ||
| 1614 | return -ENOMEM; | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id; | ||
| 1618 | |||
| 1619 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
| 1620 | if (ret) | ||
| 1621 | DRM_ERROR("failed to put m_node.\n"); | ||
| 1622 | break; | ||
| 1623 | case IPP_CMD_OUTPUT: | ||
| 1624 | /* source memory list */ | ||
| 1625 | head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; | ||
| 1626 | |||
| 1627 | m_node = list_first_entry(head, | ||
| 1628 | struct drm_exynos_ipp_mem_node, list); | ||
| 1629 | if (!m_node) { | ||
| 1630 | DRM_ERROR("empty memory node.\n"); | ||
| 1631 | return -ENOMEM; | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id; | ||
| 1635 | |||
| 1636 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
| 1637 | if (ret) | ||
| 1638 | DRM_ERROR("failed to put m_node.\n"); | ||
| 1639 | break; | ||
| 1640 | default: | ||
| 1641 | DRM_ERROR("invalid operations.\n"); | ||
| 1642 | return -EINVAL; | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST]) | ||
| 1646 | DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n", | ||
| 1647 | tbuf_id[1], buf_id[1], property->prop_id); | ||
| 1648 | |||
| 1649 | /* | ||
| 1650 | * command node have event list of destination buffer | ||
| 1651 | * If destination buffer enqueue to mem list, | ||
| 1652 | * then we make event and link to event list tail. | ||
| 1653 | * so, we get first event for first enqueued buffer. | ||
| 1654 | */ | ||
| 1655 | e = list_first_entry(&c_node->event_list, | ||
| 1656 | struct drm_exynos_ipp_send_event, base.link); | ||
| 1657 | |||
| 1658 | if (!e) { | ||
| 1659 | DRM_ERROR("empty event.\n"); | ||
| 1660 | return -EINVAL; | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | do_gettimeofday(&now); | ||
| 1664 | DRM_DEBUG_KMS("%s:tv_sec[%ld]tv_usec[%ld]\n" | ||
| 1665 | , __func__, now.tv_sec, now.tv_usec); | ||
| 1666 | e->event.tv_sec = now.tv_sec; | ||
| 1667 | e->event.tv_usec = now.tv_usec; | ||
| 1668 | e->event.prop_id = property->prop_id; | ||
| 1669 | |||
| 1670 | /* set buffer id about source destination */ | ||
| 1671 | for_each_ipp_ops(i) | ||
| 1672 | e->event.buf_id[i] = tbuf_id[i]; | ||
| 1673 | |||
| 1674 | spin_lock_irqsave(&drm_dev->event_lock, flags); | ||
| 1675 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
| 1676 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
| 1677 | spin_unlock_irqrestore(&drm_dev->event_lock, flags); | ||
| 1678 | |||
| 1679 | DRM_DEBUG_KMS("%s:done cmd[%d]prop_id[%d]buf_id[%d]\n", __func__, | ||
| 1680 | property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]); | ||
| 1681 | |||
| 1682 | return 0; | ||
| 1683 | } | ||
| 1684 | |||
| 1685 | void ipp_sched_event(struct work_struct *work) | ||
| 1686 | { | ||
| 1687 | struct drm_exynos_ipp_event_work *event_work = | ||
| 1688 | (struct drm_exynos_ipp_event_work *)work; | ||
| 1689 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1690 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 1691 | int ret; | ||
| 1692 | |||
| 1693 | if (!event_work) { | ||
| 1694 | DRM_ERROR("failed to get event_work.\n"); | ||
| 1695 | return; | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, | ||
| 1699 | event_work->buf_id[EXYNOS_DRM_OPS_DST]); | ||
| 1700 | |||
| 1701 | ippdrv = event_work->ippdrv; | ||
| 1702 | if (!ippdrv) { | ||
| 1703 | DRM_ERROR("failed to get ipp driver.\n"); | ||
| 1704 | return; | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | c_node = ippdrv->cmd; | ||
| 1708 | if (!c_node) { | ||
| 1709 | DRM_ERROR("failed to get command node.\n"); | ||
| 1710 | return; | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | /* | ||
| 1714 | * IPP supports command thread, event thread synchronization. | ||
| 1715 | * If IPP close immediately from user land, then IPP make | ||
| 1716 | * synchronization with command thread, so make complete event. | ||
| 1717 | * or going out operations. | ||
| 1718 | */ | ||
| 1719 | if (c_node->state != IPP_STATE_START) { | ||
| 1720 | DRM_DEBUG_KMS("%s:bypass state[%d]prop_id[%d]\n", | ||
| 1721 | __func__, c_node->state, c_node->property.prop_id); | ||
| 1722 | goto err_completion; | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | mutex_lock(&c_node->event_lock); | ||
| 1726 | |||
| 1727 | ret = ipp_send_event(ippdrv, c_node, event_work->buf_id); | ||
| 1728 | if (ret) { | ||
| 1729 | DRM_ERROR("failed to send event.\n"); | ||
| 1730 | goto err_completion; | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | err_completion: | ||
| 1734 | if (ipp_is_m2m_cmd(c_node->property.cmd)) | ||
| 1735 | complete(&c_node->start_complete); | ||
| 1736 | |||
| 1737 | mutex_unlock(&c_node->event_lock); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) | ||
| 1741 | { | ||
| 1742 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 1743 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1744 | int ret, count = 0; | ||
| 1745 | |||
| 1746 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1747 | |||
| 1748 | /* get ipp driver entry */ | ||
| 1749 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
| 1750 | ippdrv->drm_dev = drm_dev; | ||
| 1751 | |||
| 1752 | ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv, | ||
| 1753 | &ippdrv->ipp_id); | ||
| 1754 | if (ret) { | ||
| 1755 | DRM_ERROR("failed to create id.\n"); | ||
| 1756 | goto err_idr; | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]ipp_id[%d]\n", __func__, | ||
| 1760 | count++, (int)ippdrv, ippdrv->ipp_id); | ||
| 1761 | |||
| 1762 | if (ippdrv->ipp_id == 0) { | ||
| 1763 | DRM_ERROR("failed to get ipp_id[%d]\n", | ||
| 1764 | ippdrv->ipp_id); | ||
| 1765 | goto err_idr; | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | /* store parent device for node */ | ||
| 1769 | ippdrv->parent_dev = dev; | ||
| 1770 | |||
| 1771 | /* store event work queue and handler */ | ||
| 1772 | ippdrv->event_workq = ctx->event_workq; | ||
| 1773 | ippdrv->sched_event = ipp_sched_event; | ||
| 1774 | INIT_LIST_HEAD(&ippdrv->cmd_list); | ||
| 1775 | |||
| 1776 | if (is_drm_iommu_supported(drm_dev)) { | ||
| 1777 | ret = drm_iommu_attach_device(drm_dev, ippdrv->dev); | ||
| 1778 | if (ret) { | ||
| 1779 | DRM_ERROR("failed to activate iommu\n"); | ||
| 1780 | goto err_iommu; | ||
| 1781 | } | ||
| 1782 | } | ||
| 1783 | } | ||
| 1784 | |||
| 1785 | return 0; | ||
| 1786 | |||
| 1787 | err_iommu: | ||
| 1788 | /* get ipp driver entry */ | ||
| 1789 | list_for_each_entry_reverse(ippdrv, &exynos_drm_ippdrv_list, drv_list) | ||
| 1790 | if (is_drm_iommu_supported(drm_dev)) | ||
| 1791 | drm_iommu_detach_device(drm_dev, ippdrv->dev); | ||
| 1792 | |||
| 1793 | err_idr: | ||
| 1794 | idr_remove_all(&ctx->ipp_idr); | ||
| 1795 | idr_remove_all(&ctx->prop_idr); | ||
| 1796 | idr_destroy(&ctx->ipp_idr); | ||
| 1797 | idr_destroy(&ctx->prop_idr); | ||
| 1798 | return ret; | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) | ||
| 1802 | { | ||
| 1803 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1804 | |||
| 1805 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1806 | |||
| 1807 | /* get ipp driver entry */ | ||
| 1808 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
| 1809 | if (is_drm_iommu_supported(drm_dev)) | ||
| 1810 | drm_iommu_detach_device(drm_dev, ippdrv->dev); | ||
| 1811 | |||
| 1812 | ippdrv->drm_dev = NULL; | ||
| 1813 | exynos_drm_ippdrv_unregister(ippdrv); | ||
| 1814 | } | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, | ||
| 1818 | struct drm_file *file) | ||
| 1819 | { | ||
| 1820 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
| 1821 | struct exynos_drm_ipp_private *priv; | ||
| 1822 | |||
| 1823 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1824 | |||
| 1825 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
| 1826 | if (!priv) { | ||
| 1827 | DRM_ERROR("failed to allocate priv.\n"); | ||
| 1828 | return -ENOMEM; | ||
| 1829 | } | ||
| 1830 | priv->dev = dev; | ||
| 1831 | file_priv->ipp_priv = priv; | ||
| 1832 | |||
| 1833 | INIT_LIST_HEAD(&priv->event_list); | ||
| 1834 | |||
| 1835 | DRM_DEBUG_KMS("%s:done priv[0x%x]\n", __func__, (int)priv); | ||
| 1836 | |||
| 1837 | return 0; | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, | ||
| 1841 | struct drm_file *file) | ||
| 1842 | { | ||
| 1843 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
| 1844 | struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; | ||
| 1845 | struct exynos_drm_ippdrv *ippdrv = NULL; | ||
| 1846 | struct drm_exynos_ipp_cmd_node *c_node, *tc_node; | ||
| 1847 | int count = 0; | ||
| 1848 | |||
| 1849 | DRM_DEBUG_KMS("%s:for priv[0x%x]\n", __func__, (int)priv); | ||
| 1850 | |||
| 1851 | if (list_empty(&exynos_drm_ippdrv_list)) { | ||
| 1852 | DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__); | ||
| 1853 | goto err_clear; | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
| 1857 | if (list_empty(&ippdrv->cmd_list)) | ||
| 1858 | continue; | ||
| 1859 | |||
| 1860 | list_for_each_entry_safe(c_node, tc_node, | ||
| 1861 | &ippdrv->cmd_list, list) { | ||
| 1862 | DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", | ||
| 1863 | __func__, count++, (int)ippdrv); | ||
| 1864 | |||
| 1865 | if (c_node->priv == priv) { | ||
| 1866 | /* | ||
| 1867 | * userland goto unnormal state. process killed. | ||
| 1868 | * and close the file. | ||
| 1869 | * so, IPP didn't called stop cmd ctrl. | ||
| 1870 | * so, we are make stop operation in this state. | ||
| 1871 | */ | ||
| 1872 | if (c_node->state == IPP_STATE_START) { | ||
| 1873 | ipp_stop_property(drm_dev, ippdrv, | ||
| 1874 | c_node); | ||
| 1875 | c_node->state = IPP_STATE_STOP; | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | ippdrv->dedicated = false; | ||
| 1879 | ipp_clean_cmd_node(c_node); | ||
| 1880 | if (list_empty(&ippdrv->cmd_list)) | ||
| 1881 | pm_runtime_put_sync(ippdrv->dev); | ||
| 1882 | } | ||
| 1883 | } | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | err_clear: | ||
| 1887 | kfree(priv); | ||
| 1888 | return; | ||
| 1889 | } | ||
| 1890 | |||
| 1891 | static int __devinit ipp_probe(struct platform_device *pdev) | ||
| 1892 | { | ||
| 1893 | struct device *dev = &pdev->dev; | ||
| 1894 | struct ipp_context *ctx; | ||
| 1895 | struct exynos_drm_subdrv *subdrv; | ||
| 1896 | int ret; | ||
| 1897 | |||
| 1898 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
| 1899 | if (!ctx) | ||
| 1900 | return -ENOMEM; | ||
| 1901 | |||
| 1902 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1903 | |||
| 1904 | mutex_init(&ctx->ipp_lock); | ||
| 1905 | mutex_init(&ctx->prop_lock); | ||
| 1906 | |||
| 1907 | idr_init(&ctx->ipp_idr); | ||
| 1908 | idr_init(&ctx->prop_idr); | ||
| 1909 | |||
| 1910 | /* | ||
| 1911 | * create single thread for ipp event | ||
| 1912 | * IPP supports event thread for IPP drivers. | ||
| 1913 | * IPP driver send event_work to this thread. | ||
| 1914 | * and IPP event thread send event to user process. | ||
| 1915 | */ | ||
| 1916 | ctx->event_workq = create_singlethread_workqueue("ipp_event"); | ||
| 1917 | if (!ctx->event_workq) { | ||
| 1918 | dev_err(dev, "failed to create event workqueue\n"); | ||
| 1919 | ret = -EINVAL; | ||
| 1920 | goto err_clear; | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | /* | ||
| 1924 | * create single thread for ipp command | ||
| 1925 | * IPP supports command thread for user process. | ||
| 1926 | * user process make command node using set property ioctl. | ||
| 1927 | * and make start_work and send this work to command thread. | ||
| 1928 | * and then this command thread start property. | ||
| 1929 | */ | ||
| 1930 | ctx->cmd_workq = create_singlethread_workqueue("ipp_cmd"); | ||
| 1931 | if (!ctx->cmd_workq) { | ||
| 1932 | dev_err(dev, "failed to create cmd workqueue\n"); | ||
| 1933 | ret = -EINVAL; | ||
| 1934 | goto err_event_workq; | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | /* set sub driver informations */ | ||
| 1938 | subdrv = &ctx->subdrv; | ||
| 1939 | subdrv->dev = dev; | ||
| 1940 | subdrv->probe = ipp_subdrv_probe; | ||
| 1941 | subdrv->remove = ipp_subdrv_remove; | ||
| 1942 | subdrv->open = ipp_subdrv_open; | ||
| 1943 | subdrv->close = ipp_subdrv_close; | ||
| 1944 | |||
| 1945 | platform_set_drvdata(pdev, ctx); | ||
| 1946 | |||
| 1947 | ret = exynos_drm_subdrv_register(subdrv); | ||
| 1948 | if (ret < 0) { | ||
| 1949 | DRM_ERROR("failed to register drm ipp device.\n"); | ||
| 1950 | goto err_cmd_workq; | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | dev_info(&pdev->dev, "drm ipp registered successfully.\n"); | ||
| 1954 | |||
| 1955 | return 0; | ||
| 1956 | |||
| 1957 | err_cmd_workq: | ||
| 1958 | destroy_workqueue(ctx->cmd_workq); | ||
| 1959 | err_event_workq: | ||
| 1960 | destroy_workqueue(ctx->event_workq); | ||
| 1961 | err_clear: | ||
| 1962 | kfree(ctx); | ||
| 1963 | return ret; | ||
| 1964 | } | ||
| 1965 | |||
| 1966 | static int __devexit ipp_remove(struct platform_device *pdev) | ||
| 1967 | { | ||
| 1968 | struct ipp_context *ctx = platform_get_drvdata(pdev); | ||
| 1969 | |||
| 1970 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 1971 | |||
| 1972 | /* unregister sub driver */ | ||
| 1973 | exynos_drm_subdrv_unregister(&ctx->subdrv); | ||
| 1974 | |||
| 1975 | /* remove,destroy ipp idr */ | ||
| 1976 | idr_remove_all(&ctx->ipp_idr); | ||
| 1977 | idr_remove_all(&ctx->prop_idr); | ||
| 1978 | idr_destroy(&ctx->ipp_idr); | ||
| 1979 | idr_destroy(&ctx->prop_idr); | ||
| 1980 | |||
| 1981 | mutex_destroy(&ctx->ipp_lock); | ||
| 1982 | mutex_destroy(&ctx->prop_lock); | ||
| 1983 | |||
| 1984 | /* destroy command, event work queue */ | ||
| 1985 | destroy_workqueue(ctx->cmd_workq); | ||
| 1986 | destroy_workqueue(ctx->event_workq); | ||
| 1987 | |||
| 1988 | kfree(ctx); | ||
| 1989 | |||
| 1990 | return 0; | ||
| 1991 | } | ||
| 1992 | |||
| 1993 | static int ipp_power_ctrl(struct ipp_context *ctx, bool enable) | ||
| 1994 | { | ||
| 1995 | DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); | ||
| 1996 | |||
| 1997 | return 0; | ||
| 1998 | } | ||
| 1999 | |||
| 2000 | #ifdef CONFIG_PM_SLEEP | ||
| 2001 | static int ipp_suspend(struct device *dev) | ||
| 2002 | { | ||
| 2003 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 2004 | |||
| 2005 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 2006 | |||
| 2007 | if (pm_runtime_suspended(dev)) | ||
| 2008 | return 0; | ||
| 2009 | |||
| 2010 | return ipp_power_ctrl(ctx, false); | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | static int ipp_resume(struct device *dev) | ||
| 2014 | { | ||
| 2015 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 2016 | |||
| 2017 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 2018 | |||
| 2019 | if (!pm_runtime_suspended(dev)) | ||
| 2020 | return ipp_power_ctrl(ctx, true); | ||
| 2021 | |||
| 2022 | return 0; | ||
| 2023 | } | ||
| 2024 | #endif | ||
| 2025 | |||
| 2026 | #ifdef CONFIG_PM_RUNTIME | ||
| 2027 | static int ipp_runtime_suspend(struct device *dev) | ||
| 2028 | { | ||
| 2029 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 2030 | |||
| 2031 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 2032 | |||
| 2033 | return ipp_power_ctrl(ctx, false); | ||
| 2034 | } | ||
| 2035 | |||
| 2036 | static int ipp_runtime_resume(struct device *dev) | ||
| 2037 | { | ||
| 2038 | struct ipp_context *ctx = get_ipp_context(dev); | ||
| 2039 | |||
| 2040 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 2041 | |||
| 2042 | return ipp_power_ctrl(ctx, true); | ||
| 2043 | } | ||
| 2044 | #endif | ||
| 2045 | |||
| 2046 | static const struct dev_pm_ops ipp_pm_ops = { | ||
| 2047 | SET_SYSTEM_SLEEP_PM_OPS(ipp_suspend, ipp_resume) | ||
| 2048 | SET_RUNTIME_PM_OPS(ipp_runtime_suspend, ipp_runtime_resume, NULL) | ||
| 2049 | }; | ||
| 2050 | |||
| 2051 | struct platform_driver ipp_driver = { | ||
| 2052 | .probe = ipp_probe, | ||
| 2053 | .remove = __devexit_p(ipp_remove), | ||
| 2054 | .driver = { | ||
| 2055 | .name = "exynos-drm-ipp", | ||
| 2056 | .owner = THIS_MODULE, | ||
| 2057 | .pm = &ipp_pm_ops, | ||
| 2058 | }, | ||
| 2059 | }; | ||
| 2060 | |||
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 000000000000..28ffac95386c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 6 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 7 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 8 | * | ||
| 9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 10 | * copy of this software and associated documentation files (the "Software"), | ||
| 11 | * to deal in the Software without restriction, including without limitation | ||
| 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
| 13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
| 14 | * Software is furnished to do so, subject to the following conditions: | ||
| 15 | * | ||
| 16 | * The above copyright notice and this permission notice (including the next | ||
| 17 | * paragraph) shall be included in all copies or substantial portions of the | ||
| 18 | * Software. | ||
| 19 | * | ||
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
| 24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
| 25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
| 26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #ifndef _EXYNOS_DRM_IPP_H_ | ||
| 30 | #define _EXYNOS_DRM_IPP_H_ | ||
| 31 | |||
| 32 | #define for_each_ipp_ops(pos) \ | ||
| 33 | for (pos = 0; pos < EXYNOS_DRM_OPS_MAX; pos++) | ||
| 34 | #define for_each_ipp_planar(pos) \ | ||
| 35 | for (pos = 0; pos < EXYNOS_DRM_PLANAR_MAX; pos++) | ||
| 36 | |||
| 37 | #define IPP_GET_LCD_WIDTH _IOR('F', 302, int) | ||
| 38 | #define IPP_GET_LCD_HEIGHT _IOR('F', 303, int) | ||
| 39 | #define IPP_SET_WRITEBACK _IOW('F', 304, u32) | ||
| 40 | |||
| 41 | /* definition of state */ | ||
| 42 | enum drm_exynos_ipp_state { | ||
| 43 | IPP_STATE_IDLE, | ||
| 44 | IPP_STATE_START, | ||
| 45 | IPP_STATE_STOP, | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* | ||
| 49 | * A structure of command work information. | ||
| 50 | * @work: work structure. | ||
| 51 | * @ippdrv: current work ippdrv. | ||
| 52 | * @c_node: command node information. | ||
| 53 | * @ctrl: command control. | ||
| 54 | */ | ||
| 55 | struct drm_exynos_ipp_cmd_work { | ||
| 56 | struct work_struct work; | ||
| 57 | struct exynos_drm_ippdrv *ippdrv; | ||
| 58 | struct drm_exynos_ipp_cmd_node *c_node; | ||
| 59 | enum drm_exynos_ipp_ctrl ctrl; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /* | ||
| 63 | * A structure of command node. | ||
| 64 | * | ||
| 65 | * @priv: IPP private infomation. | ||
| 66 | * @list: list head to command queue information. | ||
| 67 | * @event_list: list head of event. | ||
| 68 | * @mem_list: list head to source,destination memory queue information. | ||
| 69 | * @cmd_lock: lock for synchronization of access to ioctl. | ||
| 70 | * @mem_lock: lock for synchronization of access to memory nodes. | ||
| 71 | * @event_lock: lock for synchronization of access to scheduled event. | ||
| 72 | * @start_complete: completion of start of command. | ||
| 73 | * @stop_complete: completion of stop of command. | ||
| 74 | * @property: property information. | ||
| 75 | * @start_work: start command work structure. | ||
| 76 | * @stop_work: stop command work structure. | ||
| 77 | * @event_work: event work structure. | ||
| 78 | * @state: state of command node. | ||
| 79 | */ | ||
| 80 | struct drm_exynos_ipp_cmd_node { | ||
| 81 | struct exynos_drm_ipp_private *priv; | ||
| 82 | struct list_head list; | ||
| 83 | struct list_head event_list; | ||
| 84 | struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; | ||
| 85 | struct mutex cmd_lock; | ||
| 86 | struct mutex mem_lock; | ||
| 87 | struct mutex event_lock; | ||
| 88 | struct completion start_complete; | ||
| 89 | struct completion stop_complete; | ||
| 90 | struct drm_exynos_ipp_property property; | ||
| 91 | struct drm_exynos_ipp_cmd_work *start_work; | ||
| 92 | struct drm_exynos_ipp_cmd_work *stop_work; | ||
| 93 | struct drm_exynos_ipp_event_work *event_work; | ||
| 94 | enum drm_exynos_ipp_state state; | ||
| 95 | }; | ||
| 96 | |||
| 97 | /* | ||
| 98 | * A structure of buffer information. | ||
| 99 | * | ||
| 100 | * @gem_objs: Y, Cb, Cr each gem object. | ||
| 101 | * @base: Y, Cb, Cr each planar address. | ||
| 102 | */ | ||
| 103 | struct drm_exynos_ipp_buf_info { | ||
| 104 | unsigned long handles[EXYNOS_DRM_PLANAR_MAX]; | ||
| 105 | dma_addr_t base[EXYNOS_DRM_PLANAR_MAX]; | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* | ||
| 109 | * A structure of wb setting infomation. | ||
| 110 | * | ||
| 111 | * @enable: enable flag for wb. | ||
| 112 | * @refresh: HZ of the refresh rate. | ||
| 113 | */ | ||
| 114 | struct drm_exynos_ipp_set_wb { | ||
| 115 | __u32 enable; | ||
| 116 | __u32 refresh; | ||
| 117 | }; | ||
| 118 | |||
| 119 | /* | ||
| 120 | * A structure of event work information. | ||
| 121 | * | ||
| 122 | * @work: work structure. | ||
| 123 | * @ippdrv: current work ippdrv. | ||
| 124 | * @buf_id: id of src, dst buffer. | ||
| 125 | */ | ||
| 126 | struct drm_exynos_ipp_event_work { | ||
| 127 | struct work_struct work; | ||
| 128 | struct exynos_drm_ippdrv *ippdrv; | ||
| 129 | u32 buf_id[EXYNOS_DRM_OPS_MAX]; | ||
| 130 | }; | ||
| 131 | |||
| 132 | /* | ||
| 133 | * A structure of source,destination operations. | ||
| 134 | * | ||
| 135 | * @set_fmt: set format of image. | ||
| 136 | * @set_transf: set transform(rotations, flip). | ||
| 137 | * @set_size: set size of region. | ||
| 138 | * @set_addr: set address for dma. | ||
| 139 | */ | ||
| 140 | struct exynos_drm_ipp_ops { | ||
| 141 | int (*set_fmt)(struct device *dev, u32 fmt); | ||
| 142 | int (*set_transf)(struct device *dev, | ||
| 143 | enum drm_exynos_degree degree, | ||
| 144 | enum drm_exynos_flip flip, bool *swap); | ||
| 145 | int (*set_size)(struct device *dev, int swap, | ||
| 146 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz); | ||
| 147 | int (*set_addr)(struct device *dev, | ||
| 148 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | ||
| 149 | enum drm_exynos_ipp_buf_type buf_type); | ||
| 150 | }; | ||
| 151 | |||
| 152 | /* | ||
| 153 | * A structure of ipp driver. | ||
| 154 | * | ||
| 155 | * @drv_list: list head for registed sub driver information. | ||
| 156 | * @parent_dev: parent device information. | ||
| 157 | * @dev: platform device. | ||
| 158 | * @drm_dev: drm device. | ||
| 159 | * @ipp_id: id of ipp driver. | ||
| 160 | * @dedicated: dedicated ipp device. | ||
| 161 | * @ops: source, destination operations. | ||
| 162 | * @event_workq: event work queue. | ||
| 163 | * @cmd: current command information. | ||
| 164 | * @cmd_list: list head for command information. | ||
| 165 | * @prop_list: property informations of current ipp driver. | ||
| 166 | * @check_property: check property about format, size, buffer. | ||
| 167 | * @reset: reset ipp block. | ||
| 168 | * @start: ipp each device start. | ||
| 169 | * @stop: ipp each device stop. | ||
| 170 | * @sched_event: work schedule handler. | ||
| 171 | */ | ||
| 172 | struct exynos_drm_ippdrv { | ||
| 173 | struct list_head drv_list; | ||
| 174 | struct device *parent_dev; | ||
| 175 | struct device *dev; | ||
| 176 | struct drm_device *drm_dev; | ||
| 177 | u32 ipp_id; | ||
| 178 | bool dedicated; | ||
| 179 | struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX]; | ||
| 180 | struct workqueue_struct *event_workq; | ||
| 181 | struct drm_exynos_ipp_cmd_node *cmd; | ||
| 182 | struct list_head cmd_list; | ||
| 183 | struct drm_exynos_ipp_prop_list *prop_list; | ||
| 184 | |||
| 185 | int (*check_property)(struct device *dev, | ||
| 186 | struct drm_exynos_ipp_property *property); | ||
| 187 | int (*reset)(struct device *dev); | ||
| 188 | int (*start)(struct device *dev, enum drm_exynos_ipp_cmd cmd); | ||
| 189 | void (*stop)(struct device *dev, enum drm_exynos_ipp_cmd cmd); | ||
| 190 | void (*sched_event)(struct work_struct *work); | ||
| 191 | }; | ||
| 192 | |||
| 193 | #ifdef CONFIG_DRM_EXYNOS_IPP | ||
| 194 | extern int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv); | ||
| 195 | extern int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv); | ||
| 196 | extern int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, | ||
| 197 | struct drm_file *file); | ||
| 198 | extern int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, | ||
| 199 | struct drm_file *file); | ||
| 200 | extern int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, | ||
| 201 | struct drm_file *file); | ||
| 202 | extern int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, | ||
| 203 | struct drm_file *file); | ||
| 204 | extern int exynos_drm_ippnb_register(struct notifier_block *nb); | ||
| 205 | extern int exynos_drm_ippnb_unregister(struct notifier_block *nb); | ||
| 206 | extern int exynos_drm_ippnb_send_event(unsigned long val, void *v); | ||
| 207 | extern void ipp_sched_cmd(struct work_struct *work); | ||
| 208 | extern void ipp_sched_event(struct work_struct *work); | ||
| 209 | |||
| 210 | #else | ||
| 211 | static inline int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) | ||
| 212 | { | ||
| 213 | return -ENODEV; | ||
| 214 | } | ||
| 215 | |||
| 216 | static inline int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) | ||
| 217 | { | ||
| 218 | return -ENODEV; | ||
| 219 | } | ||
| 220 | |||
| 221 | static inline int exynos_drm_ipp_get_property(struct drm_device *drm_dev, | ||
| 222 | void *data, | ||
| 223 | struct drm_file *file_priv) | ||
| 224 | { | ||
| 225 | return -ENOTTY; | ||
| 226 | } | ||
| 227 | |||
| 228 | static inline int exynos_drm_ipp_set_property(struct drm_device *drm_dev, | ||
| 229 | void *data, | ||
| 230 | struct drm_file *file_priv) | ||
| 231 | { | ||
| 232 | return -ENOTTY; | ||
| 233 | } | ||
| 234 | |||
| 235 | static inline int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, | ||
| 236 | void *data, | ||
| 237 | struct drm_file *file) | ||
| 238 | { | ||
| 239 | return -ENOTTY; | ||
| 240 | } | ||
| 241 | |||
| 242 | static inline int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, | ||
| 243 | void *data, | ||
| 244 | struct drm_file *file) | ||
| 245 | { | ||
| 246 | return -ENOTTY; | ||
| 247 | } | ||
| 248 | |||
| 249 | static inline int exynos_drm_ippnb_register(struct notifier_block *nb) | ||
| 250 | { | ||
| 251 | return -ENODEV; | ||
| 252 | } | ||
| 253 | |||
| 254 | static inline int exynos_drm_ippnb_unregister(struct notifier_block *nb) | ||
| 255 | { | ||
| 256 | return -ENODEV; | ||
| 257 | } | ||
| 258 | |||
| 259 | static inline int exynos_drm_ippnb_send_event(unsigned long val, void *v) | ||
| 260 | { | ||
| 261 | return -ENOTTY; | ||
| 262 | } | ||
| 263 | #endif | ||
| 264 | |||
| 265 | #endif /* _EXYNOS_DRM_IPP_H_ */ | ||
| 266 | |||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 862ca1eb2102..83efc662d65a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c | |||
| @@ -40,7 +40,7 @@ static const uint32_t formats[] = { | |||
| 40 | * CRTC ---------------- | 40 | * CRTC ---------------- |
| 41 | * ^ start ^ end | 41 | * ^ start ^ end |
| 42 | * | 42 | * |
| 43 | * There are six cases from a to b. | 43 | * There are six cases from a to f. |
| 44 | * | 44 | * |
| 45 | * <----- SCREEN -----> | 45 | * <----- SCREEN -----> |
| 46 | * 0 last | 46 | * 0 last |
| @@ -93,11 +93,9 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, | |||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | overlay->dma_addr[i] = buffer->dma_addr; | 95 | overlay->dma_addr[i] = buffer->dma_addr; |
| 96 | overlay->vaddr[i] = buffer->kvaddr; | ||
| 97 | 96 | ||
| 98 | DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", | 97 | DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n", |
| 99 | i, (unsigned long)overlay->vaddr[i], | 98 | i, (unsigned long)overlay->dma_addr[i]); |
| 100 | (unsigned long)overlay->dma_addr[i]); | ||
| 101 | } | 99 | } |
| 102 | 100 | ||
| 103 | actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay); | 101 | actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay); |
| @@ -106,16 +104,12 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, | |||
| 106 | if (crtc_x < 0) { | 104 | if (crtc_x < 0) { |
| 107 | if (actual_w) | 105 | if (actual_w) |
| 108 | src_x -= crtc_x; | 106 | src_x -= crtc_x; |
| 109 | else | ||
| 110 | src_x += crtc_w; | ||
| 111 | crtc_x = 0; | 107 | crtc_x = 0; |
| 112 | } | 108 | } |
| 113 | 109 | ||
| 114 | if (crtc_y < 0) { | 110 | if (crtc_y < 0) { |
| 115 | if (actual_h) | 111 | if (actual_h) |
| 116 | src_y -= crtc_y; | 112 | src_y -= crtc_y; |
| 117 | else | ||
| 118 | src_y += crtc_h; | ||
| 119 | crtc_y = 0; | 113 | crtc_y = 0; |
| 120 | } | 114 | } |
| 121 | 115 | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c new file mode 100644 index 000000000000..1c2366083c70 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c | |||
| @@ -0,0 +1,855 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
| 3 | * Authors: | ||
| 4 | * YoungJun Cho <yj44.cho@samsung.com> | ||
| 5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundationr | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/err.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/clk.h> | ||
| 19 | #include <linux/pm_runtime.h> | ||
| 20 | |||
| 21 | #include <drm/drmP.h> | ||
| 22 | #include <drm/exynos_drm.h> | ||
| 23 | #include "regs-rotator.h" | ||
| 24 | #include "exynos_drm.h" | ||
| 25 | #include "exynos_drm_ipp.h" | ||
| 26 | |||
| 27 | /* | ||
| 28 | * Rotator supports image crop/rotator and input/output DMA operations. | ||
| 29 | * input DMA reads image data from the memory. | ||
| 30 | * output DMA writes image data to memory. | ||
| 31 | * | ||
| 32 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
| 33 | * Memory ----> Rotator H/W ----> Memory. | ||
| 34 | */ | ||
| 35 | |||
| 36 | /* | ||
| 37 | * TODO | ||
| 38 | * 1. check suspend/resume api if needed. | ||
| 39 | * 2. need to check use case platform_device_id. | ||
| 40 | * 3. check src/dst size with, height. | ||
| 41 | * 4. need to add supported list in prop_list. | ||
| 42 | */ | ||
| 43 | |||
| 44 | #define get_rot_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
| 45 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
| 46 | struct rot_context, ippdrv); | ||
| 47 | #define rot_read(offset) readl(rot->regs + (offset)) | ||
| 48 | #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset)) | ||
| 49 | |||
| 50 | enum rot_irq_status { | ||
| 51 | ROT_IRQ_STATUS_COMPLETE = 8, | ||
| 52 | ROT_IRQ_STATUS_ILLEGAL = 9, | ||
| 53 | }; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * A structure of limitation. | ||
| 57 | * | ||
| 58 | * @min_w: minimum width. | ||
| 59 | * @min_h: minimum height. | ||
| 60 | * @max_w: maximum width. | ||
| 61 | * @max_h: maximum height. | ||
| 62 | * @align: align size. | ||
| 63 | */ | ||
| 64 | struct rot_limit { | ||
| 65 | u32 min_w; | ||
| 66 | u32 min_h; | ||
| 67 | u32 max_w; | ||
| 68 | u32 max_h; | ||
| 69 | u32 align; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* | ||
| 73 | * A structure of limitation table. | ||
| 74 | * | ||
| 75 | * @ycbcr420_2p: case of YUV. | ||
| 76 | * @rgb888: case of RGB. | ||
| 77 | */ | ||
| 78 | struct rot_limit_table { | ||
| 79 | struct rot_limit ycbcr420_2p; | ||
| 80 | struct rot_limit rgb888; | ||
| 81 | }; | ||
| 82 | |||
| 83 | /* | ||
| 84 | * A structure of rotator context. | ||
| 85 | * @ippdrv: prepare initialization using ippdrv. | ||
| 86 | * @regs_res: register resources. | ||
| 87 | * @regs: memory mapped io registers. | ||
| 88 | * @clock: rotator gate clock. | ||
| 89 | * @limit_tbl: limitation of rotator. | ||
| 90 | * @irq: irq number. | ||
| 91 | * @cur_buf_id: current operation buffer id. | ||
| 92 | * @suspended: suspended state. | ||
| 93 | */ | ||
| 94 | struct rot_context { | ||
| 95 | struct exynos_drm_ippdrv ippdrv; | ||
| 96 | struct resource *regs_res; | ||
| 97 | void __iomem *regs; | ||
| 98 | struct clk *clock; | ||
| 99 | struct rot_limit_table *limit_tbl; | ||
| 100 | int irq; | ||
| 101 | int cur_buf_id[EXYNOS_DRM_OPS_MAX]; | ||
| 102 | bool suspended; | ||
| 103 | }; | ||
| 104 | |||
| 105 | static void rotator_reg_set_irq(struct rot_context *rot, bool enable) | ||
| 106 | { | ||
| 107 | u32 val = rot_read(ROT_CONFIG); | ||
| 108 | |||
| 109 | if (enable == true) | ||
| 110 | val |= ROT_CONFIG_IRQ; | ||
| 111 | else | ||
| 112 | val &= ~ROT_CONFIG_IRQ; | ||
| 113 | |||
| 114 | rot_write(val, ROT_CONFIG); | ||
| 115 | } | ||
| 116 | |||
| 117 | static u32 rotator_reg_get_fmt(struct rot_context *rot) | ||
| 118 | { | ||
| 119 | u32 val = rot_read(ROT_CONTROL); | ||
| 120 | |||
| 121 | val &= ROT_CONTROL_FMT_MASK; | ||
| 122 | |||
| 123 | return val; | ||
| 124 | } | ||
| 125 | |||
| 126 | static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) | ||
| 127 | { | ||
| 128 | u32 val = rot_read(ROT_STATUS); | ||
| 129 | |||
| 130 | val = ROT_STATUS_IRQ(val); | ||
| 131 | |||
| 132 | if (val == ROT_STATUS_IRQ_VAL_COMPLETE) | ||
| 133 | return ROT_IRQ_STATUS_COMPLETE; | ||
| 134 | |||
| 135 | return ROT_IRQ_STATUS_ILLEGAL; | ||
| 136 | } | ||
| 137 | |||
| 138 | static irqreturn_t rotator_irq_handler(int irq, void *arg) | ||
| 139 | { | ||
| 140 | struct rot_context *rot = arg; | ||
| 141 | struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; | ||
| 142 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
| 143 | struct drm_exynos_ipp_event_work *event_work = c_node->event_work; | ||
| 144 | enum rot_irq_status irq_status; | ||
| 145 | u32 val; | ||
| 146 | |||
| 147 | /* Get execution result */ | ||
| 148 | irq_status = rotator_reg_get_irq_status(rot); | ||
| 149 | |||
| 150 | /* clear status */ | ||
| 151 | val = rot_read(ROT_STATUS); | ||
| 152 | val |= ROT_STATUS_IRQ_PENDING((u32)irq_status); | ||
| 153 | rot_write(val, ROT_STATUS); | ||
| 154 | |||
| 155 | if (irq_status == ROT_IRQ_STATUS_COMPLETE) { | ||
| 156 | event_work->ippdrv = ippdrv; | ||
| 157 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = | ||
| 158 | rot->cur_buf_id[EXYNOS_DRM_OPS_DST]; | ||
| 159 | queue_work(ippdrv->event_workq, | ||
| 160 | (struct work_struct *)event_work); | ||
| 161 | } else | ||
| 162 | DRM_ERROR("the SFR is set illegally\n"); | ||
| 163 | |||
| 164 | return IRQ_HANDLED; | ||
| 165 | } | ||
| 166 | |||
| 167 | static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize, | ||
| 168 | u32 *vsize) | ||
| 169 | { | ||
| 170 | struct rot_limit_table *limit_tbl = rot->limit_tbl; | ||
| 171 | struct rot_limit *limit; | ||
| 172 | u32 mask, val; | ||
| 173 | |||
| 174 | /* Get size limit */ | ||
| 175 | if (fmt == ROT_CONTROL_FMT_RGB888) | ||
| 176 | limit = &limit_tbl->rgb888; | ||
| 177 | else | ||
| 178 | limit = &limit_tbl->ycbcr420_2p; | ||
| 179 | |||
| 180 | /* Get mask for rounding to nearest aligned val */ | ||
| 181 | mask = ~((1 << limit->align) - 1); | ||
| 182 | |||
| 183 | /* Set aligned width */ | ||
| 184 | val = ROT_ALIGN(*hsize, limit->align, mask); | ||
| 185 | if (val < limit->min_w) | ||
| 186 | *hsize = ROT_MIN(limit->min_w, mask); | ||
| 187 | else if (val > limit->max_w) | ||
| 188 | *hsize = ROT_MAX(limit->max_w, mask); | ||
| 189 | else | ||
| 190 | *hsize = val; | ||
| 191 | |||
| 192 | /* Set aligned height */ | ||
| 193 | val = ROT_ALIGN(*vsize, limit->align, mask); | ||
| 194 | if (val < limit->min_h) | ||
| 195 | *vsize = ROT_MIN(limit->min_h, mask); | ||
| 196 | else if (val > limit->max_h) | ||
| 197 | *vsize = ROT_MAX(limit->max_h, mask); | ||
| 198 | else | ||
| 199 | *vsize = val; | ||
| 200 | } | ||
| 201 | |||
| 202 | static int rotator_src_set_fmt(struct device *dev, u32 fmt) | ||
| 203 | { | ||
| 204 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 205 | u32 val; | ||
| 206 | |||
| 207 | val = rot_read(ROT_CONTROL); | ||
| 208 | val &= ~ROT_CONTROL_FMT_MASK; | ||
| 209 | |||
| 210 | switch (fmt) { | ||
| 211 | case DRM_FORMAT_NV12: | ||
| 212 | val |= ROT_CONTROL_FMT_YCBCR420_2P; | ||
| 213 | break; | ||
| 214 | case DRM_FORMAT_XRGB8888: | ||
| 215 | val |= ROT_CONTROL_FMT_RGB888; | ||
| 216 | break; | ||
| 217 | default: | ||
| 218 | DRM_ERROR("invalid image format\n"); | ||
| 219 | return -EINVAL; | ||
| 220 | } | ||
| 221 | |||
| 222 | rot_write(val, ROT_CONTROL); | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | static inline bool rotator_check_reg_fmt(u32 fmt) | ||
| 228 | { | ||
| 229 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) || | ||
| 230 | (fmt == ROT_CONTROL_FMT_RGB888)) | ||
| 231 | return true; | ||
| 232 | |||
| 233 | return false; | ||
| 234 | } | ||
| 235 | |||
| 236 | static int rotator_src_set_size(struct device *dev, int swap, | ||
| 237 | struct drm_exynos_pos *pos, | ||
| 238 | struct drm_exynos_sz *sz) | ||
| 239 | { | ||
| 240 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 241 | u32 fmt, hsize, vsize; | ||
| 242 | u32 val; | ||
| 243 | |||
| 244 | /* Get format */ | ||
| 245 | fmt = rotator_reg_get_fmt(rot); | ||
| 246 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 247 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
| 248 | return -EINVAL; | ||
| 249 | } | ||
| 250 | |||
| 251 | /* Align buffer size */ | ||
| 252 | hsize = sz->hsize; | ||
| 253 | vsize = sz->vsize; | ||
| 254 | rotator_align_size(rot, fmt, &hsize, &vsize); | ||
| 255 | |||
| 256 | /* Set buffer size configuration */ | ||
| 257 | val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize); | ||
| 258 | rot_write(val, ROT_SRC_BUF_SIZE); | ||
| 259 | |||
| 260 | /* Set crop image position configuration */ | ||
| 261 | val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); | ||
| 262 | rot_write(val, ROT_SRC_CROP_POS); | ||
| 263 | val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w); | ||
| 264 | rot_write(val, ROT_SRC_CROP_SIZE); | ||
| 265 | |||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | static int rotator_src_set_addr(struct device *dev, | ||
| 270 | struct drm_exynos_ipp_buf_info *buf_info, | ||
| 271 | u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) | ||
| 272 | { | ||
| 273 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 274 | dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; | ||
| 275 | u32 val, fmt, hsize, vsize; | ||
| 276 | int i; | ||
| 277 | |||
| 278 | /* Set current buf_id */ | ||
| 279 | rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id; | ||
| 280 | |||
| 281 | switch (buf_type) { | ||
| 282 | case IPP_BUF_ENQUEUE: | ||
| 283 | /* Set address configuration */ | ||
| 284 | for_each_ipp_planar(i) | ||
| 285 | addr[i] = buf_info->base[i]; | ||
| 286 | |||
| 287 | /* Get format */ | ||
| 288 | fmt = rotator_reg_get_fmt(rot); | ||
| 289 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 290 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
| 291 | return -EINVAL; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* Re-set cb planar for NV12 format */ | ||
| 295 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && | ||
| 296 | !addr[EXYNOS_DRM_PLANAR_CB]) { | ||
| 297 | |||
| 298 | val = rot_read(ROT_SRC_BUF_SIZE); | ||
| 299 | hsize = ROT_GET_BUF_SIZE_W(val); | ||
| 300 | vsize = ROT_GET_BUF_SIZE_H(val); | ||
| 301 | |||
| 302 | /* Set cb planar */ | ||
| 303 | addr[EXYNOS_DRM_PLANAR_CB] = | ||
| 304 | addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; | ||
| 305 | } | ||
| 306 | |||
| 307 | for_each_ipp_planar(i) | ||
| 308 | rot_write(addr[i], ROT_SRC_BUF_ADDR(i)); | ||
| 309 | break; | ||
| 310 | case IPP_BUF_DEQUEUE: | ||
| 311 | for_each_ipp_planar(i) | ||
| 312 | rot_write(0x0, ROT_SRC_BUF_ADDR(i)); | ||
| 313 | break; | ||
| 314 | default: | ||
| 315 | /* Nothing to do */ | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | |||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | static int rotator_dst_set_transf(struct device *dev, | ||
| 323 | enum drm_exynos_degree degree, | ||
| 324 | enum drm_exynos_flip flip, bool *swap) | ||
| 325 | { | ||
| 326 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 327 | u32 val; | ||
| 328 | |||
| 329 | /* Set transform configuration */ | ||
| 330 | val = rot_read(ROT_CONTROL); | ||
| 331 | val &= ~ROT_CONTROL_FLIP_MASK; | ||
| 332 | |||
| 333 | switch (flip) { | ||
| 334 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 335 | val |= ROT_CONTROL_FLIP_VERTICAL; | ||
| 336 | break; | ||
| 337 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 338 | val |= ROT_CONTROL_FLIP_HORIZONTAL; | ||
| 339 | break; | ||
| 340 | default: | ||
| 341 | /* Flip None */ | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | |||
| 345 | val &= ~ROT_CONTROL_ROT_MASK; | ||
| 346 | |||
| 347 | switch (degree) { | ||
| 348 | case EXYNOS_DRM_DEGREE_90: | ||
| 349 | val |= ROT_CONTROL_ROT_90; | ||
| 350 | break; | ||
| 351 | case EXYNOS_DRM_DEGREE_180: | ||
| 352 | val |= ROT_CONTROL_ROT_180; | ||
| 353 | break; | ||
| 354 | case EXYNOS_DRM_DEGREE_270: | ||
| 355 | val |= ROT_CONTROL_ROT_270; | ||
| 356 | break; | ||
| 357 | default: | ||
| 358 | /* Rotation 0 Degree */ | ||
| 359 | break; | ||
| 360 | } | ||
| 361 | |||
| 362 | rot_write(val, ROT_CONTROL); | ||
| 363 | |||
| 364 | /* Check degree for setting buffer size swap */ | ||
| 365 | if ((degree == EXYNOS_DRM_DEGREE_90) || | ||
| 366 | (degree == EXYNOS_DRM_DEGREE_270)) | ||
| 367 | *swap = true; | ||
| 368 | else | ||
| 369 | *swap = false; | ||
| 370 | |||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | static int rotator_dst_set_size(struct device *dev, int swap, | ||
| 375 | struct drm_exynos_pos *pos, | ||
| 376 | struct drm_exynos_sz *sz) | ||
| 377 | { | ||
| 378 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 379 | u32 val, fmt, hsize, vsize; | ||
| 380 | |||
| 381 | /* Get format */ | ||
| 382 | fmt = rotator_reg_get_fmt(rot); | ||
| 383 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 384 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
| 385 | return -EINVAL; | ||
| 386 | } | ||
| 387 | |||
| 388 | /* Align buffer size */ | ||
| 389 | hsize = sz->hsize; | ||
| 390 | vsize = sz->vsize; | ||
| 391 | rotator_align_size(rot, fmt, &hsize, &vsize); | ||
| 392 | |||
| 393 | /* Set buffer size configuration */ | ||
| 394 | val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize); | ||
| 395 | rot_write(val, ROT_DST_BUF_SIZE); | ||
| 396 | |||
| 397 | /* Set crop image position configuration */ | ||
| 398 | val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); | ||
| 399 | rot_write(val, ROT_DST_CROP_POS); | ||
| 400 | |||
| 401 | return 0; | ||
| 402 | } | ||
| 403 | |||
| 404 | static int rotator_dst_set_addr(struct device *dev, | ||
| 405 | struct drm_exynos_ipp_buf_info *buf_info, | ||
| 406 | u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) | ||
| 407 | { | ||
| 408 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 409 | dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; | ||
| 410 | u32 val, fmt, hsize, vsize; | ||
| 411 | int i; | ||
| 412 | |||
| 413 | /* Set current buf_id */ | ||
| 414 | rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id; | ||
| 415 | |||
| 416 | switch (buf_type) { | ||
| 417 | case IPP_BUF_ENQUEUE: | ||
| 418 | /* Set address configuration */ | ||
| 419 | for_each_ipp_planar(i) | ||
| 420 | addr[i] = buf_info->base[i]; | ||
| 421 | |||
| 422 | /* Get format */ | ||
| 423 | fmt = rotator_reg_get_fmt(rot); | ||
| 424 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 425 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
| 426 | return -EINVAL; | ||
| 427 | } | ||
| 428 | |||
| 429 | /* Re-set cb planar for NV12 format */ | ||
| 430 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && | ||
| 431 | !addr[EXYNOS_DRM_PLANAR_CB]) { | ||
| 432 | /* Get buf size */ | ||
| 433 | val = rot_read(ROT_DST_BUF_SIZE); | ||
| 434 | |||
| 435 | hsize = ROT_GET_BUF_SIZE_W(val); | ||
| 436 | vsize = ROT_GET_BUF_SIZE_H(val); | ||
| 437 | |||
| 438 | /* Set cb planar */ | ||
| 439 | addr[EXYNOS_DRM_PLANAR_CB] = | ||
| 440 | addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; | ||
| 441 | } | ||
| 442 | |||
| 443 | for_each_ipp_planar(i) | ||
| 444 | rot_write(addr[i], ROT_DST_BUF_ADDR(i)); | ||
| 445 | break; | ||
| 446 | case IPP_BUF_DEQUEUE: | ||
| 447 | for_each_ipp_planar(i) | ||
| 448 | rot_write(0x0, ROT_DST_BUF_ADDR(i)); | ||
| 449 | break; | ||
| 450 | default: | ||
| 451 | /* Nothing to do */ | ||
| 452 | break; | ||
| 453 | } | ||
| 454 | |||
| 455 | return 0; | ||
| 456 | } | ||
| 457 | |||
| 458 | static struct exynos_drm_ipp_ops rot_src_ops = { | ||
| 459 | .set_fmt = rotator_src_set_fmt, | ||
| 460 | .set_size = rotator_src_set_size, | ||
| 461 | .set_addr = rotator_src_set_addr, | ||
| 462 | }; | ||
| 463 | |||
| 464 | static struct exynos_drm_ipp_ops rot_dst_ops = { | ||
| 465 | .set_transf = rotator_dst_set_transf, | ||
| 466 | .set_size = rotator_dst_set_size, | ||
| 467 | .set_addr = rotator_dst_set_addr, | ||
| 468 | }; | ||
| 469 | |||
| 470 | static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | ||
| 471 | { | ||
| 472 | struct drm_exynos_ipp_prop_list *prop_list; | ||
| 473 | |||
| 474 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 475 | |||
| 476 | prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); | ||
| 477 | if (!prop_list) { | ||
| 478 | DRM_ERROR("failed to alloc property list.\n"); | ||
| 479 | return -ENOMEM; | ||
| 480 | } | ||
| 481 | |||
| 482 | prop_list->version = 1; | ||
| 483 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
| 484 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
| 485 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
| 486 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
| 487 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
| 488 | (1 << EXYNOS_DRM_DEGREE_270); | ||
| 489 | prop_list->csc = 0; | ||
| 490 | prop_list->crop = 0; | ||
| 491 | prop_list->scale = 0; | ||
| 492 | |||
| 493 | ippdrv->prop_list = prop_list; | ||
| 494 | |||
| 495 | return 0; | ||
| 496 | } | ||
| 497 | |||
| 498 | static inline bool rotator_check_drm_fmt(u32 fmt) | ||
| 499 | { | ||
| 500 | switch (fmt) { | ||
| 501 | case DRM_FORMAT_XRGB8888: | ||
| 502 | case DRM_FORMAT_NV12: | ||
| 503 | return true; | ||
| 504 | default: | ||
| 505 | DRM_DEBUG_KMS("%s:not support format\n", __func__); | ||
| 506 | return false; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip) | ||
| 511 | { | ||
| 512 | switch (flip) { | ||
| 513 | case EXYNOS_DRM_FLIP_NONE: | ||
| 514 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 515 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 516 | return true; | ||
| 517 | default: | ||
| 518 | DRM_DEBUG_KMS("%s:invalid flip\n", __func__); | ||
| 519 | return false; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | static int rotator_ippdrv_check_property(struct device *dev, | ||
| 524 | struct drm_exynos_ipp_property *property) | ||
| 525 | { | ||
| 526 | struct drm_exynos_ipp_config *src_config = | ||
| 527 | &property->config[EXYNOS_DRM_OPS_SRC]; | ||
| 528 | struct drm_exynos_ipp_config *dst_config = | ||
| 529 | &property->config[EXYNOS_DRM_OPS_DST]; | ||
| 530 | struct drm_exynos_pos *src_pos = &src_config->pos; | ||
| 531 | struct drm_exynos_pos *dst_pos = &dst_config->pos; | ||
| 532 | struct drm_exynos_sz *src_sz = &src_config->sz; | ||
| 533 | struct drm_exynos_sz *dst_sz = &dst_config->sz; | ||
| 534 | bool swap = false; | ||
| 535 | |||
| 536 | /* Check format configuration */ | ||
| 537 | if (src_config->fmt != dst_config->fmt) { | ||
| 538 | DRM_DEBUG_KMS("%s:not support csc feature\n", __func__); | ||
| 539 | return -EINVAL; | ||
| 540 | } | ||
| 541 | |||
| 542 | if (!rotator_check_drm_fmt(dst_config->fmt)) { | ||
| 543 | DRM_DEBUG_KMS("%s:invalid format\n", __func__); | ||
| 544 | return -EINVAL; | ||
| 545 | } | ||
| 546 | |||
| 547 | /* Check transform configuration */ | ||
| 548 | if (src_config->degree != EXYNOS_DRM_DEGREE_0) { | ||
| 549 | DRM_DEBUG_KMS("%s:not support source-side rotation\n", | ||
| 550 | __func__); | ||
| 551 | return -EINVAL; | ||
| 552 | } | ||
| 553 | |||
| 554 | switch (dst_config->degree) { | ||
| 555 | case EXYNOS_DRM_DEGREE_90: | ||
| 556 | case EXYNOS_DRM_DEGREE_270: | ||
| 557 | swap = true; | ||
| 558 | case EXYNOS_DRM_DEGREE_0: | ||
| 559 | case EXYNOS_DRM_DEGREE_180: | ||
| 560 | /* No problem */ | ||
| 561 | break; | ||
| 562 | default: | ||
| 563 | DRM_DEBUG_KMS("%s:invalid degree\n", __func__); | ||
| 564 | return -EINVAL; | ||
| 565 | } | ||
| 566 | |||
| 567 | if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { | ||
| 568 | DRM_DEBUG_KMS("%s:not support source-side flip\n", __func__); | ||
| 569 | return -EINVAL; | ||
| 570 | } | ||
| 571 | |||
| 572 | if (!rotator_check_drm_flip(dst_config->flip)) { | ||
| 573 | DRM_DEBUG_KMS("%s:invalid flip\n", __func__); | ||
| 574 | return -EINVAL; | ||
| 575 | } | ||
| 576 | |||
| 577 | /* Check size configuration */ | ||
| 578 | if ((src_pos->x + src_pos->w > src_sz->hsize) || | ||
| 579 | (src_pos->y + src_pos->h > src_sz->vsize)) { | ||
| 580 | DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__); | ||
| 581 | return -EINVAL; | ||
| 582 | } | ||
| 583 | |||
| 584 | if (swap) { | ||
| 585 | if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || | ||
| 586 | (dst_pos->y + dst_pos->w > dst_sz->hsize)) { | ||
| 587 | DRM_DEBUG_KMS("%s:out of destination buffer bound\n", | ||
| 588 | __func__); | ||
| 589 | return -EINVAL; | ||
| 590 | } | ||
| 591 | |||
| 592 | if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { | ||
| 593 | DRM_DEBUG_KMS("%s:not support scale feature\n", | ||
| 594 | __func__); | ||
| 595 | return -EINVAL; | ||
| 596 | } | ||
| 597 | } else { | ||
| 598 | if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || | ||
| 599 | (dst_pos->y + dst_pos->h > dst_sz->vsize)) { | ||
| 600 | DRM_DEBUG_KMS("%s:out of destination buffer bound\n", | ||
| 601 | __func__); | ||
| 602 | return -EINVAL; | ||
| 603 | } | ||
| 604 | |||
| 605 | if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { | ||
| 606 | DRM_DEBUG_KMS("%s:not support scale feature\n", | ||
| 607 | __func__); | ||
| 608 | return -EINVAL; | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | return 0; | ||
| 613 | } | ||
| 614 | |||
| 615 | static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | ||
| 616 | { | ||
| 617 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 618 | u32 val; | ||
| 619 | |||
| 620 | if (rot->suspended) { | ||
| 621 | DRM_ERROR("suspended state\n"); | ||
| 622 | return -EPERM; | ||
| 623 | } | ||
| 624 | |||
| 625 | if (cmd != IPP_CMD_M2M) { | ||
| 626 | DRM_ERROR("not support cmd: %d\n", cmd); | ||
| 627 | return -EINVAL; | ||
| 628 | } | ||
| 629 | |||
| 630 | /* Set interrupt enable */ | ||
| 631 | rotator_reg_set_irq(rot, true); | ||
| 632 | |||
| 633 | val = rot_read(ROT_CONTROL); | ||
| 634 | val |= ROT_CONTROL_START; | ||
| 635 | |||
| 636 | rot_write(val, ROT_CONTROL); | ||
| 637 | |||
| 638 | return 0; | ||
| 639 | } | ||
| 640 | |||
| 641 | static int __devinit rotator_probe(struct platform_device *pdev) | ||
| 642 | { | ||
| 643 | struct device *dev = &pdev->dev; | ||
| 644 | struct rot_context *rot; | ||
| 645 | struct exynos_drm_ippdrv *ippdrv; | ||
| 646 | int ret; | ||
| 647 | |||
| 648 | rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); | ||
| 649 | if (!rot) { | ||
| 650 | dev_err(dev, "failed to allocate rot\n"); | ||
| 651 | return -ENOMEM; | ||
| 652 | } | ||
| 653 | |||
| 654 | rot->limit_tbl = (struct rot_limit_table *) | ||
| 655 | platform_get_device_id(pdev)->driver_data; | ||
| 656 | |||
| 657 | rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 658 | if (!rot->regs_res) { | ||
| 659 | dev_err(dev, "failed to find registers\n"); | ||
| 660 | ret = -ENOENT; | ||
| 661 | goto err_get_resource; | ||
| 662 | } | ||
| 663 | |||
| 664 | rot->regs = devm_request_and_ioremap(dev, rot->regs_res); | ||
| 665 | if (!rot->regs) { | ||
| 666 | dev_err(dev, "failed to map register\n"); | ||
| 667 | ret = -ENXIO; | ||
| 668 | goto err_get_resource; | ||
| 669 | } | ||
| 670 | |||
| 671 | rot->irq = platform_get_irq(pdev, 0); | ||
| 672 | if (rot->irq < 0) { | ||
| 673 | dev_err(dev, "failed to get irq\n"); | ||
| 674 | ret = rot->irq; | ||
| 675 | goto err_get_irq; | ||
| 676 | } | ||
| 677 | |||
| 678 | ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler, | ||
| 679 | IRQF_ONESHOT, "drm_rotator", rot); | ||
| 680 | if (ret < 0) { | ||
| 681 | dev_err(dev, "failed to request irq\n"); | ||
| 682 | goto err_get_irq; | ||
| 683 | } | ||
| 684 | |||
| 685 | rot->clock = clk_get(dev, "rotator"); | ||
| 686 | if (IS_ERR_OR_NULL(rot->clock)) { | ||
| 687 | dev_err(dev, "failed to get clock\n"); | ||
| 688 | ret = PTR_ERR(rot->clock); | ||
| 689 | goto err_clk_get; | ||
| 690 | } | ||
| 691 | |||
| 692 | pm_runtime_enable(dev); | ||
| 693 | |||
| 694 | ippdrv = &rot->ippdrv; | ||
| 695 | ippdrv->dev = dev; | ||
| 696 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops; | ||
| 697 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops; | ||
| 698 | ippdrv->check_property = rotator_ippdrv_check_property; | ||
| 699 | ippdrv->start = rotator_ippdrv_start; | ||
| 700 | ret = rotator_init_prop_list(ippdrv); | ||
| 701 | if (ret < 0) { | ||
| 702 | dev_err(dev, "failed to init property list.\n"); | ||
| 703 | goto err_ippdrv_register; | ||
| 704 | } | ||
| 705 | |||
| 706 | DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv); | ||
| 707 | |||
| 708 | platform_set_drvdata(pdev, rot); | ||
| 709 | |||
| 710 | ret = exynos_drm_ippdrv_register(ippdrv); | ||
| 711 | if (ret < 0) { | ||
| 712 | dev_err(dev, "failed to register drm rotator device\n"); | ||
| 713 | goto err_ippdrv_register; | ||
| 714 | } | ||
| 715 | |||
| 716 | dev_info(dev, "The exynos rotator is probed successfully\n"); | ||
| 717 | |||
| 718 | return 0; | ||
| 719 | |||
| 720 | err_ippdrv_register: | ||
| 721 | devm_kfree(dev, ippdrv->prop_list); | ||
| 722 | pm_runtime_disable(dev); | ||
| 723 | clk_put(rot->clock); | ||
| 724 | err_clk_get: | ||
| 725 | free_irq(rot->irq, rot); | ||
| 726 | err_get_irq: | ||
| 727 | devm_iounmap(dev, rot->regs); | ||
| 728 | err_get_resource: | ||
| 729 | devm_kfree(dev, rot); | ||
| 730 | return ret; | ||
| 731 | } | ||
| 732 | |||
| 733 | static int __devexit rotator_remove(struct platform_device *pdev) | ||
| 734 | { | ||
| 735 | struct device *dev = &pdev->dev; | ||
| 736 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 737 | struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; | ||
| 738 | |||
| 739 | devm_kfree(dev, ippdrv->prop_list); | ||
| 740 | exynos_drm_ippdrv_unregister(ippdrv); | ||
| 741 | |||
| 742 | pm_runtime_disable(dev); | ||
| 743 | clk_put(rot->clock); | ||
| 744 | |||
| 745 | free_irq(rot->irq, rot); | ||
| 746 | devm_iounmap(dev, rot->regs); | ||
| 747 | |||
| 748 | devm_kfree(dev, rot); | ||
| 749 | |||
| 750 | return 0; | ||
| 751 | } | ||
| 752 | |||
| 753 | struct rot_limit_table rot_limit_tbl = { | ||
| 754 | .ycbcr420_2p = { | ||
| 755 | .min_w = 32, | ||
| 756 | .min_h = 32, | ||
| 757 | .max_w = SZ_32K, | ||
| 758 | .max_h = SZ_32K, | ||
| 759 | .align = 3, | ||
| 760 | }, | ||
| 761 | .rgb888 = { | ||
| 762 | .min_w = 8, | ||
| 763 | .min_h = 8, | ||
| 764 | .max_w = SZ_8K, | ||
| 765 | .max_h = SZ_8K, | ||
| 766 | .align = 2, | ||
| 767 | }, | ||
| 768 | }; | ||
| 769 | |||
| 770 | struct platform_device_id rotator_driver_ids[] = { | ||
| 771 | { | ||
| 772 | .name = "exynos-rot", | ||
| 773 | .driver_data = (unsigned long)&rot_limit_tbl, | ||
| 774 | }, | ||
| 775 | {}, | ||
| 776 | }; | ||
| 777 | |||
| 778 | static int rotator_clk_crtl(struct rot_context *rot, bool enable) | ||
| 779 | { | ||
| 780 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 781 | |||
| 782 | if (enable) { | ||
| 783 | clk_enable(rot->clock); | ||
| 784 | rot->suspended = false; | ||
| 785 | } else { | ||
| 786 | clk_disable(rot->clock); | ||
| 787 | rot->suspended = true; | ||
| 788 | } | ||
| 789 | |||
| 790 | return 0; | ||
| 791 | } | ||
| 792 | |||
| 793 | |||
| 794 | #ifdef CONFIG_PM_SLEEP | ||
| 795 | static int rotator_suspend(struct device *dev) | ||
| 796 | { | ||
| 797 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 798 | |||
| 799 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 800 | |||
| 801 | if (pm_runtime_suspended(dev)) | ||
| 802 | return 0; | ||
| 803 | |||
| 804 | return rotator_clk_crtl(rot, false); | ||
| 805 | } | ||
| 806 | |||
| 807 | static int rotator_resume(struct device *dev) | ||
| 808 | { | ||
| 809 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 810 | |||
| 811 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 812 | |||
| 813 | if (!pm_runtime_suspended(dev)) | ||
| 814 | return rotator_clk_crtl(rot, true); | ||
| 815 | |||
| 816 | return 0; | ||
| 817 | } | ||
| 818 | #endif | ||
| 819 | |||
| 820 | #ifdef CONFIG_PM_RUNTIME | ||
| 821 | static int rotator_runtime_suspend(struct device *dev) | ||
| 822 | { | ||
| 823 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 824 | |||
| 825 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 826 | |||
| 827 | return rotator_clk_crtl(rot, false); | ||
| 828 | } | ||
| 829 | |||
| 830 | static int rotator_runtime_resume(struct device *dev) | ||
| 831 | { | ||
| 832 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 833 | |||
| 834 | DRM_DEBUG_KMS("%s\n", __func__); | ||
| 835 | |||
| 836 | return rotator_clk_crtl(rot, true); | ||
| 837 | } | ||
| 838 | #endif | ||
| 839 | |||
| 840 | static const struct dev_pm_ops rotator_pm_ops = { | ||
| 841 | SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume) | ||
| 842 | SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume, | ||
| 843 | NULL) | ||
| 844 | }; | ||
| 845 | |||
| 846 | struct platform_driver rotator_driver = { | ||
| 847 | .probe = rotator_probe, | ||
| 848 | .remove = __devexit_p(rotator_remove), | ||
| 849 | .id_table = rotator_driver_ids, | ||
| 850 | .driver = { | ||
| 851 | .name = "exynos-rot", | ||
| 852 | .owner = THIS_MODULE, | ||
| 853 | .pm = &rotator_pm_ops, | ||
| 854 | }, | ||
| 855 | }; | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h new file mode 100644 index 000000000000..a2d7a14a52b6 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * YoungJun Cho <yj44.cho@samsung.com> | ||
| 6 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 7 | * | ||
| 8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 9 | * copy of this software and associated documentation files (the "Software"), | ||
| 10 | * to deal in the Software without restriction, including without limitation | ||
| 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
| 12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
| 13 | * Software is furnished to do so, subject to the following conditions: | ||
| 14 | * | ||
| 15 | * The above copyright notice and this permission notice (including the next | ||
| 16 | * paragraph) shall be included in all copies or substantial portions of the | ||
| 17 | * Software. | ||
| 18 | * | ||
| 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
| 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
| 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
| 25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #ifndef _EXYNOS_DRM_ROTATOR_H_ | ||
| 29 | #define _EXYNOS_DRM_ROTATOR_H_ | ||
| 30 | |||
| 31 | /* TODO */ | ||
| 32 | |||
| 33 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 4b0c16bfd1da..99bfc38dfaa2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c | |||
| @@ -39,7 +39,6 @@ struct vidi_win_data { | |||
| 39 | unsigned int fb_height; | 39 | unsigned int fb_height; |
| 40 | unsigned int bpp; | 40 | unsigned int bpp; |
| 41 | dma_addr_t dma_addr; | 41 | dma_addr_t dma_addr; |
| 42 | void __iomem *vaddr; | ||
| 43 | unsigned int buf_offsize; | 42 | unsigned int buf_offsize; |
| 44 | unsigned int line_size; /* bytes */ | 43 | unsigned int line_size; /* bytes */ |
| 45 | bool enabled; | 44 | bool enabled; |
| @@ -294,7 +293,6 @@ static void vidi_win_mode_set(struct device *dev, | |||
| 294 | win_data->fb_width = overlay->fb_width; | 293 | win_data->fb_width = overlay->fb_width; |
| 295 | win_data->fb_height = overlay->fb_height; | 294 | win_data->fb_height = overlay->fb_height; |
| 296 | win_data->dma_addr = overlay->dma_addr[0] + offset; | 295 | win_data->dma_addr = overlay->dma_addr[0] + offset; |
| 297 | win_data->vaddr = overlay->vaddr[0] + offset; | ||
| 298 | win_data->bpp = overlay->bpp; | 296 | win_data->bpp = overlay->bpp; |
| 299 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * | 297 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * |
| 300 | (overlay->bpp >> 3); | 298 | (overlay->bpp >> 3); |
| @@ -309,9 +307,7 @@ static void vidi_win_mode_set(struct device *dev, | |||
| 309 | win_data->offset_x, win_data->offset_y); | 307 | win_data->offset_x, win_data->offset_y); |
| 310 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", | 308 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", |
| 311 | win_data->ovl_width, win_data->ovl_height); | 309 | win_data->ovl_width, win_data->ovl_height); |
| 312 | DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", | 310 | DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); |
| 313 | (unsigned long)win_data->dma_addr, | ||
| 314 | (unsigned long)win_data->vaddr); | ||
| 315 | DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", | 311 | DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", |
| 316 | overlay->fb_width, overlay->crtc_width); | 312 | overlay->fb_width, overlay->crtc_width); |
| 317 | } | 313 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index bafb65389562..2c46b6c0b82c 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c | |||
| @@ -2003,6 +2003,24 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) | |||
| 2003 | mdelay(10); | 2003 | mdelay(10); |
| 2004 | } | 2004 | } |
| 2005 | 2005 | ||
| 2006 | static void hdmiphy_poweron(struct hdmi_context *hdata) | ||
| 2007 | { | ||
| 2008 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 2009 | |||
| 2010 | if (hdata->type == HDMI_TYPE14) | ||
| 2011 | hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, | ||
| 2012 | HDMI_PHY_POWER_OFF_EN); | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | static void hdmiphy_poweroff(struct hdmi_context *hdata) | ||
| 2016 | { | ||
| 2017 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 2018 | |||
| 2019 | if (hdata->type == HDMI_TYPE14) | ||
| 2020 | hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, | ||
| 2021 | HDMI_PHY_POWER_OFF_EN); | ||
| 2022 | } | ||
| 2023 | |||
| 2006 | static void hdmiphy_conf_apply(struct hdmi_context *hdata) | 2024 | static void hdmiphy_conf_apply(struct hdmi_context *hdata) |
| 2007 | { | 2025 | { |
| 2008 | const u8 *hdmiphy_data; | 2026 | const u8 *hdmiphy_data; |
| @@ -2171,12 +2189,12 @@ static void hdmi_poweron(struct hdmi_context *hdata) | |||
| 2171 | 2189 | ||
| 2172 | mutex_unlock(&hdata->hdmi_mutex); | 2190 | mutex_unlock(&hdata->hdmi_mutex); |
| 2173 | 2191 | ||
| 2174 | pm_runtime_get_sync(hdata->dev); | ||
| 2175 | |||
| 2176 | regulator_bulk_enable(res->regul_count, res->regul_bulk); | 2192 | regulator_bulk_enable(res->regul_count, res->regul_bulk); |
| 2177 | clk_enable(res->hdmiphy); | 2193 | clk_enable(res->hdmiphy); |
| 2178 | clk_enable(res->hdmi); | 2194 | clk_enable(res->hdmi); |
| 2179 | clk_enable(res->sclk_hdmi); | 2195 | clk_enable(res->sclk_hdmi); |
| 2196 | |||
| 2197 | hdmiphy_poweron(hdata); | ||
| 2180 | } | 2198 | } |
| 2181 | 2199 | ||
| 2182 | static void hdmi_poweroff(struct hdmi_context *hdata) | 2200 | static void hdmi_poweroff(struct hdmi_context *hdata) |
| @@ -2195,14 +2213,13 @@ static void hdmi_poweroff(struct hdmi_context *hdata) | |||
| 2195 | * its reset state seems to meet the condition. | 2213 | * its reset state seems to meet the condition. |
| 2196 | */ | 2214 | */ |
| 2197 | hdmiphy_conf_reset(hdata); | 2215 | hdmiphy_conf_reset(hdata); |
| 2216 | hdmiphy_poweroff(hdata); | ||
| 2198 | 2217 | ||
| 2199 | clk_disable(res->sclk_hdmi); | 2218 | clk_disable(res->sclk_hdmi); |
| 2200 | clk_disable(res->hdmi); | 2219 | clk_disable(res->hdmi); |
| 2201 | clk_disable(res->hdmiphy); | 2220 | clk_disable(res->hdmiphy); |
| 2202 | regulator_bulk_disable(res->regul_count, res->regul_bulk); | 2221 | regulator_bulk_disable(res->regul_count, res->regul_bulk); |
| 2203 | 2222 | ||
| 2204 | pm_runtime_put_sync(hdata->dev); | ||
| 2205 | |||
| 2206 | mutex_lock(&hdata->hdmi_mutex); | 2223 | mutex_lock(&hdata->hdmi_mutex); |
| 2207 | 2224 | ||
| 2208 | hdata->powered = false; | 2225 | hdata->powered = false; |
| @@ -2215,16 +2232,18 @@ static void hdmi_dpms(void *ctx, int mode) | |||
| 2215 | { | 2232 | { |
| 2216 | struct hdmi_context *hdata = ctx; | 2233 | struct hdmi_context *hdata = ctx; |
| 2217 | 2234 | ||
| 2218 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | 2235 | DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode); |
| 2219 | 2236 | ||
| 2220 | switch (mode) { | 2237 | switch (mode) { |
| 2221 | case DRM_MODE_DPMS_ON: | 2238 | case DRM_MODE_DPMS_ON: |
| 2222 | hdmi_poweron(hdata); | 2239 | if (pm_runtime_suspended(hdata->dev)) |
| 2240 | pm_runtime_get_sync(hdata->dev); | ||
| 2223 | break; | 2241 | break; |
| 2224 | case DRM_MODE_DPMS_STANDBY: | 2242 | case DRM_MODE_DPMS_STANDBY: |
| 2225 | case DRM_MODE_DPMS_SUSPEND: | 2243 | case DRM_MODE_DPMS_SUSPEND: |
| 2226 | case DRM_MODE_DPMS_OFF: | 2244 | case DRM_MODE_DPMS_OFF: |
| 2227 | hdmi_poweroff(hdata); | 2245 | if (!pm_runtime_suspended(hdata->dev)) |
| 2246 | pm_runtime_put_sync(hdata->dev); | ||
| 2228 | break; | 2247 | break; |
| 2229 | default: | 2248 | default: |
| 2230 | DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); | 2249 | DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); |
| @@ -2421,6 +2440,7 @@ static struct platform_device_id hdmi_driver_types[] = { | |||
| 2421 | } | 2440 | } |
| 2422 | }; | 2441 | }; |
| 2423 | 2442 | ||
| 2443 | #ifdef CONFIG_OF | ||
| 2424 | static struct of_device_id hdmi_match_types[] = { | 2444 | static struct of_device_id hdmi_match_types[] = { |
| 2425 | { | 2445 | { |
| 2426 | .compatible = "samsung,exynos5-hdmi", | 2446 | .compatible = "samsung,exynos5-hdmi", |
| @@ -2429,6 +2449,7 @@ static struct of_device_id hdmi_match_types[] = { | |||
| 2429 | /* end node */ | 2449 | /* end node */ |
| 2430 | } | 2450 | } |
| 2431 | }; | 2451 | }; |
| 2452 | #endif | ||
| 2432 | 2453 | ||
| 2433 | static int __devinit hdmi_probe(struct platform_device *pdev) | 2454 | static int __devinit hdmi_probe(struct platform_device *pdev) |
| 2434 | { | 2455 | { |
| @@ -2481,6 +2502,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev) | |||
| 2481 | const struct of_device_id *match; | 2502 | const struct of_device_id *match; |
| 2482 | match = of_match_node(of_match_ptr(hdmi_match_types), | 2503 | match = of_match_node(of_match_ptr(hdmi_match_types), |
| 2483 | pdev->dev.of_node); | 2504 | pdev->dev.of_node); |
| 2505 | if (match == NULL) | ||
| 2506 | return -ENODEV; | ||
| 2484 | hdata->type = (enum hdmi_type)match->data; | 2507 | hdata->type = (enum hdmi_type)match->data; |
| 2485 | } else { | 2508 | } else { |
| 2486 | hdata->type = (enum hdmi_type)platform_get_device_id | 2509 | hdata->type = (enum hdmi_type)platform_get_device_id |
| @@ -2612,6 +2635,8 @@ static int hdmi_suspend(struct device *dev) | |||
| 2612 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); | 2635 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); |
| 2613 | struct hdmi_context *hdata = ctx->ctx; | 2636 | struct hdmi_context *hdata = ctx->ctx; |
| 2614 | 2637 | ||
| 2638 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 2639 | |||
| 2615 | disable_irq(hdata->internal_irq); | 2640 | disable_irq(hdata->internal_irq); |
| 2616 | disable_irq(hdata->external_irq); | 2641 | disable_irq(hdata->external_irq); |
| 2617 | 2642 | ||
| @@ -2619,6 +2644,11 @@ static int hdmi_suspend(struct device *dev) | |||
| 2619 | if (ctx->drm_dev) | 2644 | if (ctx->drm_dev) |
| 2620 | drm_helper_hpd_irq_event(ctx->drm_dev); | 2645 | drm_helper_hpd_irq_event(ctx->drm_dev); |
| 2621 | 2646 | ||
| 2647 | if (pm_runtime_suspended(dev)) { | ||
| 2648 | DRM_DEBUG_KMS("%s : Already suspended\n", __func__); | ||
| 2649 | return 0; | ||
| 2650 | } | ||
| 2651 | |||
| 2622 | hdmi_poweroff(hdata); | 2652 | hdmi_poweroff(hdata); |
| 2623 | 2653 | ||
| 2624 | return 0; | 2654 | return 0; |
| @@ -2629,13 +2659,52 @@ static int hdmi_resume(struct device *dev) | |||
| 2629 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); | 2659 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); |
| 2630 | struct hdmi_context *hdata = ctx->ctx; | 2660 | struct hdmi_context *hdata = ctx->ctx; |
| 2631 | 2661 | ||
| 2662 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 2663 | |||
| 2664 | hdata->hpd = gpio_get_value(hdata->hpd_gpio); | ||
| 2665 | |||
| 2632 | enable_irq(hdata->external_irq); | 2666 | enable_irq(hdata->external_irq); |
| 2633 | enable_irq(hdata->internal_irq); | 2667 | enable_irq(hdata->internal_irq); |
| 2668 | |||
| 2669 | if (!pm_runtime_suspended(dev)) { | ||
| 2670 | DRM_DEBUG_KMS("%s : Already resumed\n", __func__); | ||
| 2671 | return 0; | ||
| 2672 | } | ||
| 2673 | |||
| 2674 | hdmi_poweron(hdata); | ||
| 2675 | |||
| 2676 | return 0; | ||
| 2677 | } | ||
| 2678 | #endif | ||
| 2679 | |||
| 2680 | #ifdef CONFIG_PM_RUNTIME | ||
| 2681 | static int hdmi_runtime_suspend(struct device *dev) | ||
| 2682 | { | ||
| 2683 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); | ||
| 2684 | struct hdmi_context *hdata = ctx->ctx; | ||
| 2685 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 2686 | |||
| 2687 | hdmi_poweroff(hdata); | ||
| 2688 | |||
| 2689 | return 0; | ||
| 2690 | } | ||
| 2691 | |||
| 2692 | static int hdmi_runtime_resume(struct device *dev) | ||
| 2693 | { | ||
| 2694 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); | ||
| 2695 | struct hdmi_context *hdata = ctx->ctx; | ||
| 2696 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 2697 | |||
| 2698 | hdmi_poweron(hdata); | ||
| 2699 | |||
| 2634 | return 0; | 2700 | return 0; |
| 2635 | } | 2701 | } |
| 2636 | #endif | 2702 | #endif |
| 2637 | 2703 | ||
| 2638 | static SIMPLE_DEV_PM_OPS(hdmi_pm_ops, hdmi_suspend, hdmi_resume); | 2704 | static const struct dev_pm_ops hdmi_pm_ops = { |
| 2705 | SET_SYSTEM_SLEEP_PM_OPS(hdmi_suspend, hdmi_resume) | ||
| 2706 | SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL) | ||
| 2707 | }; | ||
| 2639 | 2708 | ||
| 2640 | struct platform_driver hdmi_driver = { | 2709 | struct platform_driver hdmi_driver = { |
| 2641 | .probe = hdmi_probe, | 2710 | .probe = hdmi_probe, |
| @@ -2645,6 +2714,6 @@ struct platform_driver hdmi_driver = { | |||
| 2645 | .name = "exynos-hdmi", | 2714 | .name = "exynos-hdmi", |
| 2646 | .owner = THIS_MODULE, | 2715 | .owner = THIS_MODULE, |
| 2647 | .pm = &hdmi_pm_ops, | 2716 | .pm = &hdmi_pm_ops, |
| 2648 | .of_match_table = hdmi_match_types, | 2717 | .of_match_table = of_match_ptr(hdmi_match_types), |
| 2649 | }, | 2718 | }, |
| 2650 | }; | 2719 | }; |
diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c index 27d1720f1bbd..6206056f4a33 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c | |||
| @@ -46,6 +46,7 @@ static const struct i2c_device_id hdmiphy_id[] = { | |||
| 46 | { }, | 46 | { }, |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | #ifdef CONFIG_OF | ||
| 49 | static struct of_device_id hdmiphy_match_types[] = { | 50 | static struct of_device_id hdmiphy_match_types[] = { |
| 50 | { | 51 | { |
| 51 | .compatible = "samsung,exynos5-hdmiphy", | 52 | .compatible = "samsung,exynos5-hdmiphy", |
| @@ -53,12 +54,13 @@ static struct of_device_id hdmiphy_match_types[] = { | |||
| 53 | /* end node */ | 54 | /* end node */ |
| 54 | } | 55 | } |
| 55 | }; | 56 | }; |
| 57 | #endif | ||
| 56 | 58 | ||
| 57 | struct i2c_driver hdmiphy_driver = { | 59 | struct i2c_driver hdmiphy_driver = { |
| 58 | .driver = { | 60 | .driver = { |
| 59 | .name = "exynos-hdmiphy", | 61 | .name = "exynos-hdmiphy", |
| 60 | .owner = THIS_MODULE, | 62 | .owner = THIS_MODULE, |
| 61 | .of_match_table = hdmiphy_match_types, | 63 | .of_match_table = of_match_ptr(hdmiphy_match_types), |
| 62 | }, | 64 | }, |
| 63 | .id_table = hdmiphy_id, | 65 | .id_table = hdmiphy_id, |
| 64 | .probe = hdmiphy_probe, | 66 | .probe = hdmiphy_probe, |
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 40a6e1906fbb..21db89530fc7 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c | |||
| @@ -42,9 +42,7 @@ | |||
| 42 | 42 | ||
| 43 | struct hdmi_win_data { | 43 | struct hdmi_win_data { |
| 44 | dma_addr_t dma_addr; | 44 | dma_addr_t dma_addr; |
| 45 | void __iomem *vaddr; | ||
| 46 | dma_addr_t chroma_dma_addr; | 45 | dma_addr_t chroma_dma_addr; |
| 47 | void __iomem *chroma_vaddr; | ||
| 48 | uint32_t pixel_format; | 46 | uint32_t pixel_format; |
| 49 | unsigned int bpp; | 47 | unsigned int bpp; |
| 50 | unsigned int crtc_x; | 48 | unsigned int crtc_x; |
| @@ -60,6 +58,8 @@ struct hdmi_win_data { | |||
| 60 | unsigned int mode_width; | 58 | unsigned int mode_width; |
| 61 | unsigned int mode_height; | 59 | unsigned int mode_height; |
| 62 | unsigned int scan_flags; | 60 | unsigned int scan_flags; |
| 61 | bool enabled; | ||
| 62 | bool resume; | ||
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| 65 | struct mixer_resources { | 65 | struct mixer_resources { |
| @@ -93,6 +93,8 @@ struct mixer_context { | |||
| 93 | struct hdmi_win_data win_data[MIXER_WIN_NR]; | 93 | struct hdmi_win_data win_data[MIXER_WIN_NR]; |
| 94 | enum mixer_version_id mxr_ver; | 94 | enum mixer_version_id mxr_ver; |
| 95 | void *parent_ctx; | 95 | void *parent_ctx; |
| 96 | wait_queue_head_t wait_vsync_queue; | ||
| 97 | atomic_t wait_vsync_event; | ||
| 96 | }; | 98 | }; |
| 97 | 99 | ||
| 98 | struct mixer_drv_data { | 100 | struct mixer_drv_data { |
| @@ -686,60 +688,6 @@ static int mixer_iommu_on(void *ctx, bool enable) | |||
| 686 | return 0; | 688 | return 0; |
| 687 | } | 689 | } |
| 688 | 690 | ||
| 689 | static void mixer_poweron(struct mixer_context *ctx) | ||
| 690 | { | ||
| 691 | struct mixer_resources *res = &ctx->mixer_res; | ||
| 692 | |||
| 693 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 694 | |||
| 695 | mutex_lock(&ctx->mixer_mutex); | ||
| 696 | if (ctx->powered) { | ||
| 697 | mutex_unlock(&ctx->mixer_mutex); | ||
| 698 | return; | ||
| 699 | } | ||
| 700 | ctx->powered = true; | ||
| 701 | mutex_unlock(&ctx->mixer_mutex); | ||
| 702 | |||
| 703 | pm_runtime_get_sync(ctx->dev); | ||
| 704 | |||
| 705 | clk_enable(res->mixer); | ||
| 706 | if (ctx->vp_enabled) { | ||
| 707 | clk_enable(res->vp); | ||
| 708 | clk_enable(res->sclk_mixer); | ||
| 709 | } | ||
| 710 | |||
| 711 | mixer_reg_write(res, MXR_INT_EN, ctx->int_en); | ||
| 712 | mixer_win_reset(ctx); | ||
| 713 | } | ||
| 714 | |||
| 715 | static void mixer_poweroff(struct mixer_context *ctx) | ||
| 716 | { | ||
| 717 | struct mixer_resources *res = &ctx->mixer_res; | ||
| 718 | |||
| 719 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 720 | |||
| 721 | mutex_lock(&ctx->mixer_mutex); | ||
| 722 | if (!ctx->powered) | ||
| 723 | goto out; | ||
| 724 | mutex_unlock(&ctx->mixer_mutex); | ||
| 725 | |||
| 726 | ctx->int_en = mixer_reg_read(res, MXR_INT_EN); | ||
| 727 | |||
| 728 | clk_disable(res->mixer); | ||
| 729 | if (ctx->vp_enabled) { | ||
| 730 | clk_disable(res->vp); | ||
| 731 | clk_disable(res->sclk_mixer); | ||
| 732 | } | ||
| 733 | |||
| 734 | pm_runtime_put_sync(ctx->dev); | ||
| 735 | |||
| 736 | mutex_lock(&ctx->mixer_mutex); | ||
| 737 | ctx->powered = false; | ||
| 738 | |||
| 739 | out: | ||
| 740 | mutex_unlock(&ctx->mixer_mutex); | ||
| 741 | } | ||
| 742 | |||
| 743 | static int mixer_enable_vblank(void *ctx, int pipe) | 691 | static int mixer_enable_vblank(void *ctx, int pipe) |
| 744 | { | 692 | { |
| 745 | struct mixer_context *mixer_ctx = ctx; | 693 | struct mixer_context *mixer_ctx = ctx; |
| @@ -767,39 +715,6 @@ static void mixer_disable_vblank(void *ctx) | |||
| 767 | mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); | 715 | mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); |
| 768 | } | 716 | } |
| 769 | 717 | ||
| 770 | static void mixer_dpms(void *ctx, int mode) | ||
| 771 | { | ||
| 772 | struct mixer_context *mixer_ctx = ctx; | ||
| 773 | |||
| 774 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 775 | |||
| 776 | switch (mode) { | ||
| 777 | case DRM_MODE_DPMS_ON: | ||
| 778 | mixer_poweron(mixer_ctx); | ||
| 779 | break; | ||
| 780 | case DRM_MODE_DPMS_STANDBY: | ||
| 781 | case DRM_MODE_DPMS_SUSPEND: | ||
| 782 | case DRM_MODE_DPMS_OFF: | ||
| 783 | mixer_poweroff(mixer_ctx); | ||
| 784 | break; | ||
| 785 | default: | ||
| 786 | DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); | ||
| 787 | break; | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 791 | static void mixer_wait_for_vblank(void *ctx) | ||
| 792 | { | ||
| 793 | struct mixer_context *mixer_ctx = ctx; | ||
| 794 | struct mixer_resources *res = &mixer_ctx->mixer_res; | ||
| 795 | int ret; | ||
| 796 | |||
| 797 | ret = wait_for((mixer_reg_read(res, MXR_INT_STATUS) & | ||
| 798 | MXR_INT_STATUS_VSYNC), 50); | ||
| 799 | if (ret < 0) | ||
| 800 | DRM_DEBUG_KMS("vblank wait timed out.\n"); | ||
| 801 | } | ||
| 802 | |||
| 803 | static void mixer_win_mode_set(void *ctx, | 718 | static void mixer_win_mode_set(void *ctx, |
| 804 | struct exynos_drm_overlay *overlay) | 719 | struct exynos_drm_overlay *overlay) |
| 805 | { | 720 | { |
| @@ -832,9 +747,7 @@ static void mixer_win_mode_set(void *ctx, | |||
| 832 | win_data = &mixer_ctx->win_data[win]; | 747 | win_data = &mixer_ctx->win_data[win]; |
| 833 | 748 | ||
| 834 | win_data->dma_addr = overlay->dma_addr[0]; | 749 | win_data->dma_addr = overlay->dma_addr[0]; |
| 835 | win_data->vaddr = overlay->vaddr[0]; | ||
| 836 | win_data->chroma_dma_addr = overlay->dma_addr[1]; | 750 | win_data->chroma_dma_addr = overlay->dma_addr[1]; |
| 837 | win_data->chroma_vaddr = overlay->vaddr[1]; | ||
| 838 | win_data->pixel_format = overlay->pixel_format; | 751 | win_data->pixel_format = overlay->pixel_format; |
| 839 | win_data->bpp = overlay->bpp; | 752 | win_data->bpp = overlay->bpp; |
| 840 | 753 | ||
| @@ -866,6 +779,8 @@ static void mixer_win_commit(void *ctx, int win) | |||
| 866 | vp_video_buffer(mixer_ctx, win); | 779 | vp_video_buffer(mixer_ctx, win); |
| 867 | else | 780 | else |
| 868 | mixer_graph_buffer(mixer_ctx, win); | 781 | mixer_graph_buffer(mixer_ctx, win); |
| 782 | |||
| 783 | mixer_ctx->win_data[win].enabled = true; | ||
| 869 | } | 784 | } |
| 870 | 785 | ||
| 871 | static void mixer_win_disable(void *ctx, int win) | 786 | static void mixer_win_disable(void *ctx, int win) |
| @@ -876,6 +791,14 @@ static void mixer_win_disable(void *ctx, int win) | |||
| 876 | 791 | ||
| 877 | DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); | 792 | DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); |
| 878 | 793 | ||
| 794 | mutex_lock(&mixer_ctx->mixer_mutex); | ||
| 795 | if (!mixer_ctx->powered) { | ||
| 796 | mutex_unlock(&mixer_ctx->mixer_mutex); | ||
| 797 | mixer_ctx->win_data[win].resume = false; | ||
| 798 | return; | ||
| 799 | } | ||
| 800 | mutex_unlock(&mixer_ctx->mixer_mutex); | ||
| 801 | |||
| 879 | spin_lock_irqsave(&res->reg_slock, flags); | 802 | spin_lock_irqsave(&res->reg_slock, flags); |
| 880 | mixer_vsync_set_update(mixer_ctx, false); | 803 | mixer_vsync_set_update(mixer_ctx, false); |
| 881 | 804 | ||
| @@ -883,6 +806,133 @@ static void mixer_win_disable(void *ctx, int win) | |||
| 883 | 806 | ||
| 884 | mixer_vsync_set_update(mixer_ctx, true); | 807 | mixer_vsync_set_update(mixer_ctx, true); |
| 885 | spin_unlock_irqrestore(&res->reg_slock, flags); | 808 | spin_unlock_irqrestore(&res->reg_slock, flags); |
| 809 | |||
| 810 | mixer_ctx->win_data[win].enabled = false; | ||
| 811 | } | ||
| 812 | |||
| 813 | static void mixer_wait_for_vblank(void *ctx) | ||
| 814 | { | ||
| 815 | struct mixer_context *mixer_ctx = ctx; | ||
| 816 | |||
| 817 | mutex_lock(&mixer_ctx->mixer_mutex); | ||
| 818 | if (!mixer_ctx->powered) { | ||
| 819 | mutex_unlock(&mixer_ctx->mixer_mutex); | ||
| 820 | return; | ||
| 821 | } | ||
| 822 | mutex_unlock(&mixer_ctx->mixer_mutex); | ||
| 823 | |||
| 824 | atomic_set(&mixer_ctx->wait_vsync_event, 1); | ||
| 825 | |||
| 826 | /* | ||
| 827 | * wait for MIXER to signal VSYNC interrupt or return after | ||
| 828 | * timeout which is set to 50ms (refresh rate of 20). | ||
| 829 | */ | ||
| 830 | if (!wait_event_timeout(mixer_ctx->wait_vsync_queue, | ||
| 831 | !atomic_read(&mixer_ctx->wait_vsync_event), | ||
| 832 | DRM_HZ/20)) | ||
| 833 | DRM_DEBUG_KMS("vblank wait timed out.\n"); | ||
| 834 | } | ||
| 835 | |||
| 836 | static void mixer_window_suspend(struct mixer_context *ctx) | ||
| 837 | { | ||
| 838 | struct hdmi_win_data *win_data; | ||
| 839 | int i; | ||
| 840 | |||
| 841 | for (i = 0; i < MIXER_WIN_NR; i++) { | ||
| 842 | win_data = &ctx->win_data[i]; | ||
| 843 | win_data->resume = win_data->enabled; | ||
| 844 | mixer_win_disable(ctx, i); | ||
| 845 | } | ||
| 846 | mixer_wait_for_vblank(ctx); | ||
| 847 | } | ||
| 848 | |||
| 849 | static void mixer_window_resume(struct mixer_context *ctx) | ||
| 850 | { | ||
| 851 | struct hdmi_win_data *win_data; | ||
| 852 | int i; | ||
| 853 | |||
| 854 | for (i = 0; i < MIXER_WIN_NR; i++) { | ||
| 855 | win_data = &ctx->win_data[i]; | ||
| 856 | win_data->enabled = win_data->resume; | ||
| 857 | win_data->resume = false; | ||
| 858 | } | ||
| 859 | } | ||
| 860 | |||
| 861 | static void mixer_poweron(struct mixer_context *ctx) | ||
| 862 | { | ||
| 863 | struct mixer_resources *res = &ctx->mixer_res; | ||
| 864 | |||
| 865 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 866 | |||
| 867 | mutex_lock(&ctx->mixer_mutex); | ||
| 868 | if (ctx->powered) { | ||
| 869 | mutex_unlock(&ctx->mixer_mutex); | ||
| 870 | return; | ||
| 871 | } | ||
| 872 | ctx->powered = true; | ||
| 873 | mutex_unlock(&ctx->mixer_mutex); | ||
| 874 | |||
| 875 | clk_enable(res->mixer); | ||
| 876 | if (ctx->vp_enabled) { | ||
| 877 | clk_enable(res->vp); | ||
| 878 | clk_enable(res->sclk_mixer); | ||
| 879 | } | ||
| 880 | |||
| 881 | mixer_reg_write(res, MXR_INT_EN, ctx->int_en); | ||
| 882 | mixer_win_reset(ctx); | ||
| 883 | |||
| 884 | mixer_window_resume(ctx); | ||
| 885 | } | ||
| 886 | |||
| 887 | static void mixer_poweroff(struct mixer_context *ctx) | ||
| 888 | { | ||
| 889 | struct mixer_resources *res = &ctx->mixer_res; | ||
| 890 | |||
| 891 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 892 | |||
| 893 | mutex_lock(&ctx->mixer_mutex); | ||
| 894 | if (!ctx->powered) | ||
| 895 | goto out; | ||
| 896 | mutex_unlock(&ctx->mixer_mutex); | ||
| 897 | |||
| 898 | mixer_window_suspend(ctx); | ||
| 899 | |||
| 900 | ctx->int_en = mixer_reg_read(res, MXR_INT_EN); | ||
| 901 | |||
| 902 | clk_disable(res->mixer); | ||
| 903 | if (ctx->vp_enabled) { | ||
| 904 | clk_disable(res->vp); | ||
| 905 | clk_disable(res->sclk_mixer); | ||
| 906 | } | ||
| 907 | |||
| 908 | mutex_lock(&ctx->mixer_mutex); | ||
| 909 | ctx->powered = false; | ||
| 910 | |||
| 911 | out: | ||
| 912 | mutex_unlock(&ctx->mixer_mutex); | ||
| 913 | } | ||
| 914 | |||
| 915 | static void mixer_dpms(void *ctx, int mode) | ||
| 916 | { | ||
| 917 | struct mixer_context *mixer_ctx = ctx; | ||
| 918 | |||
| 919 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 920 | |||
| 921 | switch (mode) { | ||
| 922 | case DRM_MODE_DPMS_ON: | ||
| 923 | if (pm_runtime_suspended(mixer_ctx->dev)) | ||
| 924 | pm_runtime_get_sync(mixer_ctx->dev); | ||
| 925 | break; | ||
| 926 | case DRM_MODE_DPMS_STANDBY: | ||
| 927 | case DRM_MODE_DPMS_SUSPEND: | ||
| 928 | case DRM_MODE_DPMS_OFF: | ||
| 929 | if (!pm_runtime_suspended(mixer_ctx->dev)) | ||
| 930 | pm_runtime_put_sync(mixer_ctx->dev); | ||
| 931 | break; | ||
| 932 | default: | ||
| 933 | DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); | ||
| 934 | break; | ||
| 935 | } | ||
| 886 | } | 936 | } |
| 887 | 937 | ||
| 888 | static struct exynos_mixer_ops mixer_ops = { | 938 | static struct exynos_mixer_ops mixer_ops = { |
| @@ -890,10 +940,10 @@ static struct exynos_mixer_ops mixer_ops = { | |||
| 890 | .iommu_on = mixer_iommu_on, | 940 | .iommu_on = mixer_iommu_on, |
| 891 | .enable_vblank = mixer_enable_vblank, | 941 | .enable_vblank = mixer_enable_vblank, |
| 892 | .disable_vblank = mixer_disable_vblank, | 942 | .disable_vblank = mixer_disable_vblank, |
| 943 | .wait_for_vblank = mixer_wait_for_vblank, | ||
| 893 | .dpms = mixer_dpms, | 944 | .dpms = mixer_dpms, |
| 894 | 945 | ||
| 895 | /* overlay */ | 946 | /* overlay */ |
| 896 | .wait_for_vblank = mixer_wait_for_vblank, | ||
| 897 | .win_mode_set = mixer_win_mode_set, | 947 | .win_mode_set = mixer_win_mode_set, |
| 898 | .win_commit = mixer_win_commit, | 948 | .win_commit = mixer_win_commit, |
| 899 | .win_disable = mixer_win_disable, | 949 | .win_disable = mixer_win_disable, |
| @@ -957,6 +1007,12 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) | |||
| 957 | 1007 | ||
| 958 | drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe); | 1008 | drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe); |
| 959 | mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe); | 1009 | mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe); |
| 1010 | |||
| 1011 | /* set wait vsync event to zero and wake up queue. */ | ||
| 1012 | if (atomic_read(&ctx->wait_vsync_event)) { | ||
| 1013 | atomic_set(&ctx->wait_vsync_event, 0); | ||
| 1014 | DRM_WAKEUP(&ctx->wait_vsync_queue); | ||
| 1015 | } | ||
| 960 | } | 1016 | } |
| 961 | 1017 | ||
| 962 | out: | 1018 | out: |
| @@ -1139,6 +1195,8 @@ static int __devinit mixer_probe(struct platform_device *pdev) | |||
| 1139 | drm_hdmi_ctx->ctx = (void *)ctx; | 1195 | drm_hdmi_ctx->ctx = (void *)ctx; |
| 1140 | ctx->vp_enabled = drv->is_vp_enabled; | 1196 | ctx->vp_enabled = drv->is_vp_enabled; |
| 1141 | ctx->mxr_ver = drv->version; | 1197 | ctx->mxr_ver = drv->version; |
| 1198 | DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue); | ||
| 1199 | atomic_set(&ctx->wait_vsync_event, 0); | ||
| 1142 | 1200 | ||
| 1143 | platform_set_drvdata(pdev, drm_hdmi_ctx); | 1201 | platform_set_drvdata(pdev, drm_hdmi_ctx); |
| 1144 | 1202 | ||
| @@ -1189,13 +1247,66 @@ static int mixer_suspend(struct device *dev) | |||
| 1189 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); | 1247 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); |
| 1190 | struct mixer_context *ctx = drm_hdmi_ctx->ctx; | 1248 | struct mixer_context *ctx = drm_hdmi_ctx->ctx; |
| 1191 | 1249 | ||
| 1250 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 1251 | |||
| 1252 | if (pm_runtime_suspended(dev)) { | ||
| 1253 | DRM_DEBUG_KMS("%s : Already suspended\n", __func__); | ||
| 1254 | return 0; | ||
| 1255 | } | ||
| 1256 | |||
| 1192 | mixer_poweroff(ctx); | 1257 | mixer_poweroff(ctx); |
| 1193 | 1258 | ||
| 1194 | return 0; | 1259 | return 0; |
| 1195 | } | 1260 | } |
| 1261 | |||
| 1262 | static int mixer_resume(struct device *dev) | ||
| 1263 | { | ||
| 1264 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); | ||
| 1265 | struct mixer_context *ctx = drm_hdmi_ctx->ctx; | ||
| 1266 | |||
| 1267 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 1268 | |||
| 1269 | if (!pm_runtime_suspended(dev)) { | ||
| 1270 | DRM_DEBUG_KMS("%s : Already resumed\n", __func__); | ||
| 1271 | return 0; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | mixer_poweron(ctx); | ||
| 1275 | |||
| 1276 | return 0; | ||
| 1277 | } | ||
| 1196 | #endif | 1278 | #endif |
| 1197 | 1279 | ||
| 1198 | static SIMPLE_DEV_PM_OPS(mixer_pm_ops, mixer_suspend, NULL); | 1280 | #ifdef CONFIG_PM_RUNTIME |
| 1281 | static int mixer_runtime_suspend(struct device *dev) | ||
| 1282 | { | ||
| 1283 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); | ||
| 1284 | struct mixer_context *ctx = drm_hdmi_ctx->ctx; | ||
| 1285 | |||
| 1286 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 1287 | |||
| 1288 | mixer_poweroff(ctx); | ||
| 1289 | |||
| 1290 | return 0; | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | static int mixer_runtime_resume(struct device *dev) | ||
| 1294 | { | ||
| 1295 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); | ||
| 1296 | struct mixer_context *ctx = drm_hdmi_ctx->ctx; | ||
| 1297 | |||
| 1298 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 1299 | |||
| 1300 | mixer_poweron(ctx); | ||
| 1301 | |||
| 1302 | return 0; | ||
| 1303 | } | ||
| 1304 | #endif | ||
| 1305 | |||
| 1306 | static const struct dev_pm_ops mixer_pm_ops = { | ||
| 1307 | SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume) | ||
| 1308 | SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL) | ||
| 1309 | }; | ||
| 1199 | 1310 | ||
| 1200 | struct platform_driver mixer_driver = { | 1311 | struct platform_driver mixer_driver = { |
| 1201 | .driver = { | 1312 | .driver = { |
diff --git a/drivers/gpu/drm/exynos/regs-fimc.h b/drivers/gpu/drm/exynos/regs-fimc.h new file mode 100644 index 000000000000..b4f9ca1fd851 --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-fimc.h | |||
| @@ -0,0 +1,669 @@ | |||
| 1 | /* drivers/gpu/drm/exynos/regs-fimc.h | ||
| 2 | * | ||
| 3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * Register definition file for Samsung Camera Interface (FIMC) driver | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef EXYNOS_REGS_FIMC_H | ||
| 14 | #define EXYNOS_REGS_FIMC_H | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Register part | ||
| 18 | */ | ||
| 19 | /* Input source format */ | ||
| 20 | #define EXYNOS_CISRCFMT (0x00) | ||
| 21 | /* Window offset */ | ||
| 22 | #define EXYNOS_CIWDOFST (0x04) | ||
| 23 | /* Global control */ | ||
| 24 | #define EXYNOS_CIGCTRL (0x08) | ||
| 25 | /* Window offset 2 */ | ||
| 26 | #define EXYNOS_CIWDOFST2 (0x14) | ||
| 27 | /* Y 1st frame start address for output DMA */ | ||
| 28 | #define EXYNOS_CIOYSA1 (0x18) | ||
| 29 | /* Y 2nd frame start address for output DMA */ | ||
| 30 | #define EXYNOS_CIOYSA2 (0x1c) | ||
| 31 | /* Y 3rd frame start address for output DMA */ | ||
| 32 | #define EXYNOS_CIOYSA3 (0x20) | ||
| 33 | /* Y 4th frame start address for output DMA */ | ||
| 34 | #define EXYNOS_CIOYSA4 (0x24) | ||
| 35 | /* Cb 1st frame start address for output DMA */ | ||
| 36 | #define EXYNOS_CIOCBSA1 (0x28) | ||
| 37 | /* Cb 2nd frame start address for output DMA */ | ||
| 38 | #define EXYNOS_CIOCBSA2 (0x2c) | ||
| 39 | /* Cb 3rd frame start address for output DMA */ | ||
| 40 | #define EXYNOS_CIOCBSA3 (0x30) | ||
| 41 | /* Cb 4th frame start address for output DMA */ | ||
| 42 | #define EXYNOS_CIOCBSA4 (0x34) | ||
| 43 | /* Cr 1st frame start address for output DMA */ | ||
| 44 | #define EXYNOS_CIOCRSA1 (0x38) | ||
| 45 | /* Cr 2nd frame start address for output DMA */ | ||
| 46 | #define EXYNOS_CIOCRSA2 (0x3c) | ||
| 47 | /* Cr 3rd frame start address for output DMA */ | ||
| 48 | #define EXYNOS_CIOCRSA3 (0x40) | ||
| 49 | /* Cr 4th frame start address for output DMA */ | ||
| 50 | #define EXYNOS_CIOCRSA4 (0x44) | ||
| 51 | /* Target image format */ | ||
| 52 | #define EXYNOS_CITRGFMT (0x48) | ||
| 53 | /* Output DMA control */ | ||
| 54 | #define EXYNOS_CIOCTRL (0x4c) | ||
| 55 | /* Pre-scaler control 1 */ | ||
| 56 | #define EXYNOS_CISCPRERATIO (0x50) | ||
| 57 | /* Pre-scaler control 2 */ | ||
| 58 | #define EXYNOS_CISCPREDST (0x54) | ||
| 59 | /* Main scaler control */ | ||
| 60 | #define EXYNOS_CISCCTRL (0x58) | ||
| 61 | /* Target area */ | ||
| 62 | #define EXYNOS_CITAREA (0x5c) | ||
| 63 | /* Status */ | ||
| 64 | #define EXYNOS_CISTATUS (0x64) | ||
| 65 | /* Status2 */ | ||
| 66 | #define EXYNOS_CISTATUS2 (0x68) | ||
| 67 | /* Image capture enable command */ | ||
| 68 | #define EXYNOS_CIIMGCPT (0xc0) | ||
| 69 | /* Capture sequence */ | ||
| 70 | #define EXYNOS_CICPTSEQ (0xc4) | ||
| 71 | /* Image effects */ | ||
| 72 | #define EXYNOS_CIIMGEFF (0xd0) | ||
| 73 | /* Y frame start address for input DMA */ | ||
| 74 | #define EXYNOS_CIIYSA0 (0xd4) | ||
| 75 | /* Cb frame start address for input DMA */ | ||
| 76 | #define EXYNOS_CIICBSA0 (0xd8) | ||
| 77 | /* Cr frame start address for input DMA */ | ||
| 78 | #define EXYNOS_CIICRSA0 (0xdc) | ||
| 79 | /* Input DMA Y Line Skip */ | ||
| 80 | #define EXYNOS_CIILINESKIP_Y (0xec) | ||
| 81 | /* Input DMA Cb Line Skip */ | ||
| 82 | #define EXYNOS_CIILINESKIP_CB (0xf0) | ||
| 83 | /* Input DMA Cr Line Skip */ | ||
| 84 | #define EXYNOS_CIILINESKIP_CR (0xf4) | ||
| 85 | /* Real input DMA image size */ | ||
| 86 | #define EXYNOS_CIREAL_ISIZE (0xf8) | ||
| 87 | /* Input DMA control */ | ||
| 88 | #define EXYNOS_MSCTRL (0xfc) | ||
| 89 | /* Y frame start address for input DMA */ | ||
| 90 | #define EXYNOS_CIIYSA1 (0x144) | ||
| 91 | /* Cb frame start address for input DMA */ | ||
| 92 | #define EXYNOS_CIICBSA1 (0x148) | ||
| 93 | /* Cr frame start address for input DMA */ | ||
| 94 | #define EXYNOS_CIICRSA1 (0x14c) | ||
| 95 | /* Output DMA Y offset */ | ||
| 96 | #define EXYNOS_CIOYOFF (0x168) | ||
| 97 | /* Output DMA CB offset */ | ||
| 98 | #define EXYNOS_CIOCBOFF (0x16c) | ||
| 99 | /* Output DMA CR offset */ | ||
| 100 | #define EXYNOS_CIOCROFF (0x170) | ||
| 101 | /* Input DMA Y offset */ | ||
| 102 | #define EXYNOS_CIIYOFF (0x174) | ||
| 103 | /* Input DMA CB offset */ | ||
| 104 | #define EXYNOS_CIICBOFF (0x178) | ||
| 105 | /* Input DMA CR offset */ | ||
| 106 | #define EXYNOS_CIICROFF (0x17c) | ||
| 107 | /* Input DMA original image size */ | ||
| 108 | #define EXYNOS_ORGISIZE (0x180) | ||
| 109 | /* Output DMA original image size */ | ||
| 110 | #define EXYNOS_ORGOSIZE (0x184) | ||
| 111 | /* Real output DMA image size */ | ||
| 112 | #define EXYNOS_CIEXTEN (0x188) | ||
| 113 | /* DMA parameter */ | ||
| 114 | #define EXYNOS_CIDMAPARAM (0x18c) | ||
| 115 | /* MIPI CSI image format */ | ||
| 116 | #define EXYNOS_CSIIMGFMT (0x194) | ||
| 117 | /* FIMC Clock Source Select */ | ||
| 118 | #define EXYNOS_MISC_FIMC (0x198) | ||
| 119 | |||
| 120 | /* Add for FIMC v5.1 */ | ||
| 121 | /* Output Frame Buffer Sequence */ | ||
| 122 | #define EXYNOS_CIFCNTSEQ (0x1fc) | ||
| 123 | /* Y 5th frame start address for output DMA */ | ||
| 124 | #define EXYNOS_CIOYSA5 (0x200) | ||
| 125 | /* Y 6th frame start address for output DMA */ | ||
| 126 | #define EXYNOS_CIOYSA6 (0x204) | ||
| 127 | /* Y 7th frame start address for output DMA */ | ||
| 128 | #define EXYNOS_CIOYSA7 (0x208) | ||
| 129 | /* Y 8th frame start address for output DMA */ | ||
| 130 | #define EXYNOS_CIOYSA8 (0x20c) | ||
| 131 | /* Y 9th frame start address for output DMA */ | ||
| 132 | #define EXYNOS_CIOYSA9 (0x210) | ||
| 133 | /* Y 10th frame start address for output DMA */ | ||
| 134 | #define EXYNOS_CIOYSA10 (0x214) | ||
| 135 | /* Y 11th frame start address for output DMA */ | ||
| 136 | #define EXYNOS_CIOYSA11 (0x218) | ||
| 137 | /* Y 12th frame start address for output DMA */ | ||
| 138 | #define EXYNOS_CIOYSA12 (0x21c) | ||
| 139 | /* Y 13th frame start address for output DMA */ | ||
| 140 | #define EXYNOS_CIOYSA13 (0x220) | ||
| 141 | /* Y 14th frame start address for output DMA */ | ||
| 142 | #define EXYNOS_CIOYSA14 (0x224) | ||
| 143 | /* Y 15th frame start address for output DMA */ | ||
| 144 | #define EXYNOS_CIOYSA15 (0x228) | ||
| 145 | /* Y 16th frame start address for output DMA */ | ||
| 146 | #define EXYNOS_CIOYSA16 (0x22c) | ||
| 147 | /* Y 17th frame start address for output DMA */ | ||
| 148 | #define EXYNOS_CIOYSA17 (0x230) | ||
| 149 | /* Y 18th frame start address for output DMA */ | ||
| 150 | #define EXYNOS_CIOYSA18 (0x234) | ||
| 151 | /* Y 19th frame start address for output DMA */ | ||
| 152 | #define EXYNOS_CIOYSA19 (0x238) | ||
| 153 | /* Y 20th frame start address for output DMA */ | ||
| 154 | #define EXYNOS_CIOYSA20 (0x23c) | ||
| 155 | /* Y 21th frame start address for output DMA */ | ||
| 156 | #define EXYNOS_CIOYSA21 (0x240) | ||
| 157 | /* Y 22th frame start address for output DMA */ | ||
| 158 | #define EXYNOS_CIOYSA22 (0x244) | ||
| 159 | /* Y 23th frame start address for output DMA */ | ||
| 160 | #define EXYNOS_CIOYSA23 (0x248) | ||
| 161 | /* Y 24th frame start address for output DMA */ | ||
| 162 | #define EXYNOS_CIOYSA24 (0x24c) | ||
| 163 | /* Y 25th frame start address for output DMA */ | ||
| 164 | #define EXYNOS_CIOYSA25 (0x250) | ||
| 165 | /* Y 26th frame start address for output DMA */ | ||
| 166 | #define EXYNOS_CIOYSA26 (0x254) | ||
| 167 | /* Y 27th frame start address for output DMA */ | ||
| 168 | #define EXYNOS_CIOYSA27 (0x258) | ||
| 169 | /* Y 28th frame start address for output DMA */ | ||
| 170 | #define EXYNOS_CIOYSA28 (0x25c) | ||
| 171 | /* Y 29th frame start address for output DMA */ | ||
| 172 | #define EXYNOS_CIOYSA29 (0x260) | ||
| 173 | /* Y 30th frame start address for output DMA */ | ||
| 174 | #define EXYNOS_CIOYSA30 (0x264) | ||
| 175 | /* Y 31th frame start address for output DMA */ | ||
| 176 | #define EXYNOS_CIOYSA31 (0x268) | ||
| 177 | /* Y 32th frame start address for output DMA */ | ||
| 178 | #define EXYNOS_CIOYSA32 (0x26c) | ||
| 179 | |||
| 180 | /* CB 5th frame start address for output DMA */ | ||
| 181 | #define EXYNOS_CIOCBSA5 (0x270) | ||
| 182 | /* CB 6th frame start address for output DMA */ | ||
| 183 | #define EXYNOS_CIOCBSA6 (0x274) | ||
| 184 | /* CB 7th frame start address for output DMA */ | ||
| 185 | #define EXYNOS_CIOCBSA7 (0x278) | ||
| 186 | /* CB 8th frame start address for output DMA */ | ||
| 187 | #define EXYNOS_CIOCBSA8 (0x27c) | ||
| 188 | /* CB 9th frame start address for output DMA */ | ||
| 189 | #define EXYNOS_CIOCBSA9 (0x280) | ||
| 190 | /* CB 10th frame start address for output DMA */ | ||
| 191 | #define EXYNOS_CIOCBSA10 (0x284) | ||
| 192 | /* CB 11th frame start address for output DMA */ | ||
| 193 | #define EXYNOS_CIOCBSA11 (0x288) | ||
| 194 | /* CB 12th frame start address for output DMA */ | ||
| 195 | #define EXYNOS_CIOCBSA12 (0x28c) | ||
| 196 | /* CB 13th frame start address for output DMA */ | ||
| 197 | #define EXYNOS_CIOCBSA13 (0x290) | ||
| 198 | /* CB 14th frame start address for output DMA */ | ||
| 199 | #define EXYNOS_CIOCBSA14 (0x294) | ||
| 200 | /* CB 15th frame start address for output DMA */ | ||
| 201 | #define EXYNOS_CIOCBSA15 (0x298) | ||
| 202 | /* CB 16th frame start address for output DMA */ | ||
| 203 | #define EXYNOS_CIOCBSA16 (0x29c) | ||
| 204 | /* CB 17th frame start address for output DMA */ | ||
| 205 | #define EXYNOS_CIOCBSA17 (0x2a0) | ||
| 206 | /* CB 18th frame start address for output DMA */ | ||
| 207 | #define EXYNOS_CIOCBSA18 (0x2a4) | ||
| 208 | /* CB 19th frame start address for output DMA */ | ||
| 209 | #define EXYNOS_CIOCBSA19 (0x2a8) | ||
| 210 | /* CB 20th frame start address for output DMA */ | ||
| 211 | #define EXYNOS_CIOCBSA20 (0x2ac) | ||
| 212 | /* CB 21th frame start address for output DMA */ | ||
| 213 | #define EXYNOS_CIOCBSA21 (0x2b0) | ||
| 214 | /* CB 22th frame start address for output DMA */ | ||
| 215 | #define EXYNOS_CIOCBSA22 (0x2b4) | ||
| 216 | /* CB 23th frame start address for output DMA */ | ||
| 217 | #define EXYNOS_CIOCBSA23 (0x2b8) | ||
| 218 | /* CB 24th frame start address for output DMA */ | ||
| 219 | #define EXYNOS_CIOCBSA24 (0x2bc) | ||
| 220 | /* CB 25th frame start address for output DMA */ | ||
| 221 | #define EXYNOS_CIOCBSA25 (0x2c0) | ||
| 222 | /* CB 26th frame start address for output DMA */ | ||
| 223 | #define EXYNOS_CIOCBSA26 (0x2c4) | ||
| 224 | /* CB 27th frame start address for output DMA */ | ||
| 225 | #define EXYNOS_CIOCBSA27 (0x2c8) | ||
| 226 | /* CB 28th frame start address for output DMA */ | ||
| 227 | #define EXYNOS_CIOCBSA28 (0x2cc) | ||
| 228 | /* CB 29th frame start address for output DMA */ | ||
| 229 | #define EXYNOS_CIOCBSA29 (0x2d0) | ||
| 230 | /* CB 30th frame start address for output DMA */ | ||
| 231 | #define EXYNOS_CIOCBSA30 (0x2d4) | ||
| 232 | /* CB 31th frame start address for output DMA */ | ||
| 233 | #define EXYNOS_CIOCBSA31 (0x2d8) | ||
| 234 | /* CB 32th frame start address for output DMA */ | ||
| 235 | #define EXYNOS_CIOCBSA32 (0x2dc) | ||
| 236 | |||
| 237 | /* CR 5th frame start address for output DMA */ | ||
| 238 | #define EXYNOS_CIOCRSA5 (0x2e0) | ||
| 239 | /* CR 6th frame start address for output DMA */ | ||
| 240 | #define EXYNOS_CIOCRSA6 (0x2e4) | ||
| 241 | /* CR 7th frame start address for output DMA */ | ||
| 242 | #define EXYNOS_CIOCRSA7 (0x2e8) | ||
| 243 | /* CR 8th frame start address for output DMA */ | ||
| 244 | #define EXYNOS_CIOCRSA8 (0x2ec) | ||
| 245 | /* CR 9th frame start address for output DMA */ | ||
| 246 | #define EXYNOS_CIOCRSA9 (0x2f0) | ||
| 247 | /* CR 10th frame start address for output DMA */ | ||
| 248 | #define EXYNOS_CIOCRSA10 (0x2f4) | ||
| 249 | /* CR 11th frame start address for output DMA */ | ||
| 250 | #define EXYNOS_CIOCRSA11 (0x2f8) | ||
| 251 | /* CR 12th frame start address for output DMA */ | ||
| 252 | #define EXYNOS_CIOCRSA12 (0x2fc) | ||
| 253 | /* CR 13th frame start address for output DMA */ | ||
| 254 | #define EXYNOS_CIOCRSA13 (0x300) | ||
| 255 | /* CR 14th frame start address for output DMA */ | ||
| 256 | #define EXYNOS_CIOCRSA14 (0x304) | ||
| 257 | /* CR 15th frame start address for output DMA */ | ||
| 258 | #define EXYNOS_CIOCRSA15 (0x308) | ||
| 259 | /* CR 16th frame start address for output DMA */ | ||
| 260 | #define EXYNOS_CIOCRSA16 (0x30c) | ||
| 261 | /* CR 17th frame start address for output DMA */ | ||
| 262 | #define EXYNOS_CIOCRSA17 (0x310) | ||
| 263 | /* CR 18th frame start address for output DMA */ | ||
| 264 | #define EXYNOS_CIOCRSA18 (0x314) | ||
| 265 | /* CR 19th frame start address for output DMA */ | ||
| 266 | #define EXYNOS_CIOCRSA19 (0x318) | ||
| 267 | /* CR 20th frame start address for output DMA */ | ||
| 268 | #define EXYNOS_CIOCRSA20 (0x31c) | ||
| 269 | /* CR 21th frame start address for output DMA */ | ||
| 270 | #define EXYNOS_CIOCRSA21 (0x320) | ||
| 271 | /* CR 22th frame start address for output DMA */ | ||
| 272 | #define EXYNOS_CIOCRSA22 (0x324) | ||
| 273 | /* CR 23th frame start address for output DMA */ | ||
| 274 | #define EXYNOS_CIOCRSA23 (0x328) | ||
| 275 | /* CR 24th frame start address for output DMA */ | ||
| 276 | #define EXYNOS_CIOCRSA24 (0x32c) | ||
| 277 | /* CR 25th frame start address for output DMA */ | ||
| 278 | #define EXYNOS_CIOCRSA25 (0x330) | ||
| 279 | /* CR 26th frame start address for output DMA */ | ||
| 280 | #define EXYNOS_CIOCRSA26 (0x334) | ||
| 281 | /* CR 27th frame start address for output DMA */ | ||
| 282 | #define EXYNOS_CIOCRSA27 (0x338) | ||
| 283 | /* CR 28th frame start address for output DMA */ | ||
| 284 | #define EXYNOS_CIOCRSA28 (0x33c) | ||
| 285 | /* CR 29th frame start address for output DMA */ | ||
| 286 | #define EXYNOS_CIOCRSA29 (0x340) | ||
| 287 | /* CR 30th frame start address for output DMA */ | ||
| 288 | #define EXYNOS_CIOCRSA30 (0x344) | ||
| 289 | /* CR 31th frame start address for output DMA */ | ||
| 290 | #define EXYNOS_CIOCRSA31 (0x348) | ||
| 291 | /* CR 32th frame start address for output DMA */ | ||
| 292 | #define EXYNOS_CIOCRSA32 (0x34c) | ||
| 293 | |||
| 294 | /* | ||
| 295 | * Macro part | ||
| 296 | */ | ||
| 297 | /* frame start address 1 ~ 4, 5 ~ 32 */ | ||
| 298 | /* Number of Default PingPong Memory */ | ||
| 299 | #define DEF_PP 4 | ||
| 300 | #define EXYNOS_CIOYSA(__x) \ | ||
| 301 | (((__x) < DEF_PP) ? \ | ||
| 302 | (EXYNOS_CIOYSA1 + (__x) * 4) : \ | ||
| 303 | (EXYNOS_CIOYSA5 + ((__x) - DEF_PP) * 4)) | ||
| 304 | #define EXYNOS_CIOCBSA(__x) \ | ||
| 305 | (((__x) < DEF_PP) ? \ | ||
| 306 | (EXYNOS_CIOCBSA1 + (__x) * 4) : \ | ||
| 307 | (EXYNOS_CIOCBSA5 + ((__x) - DEF_PP) * 4)) | ||
| 308 | #define EXYNOS_CIOCRSA(__x) \ | ||
| 309 | (((__x) < DEF_PP) ? \ | ||
| 310 | (EXYNOS_CIOCRSA1 + (__x) * 4) : \ | ||
| 311 | (EXYNOS_CIOCRSA5 + ((__x) - DEF_PP) * 4)) | ||
| 312 | /* Number of Default PingPong Memory */ | ||
| 313 | #define DEF_IPP 1 | ||
| 314 | #define EXYNOS_CIIYSA(__x) \ | ||
| 315 | (((__x) < DEF_IPP) ? \ | ||
| 316 | (EXYNOS_CIIYSA0) : (EXYNOS_CIIYSA1)) | ||
| 317 | #define EXYNOS_CIICBSA(__x) \ | ||
| 318 | (((__x) < DEF_IPP) ? \ | ||
| 319 | (EXYNOS_CIICBSA0) : (EXYNOS_CIICBSA1)) | ||
| 320 | #define EXYNOS_CIICRSA(__x) \ | ||
| 321 | (((__x) < DEF_IPP) ? \ | ||
| 322 | (EXYNOS_CIICRSA0) : (EXYNOS_CIICRSA1)) | ||
| 323 | |||
| 324 | #define EXYNOS_CISRCFMT_SOURCEHSIZE(x) ((x) << 16) | ||
| 325 | #define EXYNOS_CISRCFMT_SOURCEVSIZE(x) ((x) << 0) | ||
| 326 | |||
| 327 | #define EXYNOS_CIWDOFST_WINHOROFST(x) ((x) << 16) | ||
| 328 | #define EXYNOS_CIWDOFST_WINVEROFST(x) ((x) << 0) | ||
| 329 | |||
| 330 | #define EXYNOS_CIWDOFST2_WINHOROFST2(x) ((x) << 16) | ||
| 331 | #define EXYNOS_CIWDOFST2_WINVEROFST2(x) ((x) << 0) | ||
| 332 | |||
| 333 | #define EXYNOS_CITRGFMT_TARGETHSIZE(x) (((x) & 0x1fff) << 16) | ||
| 334 | #define EXYNOS_CITRGFMT_TARGETVSIZE(x) (((x) & 0x1fff) << 0) | ||
| 335 | |||
| 336 | #define EXYNOS_CISCPRERATIO_SHFACTOR(x) ((x) << 28) | ||
| 337 | #define EXYNOS_CISCPRERATIO_PREHORRATIO(x) ((x) << 16) | ||
| 338 | #define EXYNOS_CISCPRERATIO_PREVERRATIO(x) ((x) << 0) | ||
| 339 | |||
| 340 | #define EXYNOS_CISCPREDST_PREDSTWIDTH(x) ((x) << 16) | ||
| 341 | #define EXYNOS_CISCPREDST_PREDSTHEIGHT(x) ((x) << 0) | ||
| 342 | |||
| 343 | #define EXYNOS_CISCCTRL_MAINHORRATIO(x) ((x) << 16) | ||
| 344 | #define EXYNOS_CISCCTRL_MAINVERRATIO(x) ((x) << 0) | ||
| 345 | |||
| 346 | #define EXYNOS_CITAREA_TARGET_AREA(x) ((x) << 0) | ||
| 347 | |||
| 348 | #define EXYNOS_CISTATUS_GET_FRAME_COUNT(x) (((x) >> 26) & 0x3) | ||
| 349 | #define EXYNOS_CISTATUS_GET_FRAME_END(x) (((x) >> 17) & 0x1) | ||
| 350 | #define EXYNOS_CISTATUS_GET_LAST_CAPTURE_END(x) (((x) >> 16) & 0x1) | ||
| 351 | #define EXYNOS_CISTATUS_GET_LCD_STATUS(x) (((x) >> 9) & 0x1) | ||
| 352 | #define EXYNOS_CISTATUS_GET_ENVID_STATUS(x) (((x) >> 8) & 0x1) | ||
| 353 | |||
| 354 | #define EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(x) (((x) >> 7) & 0x3f) | ||
| 355 | #define EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(x) ((x) & 0x3f) | ||
| 356 | |||
| 357 | #define EXYNOS_CIIMGEFF_FIN(x) ((x & 0x7) << 26) | ||
| 358 | #define EXYNOS_CIIMGEFF_PAT_CB(x) ((x) << 13) | ||
| 359 | #define EXYNOS_CIIMGEFF_PAT_CR(x) ((x) << 0) | ||
| 360 | |||
| 361 | #define EXYNOS_CIILINESKIP(x) (((x) & 0xf) << 24) | ||
| 362 | |||
| 363 | #define EXYNOS_CIREAL_ISIZE_HEIGHT(x) ((x) << 16) | ||
| 364 | #define EXYNOS_CIREAL_ISIZE_WIDTH(x) ((x) << 0) | ||
| 365 | |||
| 366 | #define EXYNOS_MSCTRL_SUCCESSIVE_COUNT(x) ((x) << 24) | ||
| 367 | #define EXYNOS_MSCTRL_GET_INDMA_STATUS(x) ((x) & 0x1) | ||
| 368 | |||
| 369 | #define EXYNOS_CIOYOFF_VERTICAL(x) ((x) << 16) | ||
| 370 | #define EXYNOS_CIOYOFF_HORIZONTAL(x) ((x) << 0) | ||
| 371 | |||
| 372 | #define EXYNOS_CIOCBOFF_VERTICAL(x) ((x) << 16) | ||
| 373 | #define EXYNOS_CIOCBOFF_HORIZONTAL(x) ((x) << 0) | ||
| 374 | |||
| 375 | #define EXYNOS_CIOCROFF_VERTICAL(x) ((x) << 16) | ||
| 376 | #define EXYNOS_CIOCROFF_HORIZONTAL(x) ((x) << 0) | ||
| 377 | |||
| 378 | #define EXYNOS_CIIYOFF_VERTICAL(x) ((x) << 16) | ||
| 379 | #define EXYNOS_CIIYOFF_HORIZONTAL(x) ((x) << 0) | ||
| 380 | |||
| 381 | #define EXYNOS_CIICBOFF_VERTICAL(x) ((x) << 16) | ||
| 382 | #define EXYNOS_CIICBOFF_HORIZONTAL(x) ((x) << 0) | ||
| 383 | |||
| 384 | #define EXYNOS_CIICROFF_VERTICAL(x) ((x) << 16) | ||
| 385 | #define EXYNOS_CIICROFF_HORIZONTAL(x) ((x) << 0) | ||
| 386 | |||
| 387 | #define EXYNOS_ORGISIZE_VERTICAL(x) ((x) << 16) | ||
| 388 | #define EXYNOS_ORGISIZE_HORIZONTAL(x) ((x) << 0) | ||
| 389 | |||
| 390 | #define EXYNOS_ORGOSIZE_VERTICAL(x) ((x) << 16) | ||
| 391 | #define EXYNOS_ORGOSIZE_HORIZONTAL(x) ((x) << 0) | ||
| 392 | |||
| 393 | #define EXYNOS_CIEXTEN_TARGETH_EXT(x) ((((x) & 0x2000) >> 13) << 26) | ||
| 394 | #define EXYNOS_CIEXTEN_TARGETV_EXT(x) ((((x) & 0x2000) >> 13) << 24) | ||
| 395 | #define EXYNOS_CIEXTEN_MAINHORRATIO_EXT(x) (((x) & 0x3F) << 10) | ||
| 396 | #define EXYNOS_CIEXTEN_MAINVERRATIO_EXT(x) ((x) & 0x3F) | ||
| 397 | |||
| 398 | /* | ||
| 399 | * Bit definition part | ||
| 400 | */ | ||
| 401 | /* Source format register */ | ||
| 402 | #define EXYNOS_CISRCFMT_ITU601_8BIT (1 << 31) | ||
| 403 | #define EXYNOS_CISRCFMT_ITU656_8BIT (0 << 31) | ||
| 404 | #define EXYNOS_CISRCFMT_ITU601_16BIT (1 << 29) | ||
| 405 | #define EXYNOS_CISRCFMT_ORDER422_YCBYCR (0 << 14) | ||
| 406 | #define EXYNOS_CISRCFMT_ORDER422_YCRYCB (1 << 14) | ||
| 407 | #define EXYNOS_CISRCFMT_ORDER422_CBYCRY (2 << 14) | ||
| 408 | #define EXYNOS_CISRCFMT_ORDER422_CRYCBY (3 << 14) | ||
| 409 | /* ITU601 16bit only */ | ||
| 410 | #define EXYNOS_CISRCFMT_ORDER422_Y4CBCRCBCR (0 << 14) | ||
| 411 | /* ITU601 16bit only */ | ||
| 412 | #define EXYNOS_CISRCFMT_ORDER422_Y4CRCBCRCB (1 << 14) | ||
| 413 | |||
| 414 | /* Window offset register */ | ||
| 415 | #define EXYNOS_CIWDOFST_WINOFSEN (1 << 31) | ||
| 416 | #define EXYNOS_CIWDOFST_CLROVFIY (1 << 30) | ||
| 417 | #define EXYNOS_CIWDOFST_CLROVRLB (1 << 29) | ||
| 418 | #define EXYNOS_CIWDOFST_WINHOROFST_MASK (0x7ff << 16) | ||
| 419 | #define EXYNOS_CIWDOFST_CLROVFICB (1 << 15) | ||
| 420 | #define EXYNOS_CIWDOFST_CLROVFICR (1 << 14) | ||
| 421 | #define EXYNOS_CIWDOFST_WINVEROFST_MASK (0xfff << 0) | ||
| 422 | |||
| 423 | /* Global control register */ | ||
| 424 | #define EXYNOS_CIGCTRL_SWRST (1 << 31) | ||
| 425 | #define EXYNOS_CIGCTRL_CAMRST_A (1 << 30) | ||
| 426 | #define EXYNOS_CIGCTRL_SELCAM_ITU_B (0 << 29) | ||
| 427 | #define EXYNOS_CIGCTRL_SELCAM_ITU_A (1 << 29) | ||
| 428 | #define EXYNOS_CIGCTRL_SELCAM_ITU_MASK (1 << 29) | ||
| 429 | #define EXYNOS_CIGCTRL_TESTPATTERN_NORMAL (0 << 27) | ||
| 430 | #define EXYNOS_CIGCTRL_TESTPATTERN_COLOR_BAR (1 << 27) | ||
| 431 | #define EXYNOS_CIGCTRL_TESTPATTERN_HOR_INC (2 << 27) | ||
| 432 | #define EXYNOS_CIGCTRL_TESTPATTERN_VER_INC (3 << 27) | ||
| 433 | #define EXYNOS_CIGCTRL_TESTPATTERN_MASK (3 << 27) | ||
| 434 | #define EXYNOS_CIGCTRL_TESTPATTERN_SHIFT (27) | ||
| 435 | #define EXYNOS_CIGCTRL_INVPOLPCLK (1 << 26) | ||
| 436 | #define EXYNOS_CIGCTRL_INVPOLVSYNC (1 << 25) | ||
| 437 | #define EXYNOS_CIGCTRL_INVPOLHREF (1 << 24) | ||
| 438 | #define EXYNOS_CIGCTRL_IRQ_OVFEN (1 << 22) | ||
| 439 | #define EXYNOS_CIGCTRL_HREF_MASK (1 << 21) | ||
| 440 | #define EXYNOS_CIGCTRL_IRQ_EDGE (0 << 20) | ||
| 441 | #define EXYNOS_CIGCTRL_IRQ_LEVEL (1 << 20) | ||
| 442 | #define EXYNOS_CIGCTRL_IRQ_CLR (1 << 19) | ||
| 443 | #define EXYNOS_CIGCTRL_IRQ_END_DISABLE (1 << 18) | ||
| 444 | #define EXYNOS_CIGCTRL_IRQ_DISABLE (0 << 16) | ||
| 445 | #define EXYNOS_CIGCTRL_IRQ_ENABLE (1 << 16) | ||
| 446 | #define EXYNOS_CIGCTRL_SHADOW_DISABLE (1 << 12) | ||
| 447 | #define EXYNOS_CIGCTRL_CAM_JPEG (1 << 8) | ||
| 448 | #define EXYNOS_CIGCTRL_SELCAM_MIPI_B (0 << 7) | ||
| 449 | #define EXYNOS_CIGCTRL_SELCAM_MIPI_A (1 << 7) | ||
| 450 | #define EXYNOS_CIGCTRL_SELCAM_MIPI_MASK (1 << 7) | ||
| 451 | #define EXYNOS_CIGCTRL_SELWB_CAMIF_CAMERA (0 << 6) | ||
| 452 | #define EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK (1 << 6) | ||
| 453 | #define EXYNOS_CIGCTRL_SELWRITEBACK_MASK (1 << 10) | ||
| 454 | #define EXYNOS_CIGCTRL_SELWRITEBACK_A (1 << 10) | ||
| 455 | #define EXYNOS_CIGCTRL_SELWRITEBACK_B (0 << 10) | ||
| 456 | #define EXYNOS_CIGCTRL_SELWB_CAMIF_MASK (1 << 6) | ||
| 457 | #define EXYNOS_CIGCTRL_CSC_ITU601 (0 << 5) | ||
| 458 | #define EXYNOS_CIGCTRL_CSC_ITU709 (1 << 5) | ||
| 459 | #define EXYNOS_CIGCTRL_CSC_MASK (1 << 5) | ||
| 460 | #define EXYNOS_CIGCTRL_INVPOLHSYNC (1 << 4) | ||
| 461 | #define EXYNOS_CIGCTRL_SELCAM_FIMC_ITU (0 << 3) | ||
| 462 | #define EXYNOS_CIGCTRL_SELCAM_FIMC_MIPI (1 << 3) | ||
| 463 | #define EXYNOS_CIGCTRL_SELCAM_FIMC_MASK (1 << 3) | ||
| 464 | #define EXYNOS_CIGCTRL_PROGRESSIVE (0 << 0) | ||
| 465 | #define EXYNOS_CIGCTRL_INTERLACE (1 << 0) | ||
| 466 | |||
| 467 | /* Window offset2 register */ | ||
| 468 | #define EXYNOS_CIWDOFST_WINHOROFST2_MASK (0xfff << 16) | ||
| 469 | #define EXYNOS_CIWDOFST_WINVEROFST2_MASK (0xfff << 16) | ||
| 470 | |||
| 471 | /* Target format register */ | ||
| 472 | #define EXYNOS_CITRGFMT_INROT90_CLOCKWISE (1 << 31) | ||
| 473 | #define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420 (0 << 29) | ||
| 474 | #define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422 (1 << 29) | ||
| 475 | #define EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE (2 << 29) | ||
| 476 | #define EXYNOS_CITRGFMT_OUTFORMAT_RGB (3 << 29) | ||
| 477 | #define EXYNOS_CITRGFMT_OUTFORMAT_MASK (3 << 29) | ||
| 478 | #define EXYNOS_CITRGFMT_FLIP_SHIFT (14) | ||
| 479 | #define EXYNOS_CITRGFMT_FLIP_NORMAL (0 << 14) | ||
| 480 | #define EXYNOS_CITRGFMT_FLIP_X_MIRROR (1 << 14) | ||
| 481 | #define EXYNOS_CITRGFMT_FLIP_Y_MIRROR (2 << 14) | ||
| 482 | #define EXYNOS_CITRGFMT_FLIP_180 (3 << 14) | ||
| 483 | #define EXYNOS_CITRGFMT_FLIP_MASK (3 << 14) | ||
| 484 | #define EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE (1 << 13) | ||
| 485 | #define EXYNOS_CITRGFMT_TARGETV_MASK (0x1fff << 0) | ||
| 486 | #define EXYNOS_CITRGFMT_TARGETH_MASK (0x1fff << 16) | ||
| 487 | |||
| 488 | /* Output DMA control register */ | ||
| 489 | #define EXYNOS_CIOCTRL_WEAVE_OUT (1 << 31) | ||
| 490 | #define EXYNOS_CIOCTRL_WEAVE_MASK (1 << 31) | ||
| 491 | #define EXYNOS_CIOCTRL_LASTENDEN (1 << 30) | ||
| 492 | #define EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR (0 << 24) | ||
| 493 | #define EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB (1 << 24) | ||
| 494 | #define EXYNOS_CIOCTRL_ORDER2P_MSB_CRCB (2 << 24) | ||
| 495 | #define EXYNOS_CIOCTRL_ORDER2P_MSB_CBCR (3 << 24) | ||
| 496 | #define EXYNOS_CIOCTRL_ORDER2P_SHIFT (24) | ||
| 497 | #define EXYNOS_CIOCTRL_ORDER2P_MASK (3 << 24) | ||
| 498 | #define EXYNOS_CIOCTRL_YCBCR_3PLANE (0 << 3) | ||
| 499 | #define EXYNOS_CIOCTRL_YCBCR_2PLANE (1 << 3) | ||
| 500 | #define EXYNOS_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) | ||
| 501 | #define EXYNOS_CIOCTRL_LASTIRQ_ENABLE (1 << 2) | ||
| 502 | #define EXYNOS_CIOCTRL_ALPHA_OUT (0xff << 4) | ||
| 503 | #define EXYNOS_CIOCTRL_ORDER422_YCBYCR (0 << 0) | ||
| 504 | #define EXYNOS_CIOCTRL_ORDER422_YCRYCB (1 << 0) | ||
| 505 | #define EXYNOS_CIOCTRL_ORDER422_CBYCRY (2 << 0) | ||
| 506 | #define EXYNOS_CIOCTRL_ORDER422_CRYCBY (3 << 0) | ||
| 507 | #define EXYNOS_CIOCTRL_ORDER422_MASK (3 << 0) | ||
| 508 | |||
| 509 | /* Main scaler control register */ | ||
| 510 | #define EXYNOS_CISCCTRL_SCALERBYPASS (1 << 31) | ||
| 511 | #define EXYNOS_CISCCTRL_SCALEUP_H (1 << 30) | ||
| 512 | #define EXYNOS_CISCCTRL_SCALEUP_V (1 << 29) | ||
| 513 | #define EXYNOS_CISCCTRL_CSCR2Y_NARROW (0 << 28) | ||
| 514 | #define EXYNOS_CISCCTRL_CSCR2Y_WIDE (1 << 28) | ||
| 515 | #define EXYNOS_CISCCTRL_CSCY2R_NARROW (0 << 27) | ||
| 516 | #define EXYNOS_CISCCTRL_CSCY2R_WIDE (1 << 27) | ||
| 517 | #define EXYNOS_CISCCTRL_LCDPATHEN_FIFO (1 << 26) | ||
| 518 | #define EXYNOS_CISCCTRL_PROGRESSIVE (0 << 25) | ||
| 519 | #define EXYNOS_CISCCTRL_INTERLACE (1 << 25) | ||
| 520 | #define EXYNOS_CISCCTRL_SCAN_MASK (1 << 25) | ||
| 521 | #define EXYNOS_CISCCTRL_SCALERSTART (1 << 15) | ||
| 522 | #define EXYNOS_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) | ||
| 523 | #define EXYNOS_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) | ||
| 524 | #define EXYNOS_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) | ||
| 525 | #define EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK (3 << 13) | ||
| 526 | #define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) | ||
| 527 | #define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) | ||
| 528 | #define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) | ||
| 529 | #define EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK (3 << 11) | ||
| 530 | #define EXYNOS_CISCCTRL_EXTRGB_NORMAL (0 << 10) | ||
| 531 | #define EXYNOS_CISCCTRL_EXTRGB_EXTENSION (1 << 10) | ||
| 532 | #define EXYNOS_CISCCTRL_ONE2ONE (1 << 9) | ||
| 533 | #define EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK (0x1ff << 0) | ||
| 534 | #define EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK (0x1ff << 16) | ||
| 535 | |||
| 536 | /* Status register */ | ||
| 537 | #define EXYNOS_CISTATUS_OVFIY (1 << 31) | ||
| 538 | #define EXYNOS_CISTATUS_OVFICB (1 << 30) | ||
| 539 | #define EXYNOS_CISTATUS_OVFICR (1 << 29) | ||
| 540 | #define EXYNOS_CISTATUS_VSYNC (1 << 28) | ||
| 541 | #define EXYNOS_CISTATUS_SCALERSTART (1 << 26) | ||
| 542 | #define EXYNOS_CISTATUS_WINOFSTEN (1 << 25) | ||
| 543 | #define EXYNOS_CISTATUS_IMGCPTEN (1 << 22) | ||
| 544 | #define EXYNOS_CISTATUS_IMGCPTENSC (1 << 21) | ||
| 545 | #define EXYNOS_CISTATUS_VSYNC_A (1 << 20) | ||
| 546 | #define EXYNOS_CISTATUS_VSYNC_B (1 << 19) | ||
| 547 | #define EXYNOS_CISTATUS_OVRLB (1 << 18) | ||
| 548 | #define EXYNOS_CISTATUS_FRAMEEND (1 << 17) | ||
| 549 | #define EXYNOS_CISTATUS_LASTCAPTUREEND (1 << 16) | ||
| 550 | #define EXYNOS_CISTATUS_VVALID_A (1 << 15) | ||
| 551 | #define EXYNOS_CISTATUS_VVALID_B (1 << 14) | ||
| 552 | |||
| 553 | /* Image capture enable register */ | ||
| 554 | #define EXYNOS_CIIMGCPT_IMGCPTEN (1 << 31) | ||
| 555 | #define EXYNOS_CIIMGCPT_IMGCPTEN_SC (1 << 30) | ||
| 556 | #define EXYNOS_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) | ||
| 557 | #define EXYNOS_CIIMGCPT_CPT_FRMOD_EN (0 << 18) | ||
| 558 | #define EXYNOS_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) | ||
| 559 | |||
| 560 | /* Image effects register */ | ||
| 561 | #define EXYNOS_CIIMGEFF_IE_DISABLE (0 << 30) | ||
| 562 | #define EXYNOS_CIIMGEFF_IE_ENABLE (1 << 30) | ||
| 563 | #define EXYNOS_CIIMGEFF_IE_SC_BEFORE (0 << 29) | ||
| 564 | #define EXYNOS_CIIMGEFF_IE_SC_AFTER (1 << 29) | ||
| 565 | #define EXYNOS_CIIMGEFF_FIN_BYPASS (0 << 26) | ||
| 566 | #define EXYNOS_CIIMGEFF_FIN_ARBITRARY (1 << 26) | ||
| 567 | #define EXYNOS_CIIMGEFF_FIN_NEGATIVE (2 << 26) | ||
| 568 | #define EXYNOS_CIIMGEFF_FIN_ARTFREEZE (3 << 26) | ||
| 569 | #define EXYNOS_CIIMGEFF_FIN_EMBOSSING (4 << 26) | ||
| 570 | #define EXYNOS_CIIMGEFF_FIN_SILHOUETTE (5 << 26) | ||
| 571 | #define EXYNOS_CIIMGEFF_FIN_MASK (7 << 26) | ||
| 572 | #define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) | ||
| 573 | |||
| 574 | /* Real input DMA size register */ | ||
| 575 | #define EXYNOS_CIREAL_ISIZE_AUTOLOAD_ENABLE (1 << 31) | ||
| 576 | #define EXYNOS_CIREAL_ISIZE_ADDR_CH_DISABLE (1 << 30) | ||
| 577 | #define EXYNOS_CIREAL_ISIZE_HEIGHT_MASK (0x3FFF << 16) | ||
| 578 | #define EXYNOS_CIREAL_ISIZE_WIDTH_MASK (0x3FFF << 0) | ||
| 579 | |||
| 580 | /* Input DMA control register */ | ||
| 581 | #define EXYNOS_MSCTRL_FIELD_MASK (1 << 31) | ||
| 582 | #define EXYNOS_MSCTRL_FIELD_WEAVE (1 << 31) | ||
| 583 | #define EXYNOS_MSCTRL_FIELD_NORMAL (0 << 31) | ||
| 584 | #define EXYNOS_MSCTRL_BURST_CNT (24) | ||
| 585 | #define EXYNOS_MSCTRL_BURST_CNT_MASK (0xf << 24) | ||
| 586 | #define EXYNOS_MSCTRL_ORDER2P_LSB_CBCR (0 << 16) | ||
| 587 | #define EXYNOS_MSCTRL_ORDER2P_LSB_CRCB (1 << 16) | ||
| 588 | #define EXYNOS_MSCTRL_ORDER2P_MSB_CRCB (2 << 16) | ||
| 589 | #define EXYNOS_MSCTRL_ORDER2P_MSB_CBCR (3 << 16) | ||
| 590 | #define EXYNOS_MSCTRL_ORDER2P_SHIFT (16) | ||
| 591 | #define EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK (0x3 << 16) | ||
| 592 | #define EXYNOS_MSCTRL_C_INT_IN_3PLANE (0 << 15) | ||
| 593 | #define EXYNOS_MSCTRL_C_INT_IN_2PLANE (1 << 15) | ||
| 594 | #define EXYNOS_MSCTRL_FLIP_SHIFT (13) | ||
| 595 | #define EXYNOS_MSCTRL_FLIP_NORMAL (0 << 13) | ||
| 596 | #define EXYNOS_MSCTRL_FLIP_X_MIRROR (1 << 13) | ||
| 597 | #define EXYNOS_MSCTRL_FLIP_Y_MIRROR (2 << 13) | ||
| 598 | #define EXYNOS_MSCTRL_FLIP_180 (3 << 13) | ||
| 599 | #define EXYNOS_MSCTRL_FLIP_MASK (3 << 13) | ||
| 600 | #define EXYNOS_MSCTRL_ORDER422_CRYCBY (0 << 4) | ||
| 601 | #define EXYNOS_MSCTRL_ORDER422_YCRYCB (1 << 4) | ||
| 602 | #define EXYNOS_MSCTRL_ORDER422_CBYCRY (2 << 4) | ||
| 603 | #define EXYNOS_MSCTRL_ORDER422_YCBYCR (3 << 4) | ||
| 604 | #define EXYNOS_MSCTRL_INPUT_EXTCAM (0 << 3) | ||
| 605 | #define EXYNOS_MSCTRL_INPUT_MEMORY (1 << 3) | ||
| 606 | #define EXYNOS_MSCTRL_INPUT_MASK (1 << 3) | ||
| 607 | #define EXYNOS_MSCTRL_INFORMAT_YCBCR420 (0 << 1) | ||
| 608 | #define EXYNOS_MSCTRL_INFORMAT_YCBCR422 (1 << 1) | ||
| 609 | #define EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE (2 << 1) | ||
| 610 | #define EXYNOS_MSCTRL_INFORMAT_RGB (3 << 1) | ||
| 611 | #define EXYNOS_MSCTRL_ENVID (1 << 0) | ||
| 612 | |||
| 613 | /* DMA parameter register */ | ||
| 614 | #define EXYNOS_CIDMAPARAM_R_MODE_LINEAR (0 << 29) | ||
| 615 | #define EXYNOS_CIDMAPARAM_R_MODE_CONFTILE (1 << 29) | ||
| 616 | #define EXYNOS_CIDMAPARAM_R_MODE_16X16 (2 << 29) | ||
| 617 | #define EXYNOS_CIDMAPARAM_R_MODE_64X32 (3 << 29) | ||
| 618 | #define EXYNOS_CIDMAPARAM_R_MODE_MASK (3 << 29) | ||
| 619 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_64 (0 << 24) | ||
| 620 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_128 (1 << 24) | ||
| 621 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_256 (2 << 24) | ||
| 622 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_512 (3 << 24) | ||
| 623 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_1024 (4 << 24) | ||
| 624 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_2048 (5 << 24) | ||
| 625 | #define EXYNOS_CIDMAPARAM_R_TILE_HSIZE_4096 (6 << 24) | ||
| 626 | #define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_1 (0 << 20) | ||
| 627 | #define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_2 (1 << 20) | ||
| 628 | #define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_4 (2 << 20) | ||
| 629 | #define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_8 (3 << 20) | ||
| 630 | #define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_16 (4 << 20) | ||
| 631 | #define EXYNOS_CIDMAPARAM_R_TILE_VSIZE_32 (5 << 20) | ||
| 632 | #define EXYNOS_CIDMAPARAM_W_MODE_LINEAR (0 << 13) | ||
| 633 | #define EXYNOS_CIDMAPARAM_W_MODE_CONFTILE (1 << 13) | ||
| 634 | #define EXYNOS_CIDMAPARAM_W_MODE_16X16 (2 << 13) | ||
| 635 | #define EXYNOS_CIDMAPARAM_W_MODE_64X32 (3 << 13) | ||
| 636 | #define EXYNOS_CIDMAPARAM_W_MODE_MASK (3 << 13) | ||
| 637 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_64 (0 << 8) | ||
| 638 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_128 (1 << 8) | ||
| 639 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_256 (2 << 8) | ||
| 640 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_512 (3 << 8) | ||
| 641 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_1024 (4 << 8) | ||
| 642 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_2048 (5 << 8) | ||
| 643 | #define EXYNOS_CIDMAPARAM_W_TILE_HSIZE_4096 (6 << 8) | ||
| 644 | #define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_1 (0 << 4) | ||
| 645 | #define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_2 (1 << 4) | ||
| 646 | #define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_4 (2 << 4) | ||
| 647 | #define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_8 (3 << 4) | ||
| 648 | #define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_16 (4 << 4) | ||
| 649 | #define EXYNOS_CIDMAPARAM_W_TILE_VSIZE_32 (5 << 4) | ||
| 650 | |||
| 651 | /* Gathering Extension register */ | ||
| 652 | #define EXYNOS_CIEXTEN_TARGETH_EXT_MASK (1 << 26) | ||
| 653 | #define EXYNOS_CIEXTEN_TARGETV_EXT_MASK (1 << 24) | ||
| 654 | #define EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK (0x3F << 10) | ||
| 655 | #define EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK (0x3F) | ||
| 656 | #define EXYNOS_CIEXTEN_YUV444_OUT (1 << 22) | ||
| 657 | |||
| 658 | /* FIMC Clock Source Select register */ | ||
| 659 | #define EXYNOS_CLKSRC_HCLK (0 << 1) | ||
| 660 | #define EXYNOS_CLKSRC_HCLK_MASK (1 << 1) | ||
| 661 | #define EXYNOS_CLKSRC_SCLK (1 << 1) | ||
| 662 | |||
| 663 | /* SYSREG for FIMC writeback */ | ||
| 664 | #define SYSREG_CAMERA_BLK (S3C_VA_SYS + 0x0218) | ||
| 665 | #define SYSREG_ISP_BLK (S3C_VA_SYS + 0x020c) | ||
| 666 | #define SYSREG_FIMD0WB_DEST_MASK (0x3 << 23) | ||
| 667 | #define SYSREG_FIMD0WB_DEST_SHIFT 23 | ||
| 668 | |||
| 669 | #endif /* EXYNOS_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 000000000000..9ad592707aaf --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-gsc.h | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | /* linux/drivers/gpu/drm/exynos/regs-gsc.h | ||
| 2 | * | ||
| 3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com | ||
| 5 | * | ||
| 6 | * Register definition file for Samsung G-Scaler driver | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef EXYNOS_REGS_GSC_H_ | ||
| 14 | #define EXYNOS_REGS_GSC_H_ | ||
| 15 | |||
| 16 | /* G-Scaler enable */ | ||
| 17 | #define GSC_ENABLE 0x00 | ||
| 18 | #define GSC_ENABLE_PP_UPDATE_TIME_MASK (1 << 9) | ||
| 19 | #define GSC_ENABLE_PP_UPDATE_TIME_CURR (0 << 9) | ||
| 20 | #define GSC_ENABLE_PP_UPDATE_TIME_EOPAS (1 << 9) | ||
| 21 | #define GSC_ENABLE_CLK_GATE_MODE_MASK (1 << 8) | ||
| 22 | #define GSC_ENABLE_CLK_GATE_MODE_FREE (1 << 8) | ||
| 23 | #define GSC_ENABLE_IPC_MODE_MASK (1 << 7) | ||
| 24 | #define GSC_ENABLE_NORM_MODE (0 << 7) | ||
| 25 | #define GSC_ENABLE_IPC_MODE (1 << 7) | ||
| 26 | #define GSC_ENABLE_PP_UPDATE_MODE_MASK (1 << 6) | ||
| 27 | #define GSC_ENABLE_PP_UPDATE_FIRE_MODE (1 << 6) | ||
| 28 | #define GSC_ENABLE_IN_PP_UPDATE (1 << 5) | ||
| 29 | #define GSC_ENABLE_ON_CLEAR_MASK (1 << 4) | ||
| 30 | #define GSC_ENABLE_ON_CLEAR_ONESHOT (1 << 4) | ||
| 31 | #define GSC_ENABLE_QOS_ENABLE (1 << 3) | ||
| 32 | #define GSC_ENABLE_OP_STATUS (1 << 2) | ||
| 33 | #define GSC_ENABLE_SFR_UPDATE (1 << 1) | ||
| 34 | #define GSC_ENABLE_ON (1 << 0) | ||
| 35 | |||
| 36 | /* G-Scaler S/W reset */ | ||
| 37 | #define GSC_SW_RESET 0x04 | ||
| 38 | #define GSC_SW_RESET_SRESET (1 << 0) | ||
| 39 | |||
| 40 | /* G-Scaler IRQ */ | ||
| 41 | #define GSC_IRQ 0x08 | ||
| 42 | #define GSC_IRQ_STATUS_OR_IRQ (1 << 17) | ||
| 43 | #define GSC_IRQ_STATUS_OR_FRM_DONE (1 << 16) | ||
| 44 | #define GSC_IRQ_OR_MASK (1 << 2) | ||
| 45 | #define GSC_IRQ_FRMDONE_MASK (1 << 1) | ||
| 46 | #define GSC_IRQ_ENABLE (1 << 0) | ||
| 47 | |||
| 48 | /* G-Scaler input control */ | ||
| 49 | #define GSC_IN_CON 0x10 | ||
| 50 | #define GSC_IN_CHROM_STRIDE_SEL_MASK (1 << 20) | ||
| 51 | #define GSC_IN_CHROM_STRIDE_SEPAR (1 << 20) | ||
| 52 | #define GSC_IN_RB_SWAP_MASK (1 << 19) | ||
| 53 | #define GSC_IN_RB_SWAP (1 << 19) | ||
| 54 | #define GSC_IN_ROT_MASK (7 << 16) | ||
| 55 | #define GSC_IN_ROT_270 (7 << 16) | ||
| 56 | #define GSC_IN_ROT_90_YFLIP (6 << 16) | ||
| 57 | #define GSC_IN_ROT_90_XFLIP (5 << 16) | ||
| 58 | #define GSC_IN_ROT_90 (4 << 16) | ||
| 59 | #define GSC_IN_ROT_180 (3 << 16) | ||
| 60 | #define GSC_IN_ROT_YFLIP (2 << 16) | ||
| 61 | #define GSC_IN_ROT_XFLIP (1 << 16) | ||
| 62 | #define GSC_IN_RGB_TYPE_MASK (3 << 14) | ||
| 63 | #define GSC_IN_RGB_HD_WIDE (3 << 14) | ||
| 64 | #define GSC_IN_RGB_HD_NARROW (2 << 14) | ||
| 65 | #define GSC_IN_RGB_SD_WIDE (1 << 14) | ||
| 66 | #define GSC_IN_RGB_SD_NARROW (0 << 14) | ||
| 67 | #define GSC_IN_YUV422_1P_ORDER_MASK (1 << 13) | ||
| 68 | #define GSC_IN_YUV422_1P_ORDER_LSB_Y (0 << 13) | ||
| 69 | #define GSC_IN_YUV422_1P_OEDER_LSB_C (1 << 13) | ||
| 70 | #define GSC_IN_CHROMA_ORDER_MASK (1 << 12) | ||
| 71 | #define GSC_IN_CHROMA_ORDER_CBCR (0 << 12) | ||
| 72 | #define GSC_IN_CHROMA_ORDER_CRCB (1 << 12) | ||
| 73 | #define GSC_IN_FORMAT_MASK (7 << 8) | ||
| 74 | #define GSC_IN_XRGB8888 (0 << 8) | ||
| 75 | #define GSC_IN_RGB565 (1 << 8) | ||
| 76 | #define GSC_IN_YUV420_2P (2 << 8) | ||
| 77 | #define GSC_IN_YUV420_3P (3 << 8) | ||
| 78 | #define GSC_IN_YUV422_1P (4 << 8) | ||
| 79 | #define GSC_IN_YUV422_2P (5 << 8) | ||
| 80 | #define GSC_IN_YUV422_3P (6 << 8) | ||
| 81 | #define GSC_IN_TILE_TYPE_MASK (1 << 4) | ||
| 82 | #define GSC_IN_TILE_C_16x8 (0 << 4) | ||
| 83 | #define GSC_IN_TILE_C_16x16 (1 << 4) | ||
| 84 | #define GSC_IN_TILE_MODE (1 << 3) | ||
| 85 | #define GSC_IN_LOCAL_SEL_MASK (3 << 1) | ||
| 86 | #define GSC_IN_LOCAL_CAM3 (3 << 1) | ||
| 87 | #define GSC_IN_LOCAL_FIMD_WB (2 << 1) | ||
| 88 | #define GSC_IN_LOCAL_CAM1 (1 << 1) | ||
| 89 | #define GSC_IN_LOCAL_CAM0 (0 << 1) | ||
| 90 | #define GSC_IN_PATH_MASK (1 << 0) | ||
| 91 | #define GSC_IN_PATH_LOCAL (1 << 0) | ||
| 92 | #define GSC_IN_PATH_MEMORY (0 << 0) | ||
| 93 | |||
| 94 | /* G-Scaler source image size */ | ||
| 95 | #define GSC_SRCIMG_SIZE 0x14 | ||
| 96 | #define GSC_SRCIMG_HEIGHT_MASK (0x1fff << 16) | ||
| 97 | #define GSC_SRCIMG_HEIGHT(x) ((x) << 16) | ||
| 98 | #define GSC_SRCIMG_WIDTH_MASK (0x3fff << 0) | ||
| 99 | #define GSC_SRCIMG_WIDTH(x) ((x) << 0) | ||
| 100 | |||
| 101 | /* G-Scaler source image offset */ | ||
| 102 | #define GSC_SRCIMG_OFFSET 0x18 | ||
| 103 | #define GSC_SRCIMG_OFFSET_Y_MASK (0x1fff << 16) | ||
| 104 | #define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16) | ||
| 105 | #define GSC_SRCIMG_OFFSET_X_MASK (0x1fff << 0) | ||
| 106 | #define GSC_SRCIMG_OFFSET_X(x) ((x) << 0) | ||
| 107 | |||
| 108 | /* G-Scaler cropped source image size */ | ||
| 109 | #define GSC_CROPPED_SIZE 0x1C | ||
| 110 | #define GSC_CROPPED_HEIGHT_MASK (0x1fff << 16) | ||
| 111 | #define GSC_CROPPED_HEIGHT(x) ((x) << 16) | ||
| 112 | #define GSC_CROPPED_WIDTH_MASK (0x1fff << 0) | ||
| 113 | #define GSC_CROPPED_WIDTH(x) ((x) << 0) | ||
| 114 | |||
| 115 | /* G-Scaler output control */ | ||
| 116 | #define GSC_OUT_CON 0x20 | ||
| 117 | #define GSC_OUT_GLOBAL_ALPHA_MASK (0xff << 24) | ||
| 118 | #define GSC_OUT_GLOBAL_ALPHA(x) ((x) << 24) | ||
| 119 | #define GSC_OUT_CHROM_STRIDE_SEL_MASK (1 << 13) | ||
| 120 | #define GSC_OUT_CHROM_STRIDE_SEPAR (1 << 13) | ||
| 121 | #define GSC_OUT_RB_SWAP_MASK (1 << 12) | ||
| 122 | #define GSC_OUT_RB_SWAP (1 << 12) | ||
| 123 | #define GSC_OUT_RGB_TYPE_MASK (3 << 10) | ||
| 124 | #define GSC_OUT_RGB_HD_NARROW (3 << 10) | ||
| 125 | #define GSC_OUT_RGB_HD_WIDE (2 << 10) | ||
| 126 | #define GSC_OUT_RGB_SD_NARROW (1 << 10) | ||
| 127 | #define GSC_OUT_RGB_SD_WIDE (0 << 10) | ||
| 128 | #define GSC_OUT_YUV422_1P_ORDER_MASK (1 << 9) | ||
| 129 | #define GSC_OUT_YUV422_1P_ORDER_LSB_Y (0 << 9) | ||
| 130 | #define GSC_OUT_YUV422_1P_OEDER_LSB_C (1 << 9) | ||
| 131 | #define GSC_OUT_CHROMA_ORDER_MASK (1 << 8) | ||
| 132 | #define GSC_OUT_CHROMA_ORDER_CBCR (0 << 8) | ||
| 133 | #define GSC_OUT_CHROMA_ORDER_CRCB (1 << 8) | ||
| 134 | #define GSC_OUT_FORMAT_MASK (7 << 4) | ||
| 135 | #define GSC_OUT_XRGB8888 (0 << 4) | ||
| 136 | #define GSC_OUT_RGB565 (1 << 4) | ||
| 137 | #define GSC_OUT_YUV420_2P (2 << 4) | ||
| 138 | #define GSC_OUT_YUV420_3P (3 << 4) | ||
| 139 | #define GSC_OUT_YUV422_1P (4 << 4) | ||
| 140 | #define GSC_OUT_YUV422_2P (5 << 4) | ||
| 141 | #define GSC_OUT_YUV444 (7 << 4) | ||
| 142 | #define GSC_OUT_TILE_TYPE_MASK (1 << 2) | ||
| 143 | #define GSC_OUT_TILE_C_16x8 (0 << 2) | ||
| 144 | #define GSC_OUT_TILE_C_16x16 (1 << 2) | ||
| 145 | #define GSC_OUT_TILE_MODE (1 << 1) | ||
| 146 | #define GSC_OUT_PATH_MASK (1 << 0) | ||
| 147 | #define GSC_OUT_PATH_LOCAL (1 << 0) | ||
| 148 | #define GSC_OUT_PATH_MEMORY (0 << 0) | ||
| 149 | |||
| 150 | /* G-Scaler scaled destination image size */ | ||
| 151 | #define GSC_SCALED_SIZE 0x24 | ||
| 152 | #define GSC_SCALED_HEIGHT_MASK (0x1fff << 16) | ||
| 153 | #define GSC_SCALED_HEIGHT(x) ((x) << 16) | ||
| 154 | #define GSC_SCALED_WIDTH_MASK (0x1fff << 0) | ||
| 155 | #define GSC_SCALED_WIDTH(x) ((x) << 0) | ||
| 156 | |||
| 157 | /* G-Scaler pre scale ratio */ | ||
| 158 | #define GSC_PRE_SCALE_RATIO 0x28 | ||
| 159 | #define GSC_PRESC_SHFACTOR_MASK (7 << 28) | ||
| 160 | #define GSC_PRESC_SHFACTOR(x) ((x) << 28) | ||
| 161 | #define GSC_PRESC_V_RATIO_MASK (7 << 16) | ||
| 162 | #define GSC_PRESC_V_RATIO(x) ((x) << 16) | ||
| 163 | #define GSC_PRESC_H_RATIO_MASK (7 << 0) | ||
| 164 | #define GSC_PRESC_H_RATIO(x) ((x) << 0) | ||
| 165 | |||
| 166 | /* G-Scaler main scale horizontal ratio */ | ||
| 167 | #define GSC_MAIN_H_RATIO 0x2C | ||
| 168 | #define GSC_MAIN_H_RATIO_MASK (0xfffff << 0) | ||
| 169 | #define GSC_MAIN_H_RATIO_VALUE(x) ((x) << 0) | ||
| 170 | |||
| 171 | /* G-Scaler main scale vertical ratio */ | ||
| 172 | #define GSC_MAIN_V_RATIO 0x30 | ||
| 173 | #define GSC_MAIN_V_RATIO_MASK (0xfffff << 0) | ||
| 174 | #define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0) | ||
| 175 | |||
| 176 | /* G-Scaler input chrominance stride */ | ||
| 177 | #define GSC_IN_CHROM_STRIDE 0x3C | ||
| 178 | #define GSC_IN_CHROM_STRIDE_MASK (0x3fff << 0) | ||
| 179 | #define GSC_IN_CHROM_STRIDE_VALUE(x) ((x) << 0) | ||
| 180 | |||
| 181 | /* G-Scaler destination image size */ | ||
| 182 | #define GSC_DSTIMG_SIZE 0x40 | ||
| 183 | #define GSC_DSTIMG_HEIGHT_MASK (0x1fff << 16) | ||
| 184 | #define GSC_DSTIMG_HEIGHT(x) ((x) << 16) | ||
| 185 | #define GSC_DSTIMG_WIDTH_MASK (0x1fff << 0) | ||
| 186 | #define GSC_DSTIMG_WIDTH(x) ((x) << 0) | ||
| 187 | |||
| 188 | /* G-Scaler destination image offset */ | ||
| 189 | #define GSC_DSTIMG_OFFSET 0x44 | ||
| 190 | #define GSC_DSTIMG_OFFSET_Y_MASK (0x1fff << 16) | ||
| 191 | #define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16) | ||
| 192 | #define GSC_DSTIMG_OFFSET_X_MASK (0x1fff << 0) | ||
| 193 | #define GSC_DSTIMG_OFFSET_X(x) ((x) << 0) | ||
| 194 | |||
| 195 | /* G-Scaler output chrominance stride */ | ||
| 196 | #define GSC_OUT_CHROM_STRIDE 0x48 | ||
| 197 | #define GSC_OUT_CHROM_STRIDE_MASK (0x3fff << 0) | ||
| 198 | #define GSC_OUT_CHROM_STRIDE_VALUE(x) ((x) << 0) | ||
| 199 | |||
| 200 | /* G-Scaler input y address mask */ | ||
| 201 | #define GSC_IN_BASE_ADDR_Y_MASK 0x4C | ||
| 202 | /* G-Scaler input y base address */ | ||
| 203 | #define GSC_IN_BASE_ADDR_Y(n) (0x50 + (n) * 0x4) | ||
| 204 | /* G-Scaler input y base current address */ | ||
| 205 | #define GSC_IN_BASE_ADDR_Y_CUR(n) (0x60 + (n) * 0x4) | ||
| 206 | |||
| 207 | /* G-Scaler input cb address mask */ | ||
| 208 | #define GSC_IN_BASE_ADDR_CB_MASK 0x7C | ||
| 209 | /* G-Scaler input cb base address */ | ||
| 210 | #define GSC_IN_BASE_ADDR_CB(n) (0x80 + (n) * 0x4) | ||
| 211 | /* G-Scaler input cb base current address */ | ||
| 212 | #define GSC_IN_BASE_ADDR_CB_CUR(n) (0x90 + (n) * 0x4) | ||
| 213 | |||
| 214 | /* G-Scaler input cr address mask */ | ||
| 215 | #define GSC_IN_BASE_ADDR_CR_MASK 0xAC | ||
| 216 | /* G-Scaler input cr base address */ | ||
| 217 | #define GSC_IN_BASE_ADDR_CR(n) (0xB0 + (n) * 0x4) | ||
| 218 | /* G-Scaler input cr base current address */ | ||
| 219 | #define GSC_IN_BASE_ADDR_CR_CUR(n) (0xC0 + (n) * 0x4) | ||
| 220 | |||
| 221 | /* G-Scaler input address mask */ | ||
| 222 | #define GSC_IN_CURR_ADDR_INDEX (0xf << 24) | ||
| 223 | #define GSC_IN_CURR_GET_INDEX(x) ((x) >> 24) | ||
| 224 | #define GSC_IN_BASE_ADDR_PINGPONG(x) ((x) << 16) | ||
| 225 | #define GSC_IN_BASE_ADDR_MASK (0xff << 0) | ||
| 226 | |||
| 227 | /* G-Scaler output y address mask */ | ||
| 228 | #define GSC_OUT_BASE_ADDR_Y_MASK 0x10C | ||
| 229 | /* G-Scaler output y base address */ | ||
| 230 | #define GSC_OUT_BASE_ADDR_Y(n) (0x110 + (n) * 0x4) | ||
| 231 | |||
| 232 | /* G-Scaler output cb address mask */ | ||
| 233 | #define GSC_OUT_BASE_ADDR_CB_MASK 0x15C | ||
| 234 | /* G-Scaler output cb base address */ | ||
| 235 | #define GSC_OUT_BASE_ADDR_CB(n) (0x160 + (n) * 0x4) | ||
| 236 | |||
| 237 | /* G-Scaler output cr address mask */ | ||
| 238 | #define GSC_OUT_BASE_ADDR_CR_MASK 0x1AC | ||
| 239 | /* G-Scaler output cr base address */ | ||
| 240 | #define GSC_OUT_BASE_ADDR_CR(n) (0x1B0 + (n) * 0x4) | ||
| 241 | |||
| 242 | /* G-Scaler output address mask */ | ||
| 243 | #define GSC_OUT_CURR_ADDR_INDEX (0xf << 24) | ||
| 244 | #define GSC_OUT_CURR_GET_INDEX(x) ((x) >> 24) | ||
| 245 | #define GSC_OUT_BASE_ADDR_PINGPONG(x) ((x) << 16) | ||
| 246 | #define GSC_OUT_BASE_ADDR_MASK (0xffff << 0) | ||
| 247 | |||
| 248 | /* G-Scaler horizontal scaling filter */ | ||
| 249 | #define GSC_HCOEF(n, s, x) (0x300 + (n) * 0x4 + (s) * 0x30 + (x) * 0x300) | ||
| 250 | |||
| 251 | /* G-Scaler vertical scaling filter */ | ||
| 252 | #define GSC_VCOEF(n, s, x) (0x200 + (n) * 0x4 + (s) * 0x30 + (x) * 0x300) | ||
| 253 | |||
| 254 | /* G-Scaler BUS control */ | ||
| 255 | #define GSC_BUSCON 0xA78 | ||
| 256 | #define GSC_BUSCON_INT_TIME_MASK (1 << 8) | ||
| 257 | #define GSC_BUSCON_INT_DATA_TRANS (0 << 8) | ||
| 258 | #define GSC_BUSCON_INT_AXI_RESPONSE (1 << 8) | ||
| 259 | #define GSC_BUSCON_AWCACHE(x) ((x) << 4) | ||
| 260 | #define GSC_BUSCON_ARCACHE(x) ((x) << 0) | ||
| 261 | |||
| 262 | /* G-Scaler V position */ | ||
| 263 | #define GSC_VPOSITION 0xA7C | ||
| 264 | #define GSC_VPOS_F(x) ((x) << 0) | ||
| 265 | |||
| 266 | |||
| 267 | /* G-Scaler clock initial count */ | ||
| 268 | #define GSC_CLK_INIT_COUNT 0xC00 | ||
| 269 | #define GSC_CLK_GATE_MODE_INIT_CNT(x) ((x) << 0) | ||
| 270 | |||
| 271 | /* G-Scaler clock snoop count */ | ||
| 272 | #define GSC_CLK_SNOOP_COUNT 0xC04 | ||
| 273 | #define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0) | ||
| 274 | |||
| 275 | /* SYSCON. GSCBLK_CFG */ | ||
| 276 | #define SYSREG_GSCBLK_CFG1 (S3C_VA_SYS + 0x0224) | ||
| 277 | #define GSC_BLK_DISP1WB_DEST(x) (x << 10) | ||
| 278 | #define GSC_BLK_SW_RESET_WB_DEST(x) (1 << (18 + x)) | ||
| 279 | #define GSC_BLK_PXLASYNC_LO_MASK_WB(x) (0 << (14 + x)) | ||
| 280 | #define GSC_BLK_GSCL_WB_IN_SRC_SEL(x) (1 << (2 * x)) | ||
| 281 | #define SYSREG_GSCBLK_CFG2 (S3C_VA_SYS + 0x2000) | ||
| 282 | #define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x)) | ||
| 283 | |||
| 284 | #endif /* EXYNOS_REGS_GSC_H_ */ | ||
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 970cdb518eb1..ef1b3eb3ba6e 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h | |||
| @@ -176,6 +176,11 @@ | |||
| 176 | #define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C) | 176 | #define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C) |
| 177 | #define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080) | 177 | #define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080) |
| 178 | 178 | ||
| 179 | /* PHY Control bit definition */ | ||
| 180 | |||
| 181 | /* HDMI_PHY_CON_0 */ | ||
| 182 | #define HDMI_PHY_POWER_OFF_EN (1 << 0) | ||
| 183 | |||
| 179 | /* Video related registers */ | 184 | /* Video related registers */ |
| 180 | #define HDMI_YMAX HDMI_CORE_BASE(0x0060) | 185 | #define HDMI_YMAX HDMI_CORE_BASE(0x0060) |
| 181 | #define HDMI_YMIN HDMI_CORE_BASE(0x0064) | 186 | #define HDMI_YMIN HDMI_CORE_BASE(0x0064) |
diff --git a/drivers/gpu/drm/exynos/regs-rotator.h b/drivers/gpu/drm/exynos/regs-rotator.h new file mode 100644 index 000000000000..a09ac6e180da --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-rotator.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | /* drivers/gpu/drm/exynos/regs-rotator.h | ||
| 2 | * | ||
| 3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * Register definition file for Samsung Rotator Interface (Rotator) driver | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef EXYNOS_REGS_ROTATOR_H | ||
| 14 | #define EXYNOS_REGS_ROTATOR_H | ||
| 15 | |||
| 16 | /* Configuration */ | ||
| 17 | #define ROT_CONFIG 0x00 | ||
| 18 | #define ROT_CONFIG_IRQ (3 << 8) | ||
| 19 | |||
| 20 | /* Image Control */ | ||
| 21 | #define ROT_CONTROL 0x10 | ||
| 22 | #define ROT_CONTROL_PATTERN_WRITE (1 << 16) | ||
| 23 | #define ROT_CONTROL_FMT_YCBCR420_2P (1 << 8) | ||
| 24 | #define ROT_CONTROL_FMT_RGB888 (6 << 8) | ||
| 25 | #define ROT_CONTROL_FMT_MASK (7 << 8) | ||
| 26 | #define ROT_CONTROL_FLIP_VERTICAL (2 << 6) | ||
| 27 | #define ROT_CONTROL_FLIP_HORIZONTAL (3 << 6) | ||
| 28 | #define ROT_CONTROL_FLIP_MASK (3 << 6) | ||
| 29 | #define ROT_CONTROL_ROT_90 (1 << 4) | ||
| 30 | #define ROT_CONTROL_ROT_180 (2 << 4) | ||
| 31 | #define ROT_CONTROL_ROT_270 (3 << 4) | ||
| 32 | #define ROT_CONTROL_ROT_MASK (3 << 4) | ||
| 33 | #define ROT_CONTROL_START (1 << 0) | ||
| 34 | |||
| 35 | /* Status */ | ||
| 36 | #define ROT_STATUS 0x20 | ||
| 37 | #define ROT_STATUS_IRQ_PENDING(x) (1 << (x)) | ||
| 38 | #define ROT_STATUS_IRQ(x) (((x) >> 8) & 0x3) | ||
| 39 | #define ROT_STATUS_IRQ_VAL_COMPLETE 1 | ||
| 40 | #define ROT_STATUS_IRQ_VAL_ILLEGAL 2 | ||
| 41 | |||
| 42 | /* Buffer Address */ | ||
| 43 | #define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2)) | ||
| 44 | #define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) | ||
| 45 | |||
| 46 | /* Buffer Size */ | ||
| 47 | #define ROT_SRC_BUF_SIZE 0x3c | ||
| 48 | #define ROT_DST_BUF_SIZE 0x5c | ||
| 49 | #define ROT_SET_BUF_SIZE_H(x) ((x) << 16) | ||
| 50 | #define ROT_SET_BUF_SIZE_W(x) ((x) << 0) | ||
| 51 | #define ROT_GET_BUF_SIZE_H(x) ((x) >> 16) | ||
| 52 | #define ROT_GET_BUF_SIZE_W(x) ((x) & 0xffff) | ||
| 53 | |||
| 54 | /* Crop Position */ | ||
| 55 | #define ROT_SRC_CROP_POS 0x40 | ||
| 56 | #define ROT_DST_CROP_POS 0x60 | ||
| 57 | #define ROT_CROP_POS_Y(x) ((x) << 16) | ||
| 58 | #define ROT_CROP_POS_X(x) ((x) << 0) | ||
| 59 | |||
| 60 | /* Source Crop Size */ | ||
| 61 | #define ROT_SRC_CROP_SIZE 0x44 | ||
| 62 | #define ROT_SRC_CROP_SIZE_H(x) ((x) << 16) | ||
| 63 | #define ROT_SRC_CROP_SIZE_W(x) ((x) << 0) | ||
| 64 | |||
| 65 | /* Round to nearest aligned value */ | ||
| 66 | #define ROT_ALIGN(x, align, mask) (((x) + (1 << ((align) - 1))) & (mask)) | ||
| 67 | /* Minimum limit value */ | ||
| 68 | #define ROT_MIN(min, mask) (((min) + ~(mask)) & (mask)) | ||
| 69 | /* Maximum limit value */ | ||
| 70 | #define ROT_MAX(max, mask) ((max) & (mask)) | ||
| 71 | |||
| 72 | #endif /* EXYNOS_REGS_ROTATOR_H */ | ||
| 73 | |||
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 3c13a3a4b158..808dad29607a 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h | |||
| @@ -85,4 +85,30 @@ struct exynos_drm_hdmi_pdata { | |||
| 85 | int (*get_hpd)(void); | 85 | int (*get_hpd)(void); |
| 86 | }; | 86 | }; |
| 87 | 87 | ||
| 88 | /** | ||
| 89 | * Platform Specific Structure for DRM based IPP. | ||
| 90 | * | ||
| 91 | * @inv_pclk: if set 1. invert pixel clock | ||
| 92 | * @inv_vsync: if set 1. invert vsync signal for wb | ||
| 93 | * @inv_href: if set 1. invert href signal | ||
| 94 | * @inv_hsync: if set 1. invert hsync signal for wb | ||
| 95 | */ | ||
| 96 | struct exynos_drm_ipp_pol { | ||
| 97 | unsigned int inv_pclk; | ||
| 98 | unsigned int inv_vsync; | ||
| 99 | unsigned int inv_href; | ||
| 100 | unsigned int inv_hsync; | ||
| 101 | }; | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Platform Specific Structure for DRM based FIMC. | ||
| 105 | * | ||
| 106 | * @pol: current hardware block polarity settings. | ||
| 107 | * @clk_rate: current hardware clock rate. | ||
| 108 | */ | ||
| 109 | struct exynos_drm_fimc_pdata { | ||
| 110 | struct exynos_drm_ipp_pol pol; | ||
| 111 | int clk_rate; | ||
| 112 | }; | ||
| 113 | |||
| 88 | #endif /* _EXYNOS_DRM_H_ */ | 114 | #endif /* _EXYNOS_DRM_H_ */ |
diff --git a/include/uapi/drm/exynos_drm.h b/include/uapi/drm/exynos_drm.h index 49f010f2b27f..e7f52c334005 100644 --- a/include/uapi/drm/exynos_drm.h +++ b/include/uapi/drm/exynos_drm.h | |||
| @@ -163,6 +163,170 @@ struct drm_exynos_g2d_exec { | |||
| 163 | __u64 async; | 163 | __u64 async; |
| 164 | }; | 164 | }; |
| 165 | 165 | ||
| 166 | enum drm_exynos_ops_id { | ||
| 167 | EXYNOS_DRM_OPS_SRC, | ||
| 168 | EXYNOS_DRM_OPS_DST, | ||
| 169 | EXYNOS_DRM_OPS_MAX, | ||
| 170 | }; | ||
| 171 | |||
| 172 | struct drm_exynos_sz { | ||
| 173 | __u32 hsize; | ||
| 174 | __u32 vsize; | ||
| 175 | }; | ||
| 176 | |||
| 177 | struct drm_exynos_pos { | ||
| 178 | __u32 x; | ||
| 179 | __u32 y; | ||
| 180 | __u32 w; | ||
| 181 | __u32 h; | ||
| 182 | }; | ||
| 183 | |||
| 184 | enum drm_exynos_flip { | ||
| 185 | EXYNOS_DRM_FLIP_NONE = (0 << 0), | ||
| 186 | EXYNOS_DRM_FLIP_VERTICAL = (1 << 0), | ||
| 187 | EXYNOS_DRM_FLIP_HORIZONTAL = (1 << 1), | ||
| 188 | }; | ||
| 189 | |||
| 190 | enum drm_exynos_degree { | ||
| 191 | EXYNOS_DRM_DEGREE_0, | ||
| 192 | EXYNOS_DRM_DEGREE_90, | ||
| 193 | EXYNOS_DRM_DEGREE_180, | ||
| 194 | EXYNOS_DRM_DEGREE_270, | ||
| 195 | }; | ||
| 196 | |||
| 197 | enum drm_exynos_planer { | ||
| 198 | EXYNOS_DRM_PLANAR_Y, | ||
| 199 | EXYNOS_DRM_PLANAR_CB, | ||
| 200 | EXYNOS_DRM_PLANAR_CR, | ||
| 201 | EXYNOS_DRM_PLANAR_MAX, | ||
| 202 | }; | ||
| 203 | |||
| 204 | /** | ||
| 205 | * A structure for ipp supported property list. | ||
| 206 | * | ||
| 207 | * @version: version of this structure. | ||
| 208 | * @ipp_id: id of ipp driver. | ||
| 209 | * @count: count of ipp driver. | ||
| 210 | * @writeback: flag of writeback supporting. | ||
| 211 | * @flip: flag of flip supporting. | ||
| 212 | * @degree: flag of degree information. | ||
| 213 | * @csc: flag of csc supporting. | ||
| 214 | * @crop: flag of crop supporting. | ||
| 215 | * @scale: flag of scale supporting. | ||
| 216 | * @refresh_min: min hz of refresh. | ||
| 217 | * @refresh_max: max hz of refresh. | ||
| 218 | * @crop_min: crop min resolution. | ||
| 219 | * @crop_max: crop max resolution. | ||
| 220 | * @scale_min: scale min resolution. | ||
| 221 | * @scale_max: scale max resolution. | ||
| 222 | */ | ||
| 223 | struct drm_exynos_ipp_prop_list { | ||
| 224 | __u32 version; | ||
| 225 | __u32 ipp_id; | ||
| 226 | __u32 count; | ||
| 227 | __u32 writeback; | ||
| 228 | __u32 flip; | ||
| 229 | __u32 degree; | ||
| 230 | __u32 csc; | ||
| 231 | __u32 crop; | ||
| 232 | __u32 scale; | ||
| 233 | __u32 refresh_min; | ||
| 234 | __u32 refresh_max; | ||
| 235 | __u32 reserved; | ||
| 236 | struct drm_exynos_sz crop_min; | ||
| 237 | struct drm_exynos_sz crop_max; | ||
| 238 | struct drm_exynos_sz scale_min; | ||
| 239 | struct drm_exynos_sz scale_max; | ||
| 240 | }; | ||
| 241 | |||
| 242 | /** | ||
| 243 | * A structure for ipp config. | ||
| 244 | * | ||
| 245 | * @ops_id: property of operation directions. | ||
| 246 | * @flip: property of mirror, flip. | ||
| 247 | * @degree: property of rotation degree. | ||
| 248 | * @fmt: property of image format. | ||
| 249 | * @sz: property of image size. | ||
| 250 | * @pos: property of image position(src-cropped,dst-scaler). | ||
| 251 | */ | ||
| 252 | struct drm_exynos_ipp_config { | ||
| 253 | enum drm_exynos_ops_id ops_id; | ||
| 254 | enum drm_exynos_flip flip; | ||
| 255 | enum drm_exynos_degree degree; | ||
| 256 | __u32 fmt; | ||
| 257 | struct drm_exynos_sz sz; | ||
| 258 | struct drm_exynos_pos pos; | ||
| 259 | }; | ||
| 260 | |||
| 261 | enum drm_exynos_ipp_cmd { | ||
| 262 | IPP_CMD_NONE, | ||
| 263 | IPP_CMD_M2M, | ||
| 264 | IPP_CMD_WB, | ||
| 265 | IPP_CMD_OUTPUT, | ||
| 266 | IPP_CMD_MAX, | ||
| 267 | }; | ||
| 268 | |||
| 269 | /** | ||
| 270 | * A structure for ipp property. | ||
| 271 | * | ||
| 272 | * @config: source, destination config. | ||
| 273 | * @cmd: definition of command. | ||
| 274 | * @ipp_id: id of ipp driver. | ||
| 275 | * @prop_id: id of property. | ||
| 276 | * @refresh_rate: refresh rate. | ||
| 277 | */ | ||
| 278 | struct drm_exynos_ipp_property { | ||
| 279 | struct drm_exynos_ipp_config config[EXYNOS_DRM_OPS_MAX]; | ||
| 280 | enum drm_exynos_ipp_cmd cmd; | ||
| 281 | __u32 ipp_id; | ||
| 282 | __u32 prop_id; | ||
| 283 | __u32 refresh_rate; | ||
| 284 | }; | ||
| 285 | |||
| 286 | enum drm_exynos_ipp_buf_type { | ||
| 287 | IPP_BUF_ENQUEUE, | ||
| 288 | IPP_BUF_DEQUEUE, | ||
| 289 | }; | ||
| 290 | |||
| 291 | /** | ||
| 292 | * A structure for ipp buffer operations. | ||
| 293 | * | ||
| 294 | * @ops_id: operation directions. | ||
| 295 | * @buf_type: definition of buffer. | ||
| 296 | * @prop_id: id of property. | ||
| 297 | * @buf_id: id of buffer. | ||
| 298 | * @handle: Y, Cb, Cr each planar handle. | ||
| 299 | * @user_data: user data. | ||
| 300 | */ | ||
| 301 | struct drm_exynos_ipp_queue_buf { | ||
| 302 | enum drm_exynos_ops_id ops_id; | ||
| 303 | enum drm_exynos_ipp_buf_type buf_type; | ||
| 304 | __u32 prop_id; | ||
| 305 | __u32 buf_id; | ||
| 306 | __u32 handle[EXYNOS_DRM_PLANAR_MAX]; | ||
| 307 | __u32 reserved; | ||
| 308 | __u64 user_data; | ||
| 309 | }; | ||
| 310 | |||
| 311 | enum drm_exynos_ipp_ctrl { | ||
| 312 | IPP_CTRL_PLAY, | ||
| 313 | IPP_CTRL_STOP, | ||
| 314 | IPP_CTRL_PAUSE, | ||
| 315 | IPP_CTRL_RESUME, | ||
| 316 | IPP_CTRL_MAX, | ||
| 317 | }; | ||
| 318 | |||
| 319 | /** | ||
| 320 | * A structure for ipp start/stop operations. | ||
| 321 | * | ||
| 322 | * @prop_id: id of property. | ||
| 323 | * @ctrl: definition of control. | ||
| 324 | */ | ||
| 325 | struct drm_exynos_ipp_cmd_ctrl { | ||
| 326 | __u32 prop_id; | ||
| 327 | enum drm_exynos_ipp_ctrl ctrl; | ||
| 328 | }; | ||
| 329 | |||
| 166 | #define DRM_EXYNOS_GEM_CREATE 0x00 | 330 | #define DRM_EXYNOS_GEM_CREATE 0x00 |
| 167 | #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 | 331 | #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 |
| 168 | #define DRM_EXYNOS_GEM_MMAP 0x02 | 332 | #define DRM_EXYNOS_GEM_MMAP 0x02 |
| @@ -175,6 +339,12 @@ struct drm_exynos_g2d_exec { | |||
| 175 | #define DRM_EXYNOS_G2D_SET_CMDLIST 0x21 | 339 | #define DRM_EXYNOS_G2D_SET_CMDLIST 0x21 |
| 176 | #define DRM_EXYNOS_G2D_EXEC 0x22 | 340 | #define DRM_EXYNOS_G2D_EXEC 0x22 |
| 177 | 341 | ||
| 342 | /* IPP - Image Post Processing */ | ||
| 343 | #define DRM_EXYNOS_IPP_GET_PROPERTY 0x30 | ||
| 344 | #define DRM_EXYNOS_IPP_SET_PROPERTY 0x31 | ||
| 345 | #define DRM_EXYNOS_IPP_QUEUE_BUF 0x32 | ||
| 346 | #define DRM_EXYNOS_IPP_CMD_CTRL 0x33 | ||
| 347 | |||
| 178 | #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ | 348 | #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ |
| 179 | DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) | 349 | DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) |
| 180 | 350 | ||
| @@ -197,8 +367,18 @@ struct drm_exynos_g2d_exec { | |||
| 197 | #define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ | 367 | #define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ |
| 198 | DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) | 368 | DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) |
| 199 | 369 | ||
| 370 | #define DRM_IOCTL_EXYNOS_IPP_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 371 | DRM_EXYNOS_IPP_GET_PROPERTY, struct drm_exynos_ipp_prop_list) | ||
| 372 | #define DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 373 | DRM_EXYNOS_IPP_SET_PROPERTY, struct drm_exynos_ipp_property) | ||
| 374 | #define DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 375 | DRM_EXYNOS_IPP_QUEUE_BUF, struct drm_exynos_ipp_queue_buf) | ||
| 376 | #define DRM_IOCTL_EXYNOS_IPP_CMD_CTRL DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 377 | DRM_EXYNOS_IPP_CMD_CTRL, struct drm_exynos_ipp_cmd_ctrl) | ||
| 378 | |||
| 200 | /* EXYNOS specific events */ | 379 | /* EXYNOS specific events */ |
| 201 | #define DRM_EXYNOS_G2D_EVENT 0x80000000 | 380 | #define DRM_EXYNOS_G2D_EVENT 0x80000000 |
| 381 | #define DRM_EXYNOS_IPP_EVENT 0x80000001 | ||
| 202 | 382 | ||
| 203 | struct drm_exynos_g2d_event { | 383 | struct drm_exynos_g2d_event { |
| 204 | struct drm_event base; | 384 | struct drm_event base; |
| @@ -209,4 +389,14 @@ struct drm_exynos_g2d_event { | |||
| 209 | __u32 reserved; | 389 | __u32 reserved; |
| 210 | }; | 390 | }; |
| 211 | 391 | ||
| 392 | struct drm_exynos_ipp_event { | ||
| 393 | struct drm_event base; | ||
| 394 | __u64 user_data; | ||
| 395 | __u32 tv_sec; | ||
| 396 | __u32 tv_usec; | ||
| 397 | __u32 prop_id; | ||
| 398 | __u32 reserved; | ||
| 399 | __u32 buf_id[EXYNOS_DRM_OPS_MAX]; | ||
| 400 | }; | ||
| 401 | |||
| 212 | #endif /* _UAPI_EXYNOS_DRM_H_ */ | 402 | #endif /* _UAPI_EXYNOS_DRM_H_ */ |
