diff options
Diffstat (limited to 'drivers')
33 files changed, 8942 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 | |||