diff options
Diffstat (limited to 'drivers/gpu/drm/shmobile/shmob_drm_plane.c')
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_plane.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c new file mode 100644 index 000000000000..e1eb899b0288 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* | ||
2 | * shmob_drm_plane.c -- SH Mobile DRM Planes | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
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 as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <drm/drmP.h> | ||
15 | #include <drm/drm_crtc.h> | ||
16 | #include <drm/drm_crtc_helper.h> | ||
17 | #include <drm/drm_fb_cma_helper.h> | ||
18 | #include <drm/drm_gem_cma_helper.h> | ||
19 | |||
20 | #include <video/sh_mobile_meram.h> | ||
21 | |||
22 | #include "shmob_drm_drv.h" | ||
23 | #include "shmob_drm_kms.h" | ||
24 | #include "shmob_drm_plane.h" | ||
25 | #include "shmob_drm_regs.h" | ||
26 | |||
27 | struct shmob_drm_plane { | ||
28 | struct drm_plane plane; | ||
29 | unsigned int index; | ||
30 | unsigned int alpha; | ||
31 | |||
32 | const struct shmob_drm_format_info *format; | ||
33 | unsigned long dma[2]; | ||
34 | |||
35 | unsigned int src_x; | ||
36 | unsigned int src_y; | ||
37 | unsigned int crtc_x; | ||
38 | unsigned int crtc_y; | ||
39 | unsigned int crtc_w; | ||
40 | unsigned int crtc_h; | ||
41 | }; | ||
42 | |||
43 | #define to_shmob_plane(p) container_of(p, struct shmob_drm_plane, plane) | ||
44 | |||
45 | static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane, | ||
46 | struct drm_framebuffer *fb, | ||
47 | int x, int y) | ||
48 | { | ||
49 | struct drm_gem_cma_object *gem; | ||
50 | unsigned int bpp; | ||
51 | |||
52 | bpp = splane->format->yuv ? 8 : splane->format->bpp; | ||
53 | gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
54 | splane->dma[0] = gem->paddr + fb->offsets[0] | ||
55 | + y * fb->pitches[0] + x * bpp / 8; | ||
56 | |||
57 | if (splane->format->yuv) { | ||
58 | bpp = splane->format->bpp - 8; | ||
59 | gem = drm_fb_cma_get_gem_obj(fb, 1); | ||
60 | splane->dma[1] = gem->paddr + fb->offsets[1] | ||
61 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] | ||
62 | + x * (bpp == 16 ? 2 : 1); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, | ||
67 | struct drm_framebuffer *fb) | ||
68 | { | ||
69 | struct shmob_drm_device *sdev = splane->plane.dev->dev_private; | ||
70 | u32 format; | ||
71 | |||
72 | /* TODO: Support ROP3 mode */ | ||
73 | format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT); | ||
74 | |||
75 | switch (splane->format->fourcc) { | ||
76 | case DRM_FORMAT_RGB565: | ||
77 | case DRM_FORMAT_NV21: | ||
78 | case DRM_FORMAT_NV61: | ||
79 | case DRM_FORMAT_NV42: | ||
80 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; | ||
81 | break; | ||
82 | case DRM_FORMAT_RGB888: | ||
83 | case DRM_FORMAT_NV12: | ||
84 | case DRM_FORMAT_NV16: | ||
85 | case DRM_FORMAT_NV24: | ||
86 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; | ||
87 | break; | ||
88 | case DRM_FORMAT_ARGB8888: | ||
89 | default: | ||
90 | format |= LDBBSIFR_SWPL; | ||
91 | break; | ||
92 | } | ||
93 | |||
94 | switch (splane->format->fourcc) { | ||
95 | case DRM_FORMAT_RGB565: | ||
96 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; | ||
97 | break; | ||
98 | case DRM_FORMAT_RGB888: | ||
99 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; | ||
100 | break; | ||
101 | case DRM_FORMAT_ARGB8888: | ||
102 | format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; | ||
103 | break; | ||
104 | case DRM_FORMAT_NV12: | ||
105 | case DRM_FORMAT_NV21: | ||
106 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; | ||
107 | break; | ||
108 | case DRM_FORMAT_NV16: | ||
109 | case DRM_FORMAT_NV61: | ||
110 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; | ||
111 | break; | ||
112 | case DRM_FORMAT_NV24: | ||
113 | case DRM_FORMAT_NV42: | ||
114 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; | ||
115 | break; | ||
116 | } | ||
117 | |||
118 | #define plane_reg_dump(sdev, splane, reg) \ | ||
119 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ | ||
120 | splane->index, #reg, \ | ||
121 | lcdc_read(sdev, reg(splane->index)), \ | ||
122 | lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) | ||
123 | |||
124 | plane_reg_dump(sdev, splane, LDBnBSIFR); | ||
125 | plane_reg_dump(sdev, splane, LDBnBSSZR); | ||
126 | plane_reg_dump(sdev, splane, LDBnBLOCR); | ||
127 | plane_reg_dump(sdev, splane, LDBnBSMWR); | ||
128 | plane_reg_dump(sdev, splane, LDBnBSAYR); | ||
129 | plane_reg_dump(sdev, splane, LDBnBSACR); | ||
130 | |||
131 | lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); | ||
132 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | ||
133 | "LDBCR", lcdc_read(sdev, LDBCR)); | ||
134 | |||
135 | lcdc_write(sdev, LDBnBSIFR(splane->index), format); | ||
136 | |||
137 | lcdc_write(sdev, LDBnBSSZR(splane->index), | ||
138 | (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) | | ||
139 | (splane->crtc_w << LDBBSSZR_BHSS_SHIFT)); | ||
140 | lcdc_write(sdev, LDBnBLOCR(splane->index), | ||
141 | (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) | | ||
142 | (splane->crtc_x << LDBBLOCR_CHLC_SHIFT)); | ||
143 | lcdc_write(sdev, LDBnBSMWR(splane->index), | ||
144 | fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); | ||
145 | |||
146 | shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y); | ||
147 | |||
148 | lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]); | ||
149 | if (splane->format->yuv) | ||
150 | lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]); | ||
151 | |||
152 | lcdc_write(sdev, LDBCR, | ||
153 | LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); | ||
154 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | ||
155 | "LDBCR", lcdc_read(sdev, LDBCR)); | ||
156 | |||
157 | plane_reg_dump(sdev, splane, LDBnBSIFR); | ||
158 | plane_reg_dump(sdev, splane, LDBnBSSZR); | ||
159 | plane_reg_dump(sdev, splane, LDBnBLOCR); | ||
160 | plane_reg_dump(sdev, splane, LDBnBSMWR); | ||
161 | plane_reg_dump(sdev, splane, LDBnBSAYR); | ||
162 | plane_reg_dump(sdev, splane, LDBnBSACR); | ||
163 | } | ||
164 | |||
165 | void shmob_drm_plane_setup(struct drm_plane *plane) | ||
166 | { | ||
167 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
168 | |||
169 | if (plane->fb == NULL || !plane->enabled) | ||
170 | return; | ||
171 | |||
172 | __shmob_drm_plane_setup(splane, plane->fb); | ||
173 | } | ||
174 | |||
175 | static int | ||
176 | shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | ||
177 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
178 | unsigned int crtc_w, unsigned int crtc_h, | ||
179 | uint32_t src_x, uint32_t src_y, | ||
180 | uint32_t src_w, uint32_t src_h) | ||
181 | { | ||
182 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
183 | struct shmob_drm_device *sdev = plane->dev->dev_private; | ||
184 | const struct shmob_drm_format_info *format; | ||
185 | |||
186 | format = shmob_drm_format_info(fb->pixel_format); | ||
187 | if (format == NULL) { | ||
188 | dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", | ||
189 | fb->pixel_format); | ||
190 | return -EINVAL; | ||
191 | } | ||
192 | |||
193 | if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { | ||
194 | dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__); | ||
195 | return -EINVAL; | ||
196 | } | ||
197 | |||
198 | splane->format = format; | ||
199 | |||
200 | splane->src_x = src_x >> 16; | ||
201 | splane->src_y = src_y >> 16; | ||
202 | splane->crtc_x = crtc_x; | ||
203 | splane->crtc_y = crtc_y; | ||
204 | splane->crtc_w = crtc_w; | ||
205 | splane->crtc_h = crtc_h; | ||
206 | |||
207 | __shmob_drm_plane_setup(splane, fb); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int shmob_drm_plane_disable(struct drm_plane *plane) | ||
212 | { | ||
213 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
214 | struct shmob_drm_device *sdev = plane->dev->dev_private; | ||
215 | |||
216 | splane->format = NULL; | ||
217 | |||
218 | lcdc_write(sdev, LDBnBSIFR(splane->index), 0); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static void shmob_drm_plane_destroy(struct drm_plane *plane) | ||
223 | { | ||
224 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
225 | |||
226 | shmob_drm_plane_disable(plane); | ||
227 | drm_plane_cleanup(plane); | ||
228 | kfree(splane); | ||
229 | } | ||
230 | |||
231 | static const struct drm_plane_funcs shmob_drm_plane_funcs = { | ||
232 | .update_plane = shmob_drm_plane_update, | ||
233 | .disable_plane = shmob_drm_plane_disable, | ||
234 | .destroy = shmob_drm_plane_destroy, | ||
235 | }; | ||
236 | |||
237 | static const uint32_t formats[] = { | ||
238 | DRM_FORMAT_RGB565, | ||
239 | DRM_FORMAT_RGB888, | ||
240 | DRM_FORMAT_ARGB8888, | ||
241 | DRM_FORMAT_NV12, | ||
242 | DRM_FORMAT_NV21, | ||
243 | DRM_FORMAT_NV16, | ||
244 | DRM_FORMAT_NV61, | ||
245 | DRM_FORMAT_NV24, | ||
246 | DRM_FORMAT_NV42, | ||
247 | }; | ||
248 | |||
249 | int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index) | ||
250 | { | ||
251 | struct shmob_drm_plane *splane; | ||
252 | int ret; | ||
253 | |||
254 | splane = kzalloc(sizeof(*splane), GFP_KERNEL); | ||
255 | if (splane == NULL) | ||
256 | return -ENOMEM; | ||
257 | |||
258 | splane->index = index; | ||
259 | splane->alpha = 255; | ||
260 | |||
261 | ret = drm_plane_init(sdev->ddev, &splane->plane, 1, | ||
262 | &shmob_drm_plane_funcs, formats, | ||
263 | ARRAY_SIZE(formats), false); | ||
264 | if (ret < 0) | ||
265 | kfree(splane); | ||
266 | |||
267 | return ret; | ||
268 | } | ||