Commit | Line | Data |
---|---|---|
1a396789 BB |
1 | /* |
2 | * Copyright (C) 2014 Free Electrons | |
3 | * Copyright (C) 2014 Atmel | |
4 | * | |
5 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "atmel_hlcdc_dc.h" | |
21 | ||
2389fc13 BB |
22 | /** |
23 | * Atmel HLCDC Plane state structure. | |
24 | * | |
25 | * @base: DRM plane state | |
26 | * @crtc_x: x position of the plane relative to the CRTC | |
27 | * @crtc_y: y position of the plane relative to the CRTC | |
28 | * @crtc_w: visible width of the plane | |
29 | * @crtc_h: visible height of the plane | |
30 | * @src_x: x buffer position | |
31 | * @src_y: y buffer position | |
32 | * @src_w: buffer width | |
33 | * @src_h: buffer height | |
34 | * @alpha: alpha blending of the plane | |
35 | * @bpp: bytes per pixel deduced from pixel_format | |
36 | * @offsets: offsets to apply to the GEM buffers | |
37 | * @xstride: value to add to the pixel pointer between each line | |
38 | * @pstride: value to add to the pixel pointer between each pixel | |
39 | * @nplanes: number of planes (deduced from pixel_format) | |
40 | */ | |
41 | struct atmel_hlcdc_plane_state { | |
42 | struct drm_plane_state base; | |
43 | int crtc_x; | |
44 | int crtc_y; | |
45 | unsigned int crtc_w; | |
46 | unsigned int crtc_h; | |
47 | uint32_t src_x; | |
48 | uint32_t src_y; | |
49 | uint32_t src_w; | |
50 | uint32_t src_h; | |
51 | ||
52 | u8 alpha; | |
53 | ||
54 | /* These fields are private and should not be touched */ | |
55 | int bpp[ATMEL_HLCDC_MAX_PLANES]; | |
56 | unsigned int offsets[ATMEL_HLCDC_MAX_PLANES]; | |
57 | int xstride[ATMEL_HLCDC_MAX_PLANES]; | |
58 | int pstride[ATMEL_HLCDC_MAX_PLANES]; | |
59 | int nplanes; | |
60 | }; | |
61 | ||
62 | static inline struct atmel_hlcdc_plane_state * | |
63 | drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s) | |
64 | { | |
65 | return container_of(s, struct atmel_hlcdc_plane_state, base); | |
66 | } | |
67 | ||
1a396789 BB |
68 | #define SUBPIXEL_MASK 0xffff |
69 | ||
70 | static uint32_t rgb_formats[] = { | |
71 | DRM_FORMAT_XRGB4444, | |
72 | DRM_FORMAT_ARGB4444, | |
73 | DRM_FORMAT_RGBA4444, | |
74 | DRM_FORMAT_ARGB1555, | |
75 | DRM_FORMAT_RGB565, | |
76 | DRM_FORMAT_RGB888, | |
77 | DRM_FORMAT_XRGB8888, | |
78 | DRM_FORMAT_ARGB8888, | |
79 | DRM_FORMAT_RGBA8888, | |
80 | }; | |
81 | ||
82 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = { | |
83 | .formats = rgb_formats, | |
84 | .nformats = ARRAY_SIZE(rgb_formats), | |
85 | }; | |
86 | ||
87 | static uint32_t rgb_and_yuv_formats[] = { | |
88 | DRM_FORMAT_XRGB4444, | |
89 | DRM_FORMAT_ARGB4444, | |
90 | DRM_FORMAT_RGBA4444, | |
91 | DRM_FORMAT_ARGB1555, | |
92 | DRM_FORMAT_RGB565, | |
93 | DRM_FORMAT_RGB888, | |
94 | DRM_FORMAT_XRGB8888, | |
95 | DRM_FORMAT_ARGB8888, | |
96 | DRM_FORMAT_RGBA8888, | |
97 | DRM_FORMAT_AYUV, | |
98 | DRM_FORMAT_YUYV, | |
99 | DRM_FORMAT_UYVY, | |
100 | DRM_FORMAT_YVYU, | |
101 | DRM_FORMAT_VYUY, | |
102 | DRM_FORMAT_NV21, | |
103 | DRM_FORMAT_NV61, | |
104 | DRM_FORMAT_YUV422, | |
105 | DRM_FORMAT_YUV420, | |
106 | }; | |
107 | ||
108 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = { | |
109 | .formats = rgb_and_yuv_formats, | |
110 | .nformats = ARRAY_SIZE(rgb_and_yuv_formats), | |
111 | }; | |
112 | ||
113 | static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) | |
114 | { | |
115 | switch (format) { | |
116 | case DRM_FORMAT_XRGB4444: | |
117 | *mode = ATMEL_HLCDC_XRGB4444_MODE; | |
118 | break; | |
119 | case DRM_FORMAT_ARGB4444: | |
120 | *mode = ATMEL_HLCDC_ARGB4444_MODE; | |
121 | break; | |
122 | case DRM_FORMAT_RGBA4444: | |
123 | *mode = ATMEL_HLCDC_RGBA4444_MODE; | |
124 | break; | |
125 | case DRM_FORMAT_RGB565: | |
126 | *mode = ATMEL_HLCDC_RGB565_MODE; | |
127 | break; | |
128 | case DRM_FORMAT_RGB888: | |
129 | *mode = ATMEL_HLCDC_RGB888_MODE; | |
130 | break; | |
131 | case DRM_FORMAT_ARGB1555: | |
132 | *mode = ATMEL_HLCDC_ARGB1555_MODE; | |
133 | break; | |
134 | case DRM_FORMAT_XRGB8888: | |
135 | *mode = ATMEL_HLCDC_XRGB8888_MODE; | |
136 | break; | |
137 | case DRM_FORMAT_ARGB8888: | |
138 | *mode = ATMEL_HLCDC_ARGB8888_MODE; | |
139 | break; | |
140 | case DRM_FORMAT_RGBA8888: | |
141 | *mode = ATMEL_HLCDC_RGBA8888_MODE; | |
142 | break; | |
143 | case DRM_FORMAT_AYUV: | |
144 | *mode = ATMEL_HLCDC_AYUV_MODE; | |
145 | break; | |
146 | case DRM_FORMAT_YUYV: | |
147 | *mode = ATMEL_HLCDC_YUYV_MODE; | |
148 | break; | |
149 | case DRM_FORMAT_UYVY: | |
150 | *mode = ATMEL_HLCDC_UYVY_MODE; | |
151 | break; | |
152 | case DRM_FORMAT_YVYU: | |
153 | *mode = ATMEL_HLCDC_YVYU_MODE; | |
154 | break; | |
155 | case DRM_FORMAT_VYUY: | |
156 | *mode = ATMEL_HLCDC_VYUY_MODE; | |
157 | break; | |
158 | case DRM_FORMAT_NV21: | |
159 | *mode = ATMEL_HLCDC_NV21_MODE; | |
160 | break; | |
161 | case DRM_FORMAT_NV61: | |
162 | *mode = ATMEL_HLCDC_NV61_MODE; | |
163 | break; | |
164 | case DRM_FORMAT_YUV420: | |
165 | *mode = ATMEL_HLCDC_YUV420_MODE; | |
166 | break; | |
167 | case DRM_FORMAT_YUV422: | |
168 | *mode = ATMEL_HLCDC_YUV422_MODE; | |
169 | break; | |
170 | default: | |
171 | return -ENOTSUPP; | |
172 | } | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
2389fc13 | 177 | static bool atmel_hlcdc_format_embeds_alpha(u32 format) |
1a396789 BB |
178 | { |
179 | int i; | |
180 | ||
181 | for (i = 0; i < sizeof(format); i++) { | |
182 | char tmp = (format >> (8 * i)) & 0xff; | |
183 | ||
184 | if (tmp == 'A') | |
185 | return true; | |
186 | } | |
187 | ||
188 | return false; | |
189 | } | |
190 | ||
191 | static u32 heo_downscaling_xcoef[] = { | |
192 | 0x11343311, | |
193 | 0x000000f7, | |
194 | 0x1635300c, | |
195 | 0x000000f9, | |
196 | 0x1b362c08, | |
197 | 0x000000fb, | |
198 | 0x1f372804, | |
199 | 0x000000fe, | |
200 | 0x24382400, | |
201 | 0x00000000, | |
202 | 0x28371ffe, | |
203 | 0x00000004, | |
204 | 0x2c361bfb, | |
205 | 0x00000008, | |
206 | 0x303516f9, | |
207 | 0x0000000c, | |
208 | }; | |
209 | ||
210 | static u32 heo_downscaling_ycoef[] = { | |
211 | 0x00123737, | |
212 | 0x00173732, | |
213 | 0x001b382d, | |
214 | 0x001f3928, | |
215 | 0x00243824, | |
216 | 0x0028391f, | |
217 | 0x002d381b, | |
218 | 0x00323717, | |
219 | }; | |
220 | ||
221 | static u32 heo_upscaling_xcoef[] = { | |
222 | 0xf74949f7, | |
223 | 0x00000000, | |
224 | 0xf55f33fb, | |
225 | 0x000000fe, | |
226 | 0xf5701efe, | |
227 | 0x000000ff, | |
228 | 0xf87c0dff, | |
229 | 0x00000000, | |
230 | 0x00800000, | |
231 | 0x00000000, | |
232 | 0x0d7cf800, | |
233 | 0x000000ff, | |
234 | 0x1e70f5ff, | |
235 | 0x000000fe, | |
236 | 0x335ff5fe, | |
237 | 0x000000fb, | |
238 | }; | |
239 | ||
240 | static u32 heo_upscaling_ycoef[] = { | |
241 | 0x00004040, | |
242 | 0x00075920, | |
243 | 0x00056f0c, | |
244 | 0x00027b03, | |
245 | 0x00008000, | |
246 | 0x00037b02, | |
247 | 0x000c6f05, | |
248 | 0x00205907, | |
249 | }; | |
250 | ||
251 | static void | |
252 | atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 253 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
254 | { |
255 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
256 | &plane->layer.desc->layout; | |
257 | ||
258 | if (layout->size) | |
259 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
260 | layout->size, | |
261 | 0xffffffff, | |
2389fc13 BB |
262 | (state->crtc_w - 1) | |
263 | ((state->crtc_h - 1) << 16)); | |
1a396789 BB |
264 | |
265 | if (layout->memsize) | |
266 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
267 | layout->memsize, | |
268 | 0xffffffff, | |
2389fc13 BB |
269 | (state->src_w - 1) | |
270 | ((state->src_h - 1) << 16)); | |
1a396789 BB |
271 | |
272 | if (layout->pos) | |
273 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
274 | layout->pos, | |
275 | 0xffffffff, | |
2389fc13 BB |
276 | state->crtc_x | |
277 | (state->crtc_y << 16)); | |
1a396789 BB |
278 | |
279 | /* TODO: rework the rescaling part */ | |
2389fc13 | 280 | if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) { |
1a396789 BB |
281 | u32 factor_reg = 0; |
282 | ||
2389fc13 | 283 | if (state->crtc_w != state->src_w) { |
1a396789 BB |
284 | int i; |
285 | u32 factor; | |
286 | u32 *coeff_tab = heo_upscaling_xcoef; | |
287 | u32 max_memsize; | |
288 | ||
2389fc13 | 289 | if (state->crtc_w < state->src_w) |
1a396789 BB |
290 | coeff_tab = heo_downscaling_xcoef; |
291 | for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++) | |
292 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
293 | 17 + i, | |
294 | 0xffffffff, | |
295 | coeff_tab[i]); | |
2389fc13 BB |
296 | factor = ((8 * 256 * state->src_w) - (256 * 4)) / |
297 | state->crtc_w; | |
1a396789 | 298 | factor++; |
2389fc13 | 299 | max_memsize = ((factor * state->crtc_w) + (256 * 4)) / |
1a396789 | 300 | 2048; |
2389fc13 | 301 | if (max_memsize > state->src_w) |
1a396789 BB |
302 | factor--; |
303 | factor_reg |= factor | 0x80000000; | |
304 | } | |
305 | ||
2389fc13 | 306 | if (state->crtc_h != state->src_h) { |
1a396789 BB |
307 | int i; |
308 | u32 factor; | |
309 | u32 *coeff_tab = heo_upscaling_ycoef; | |
310 | u32 max_memsize; | |
311 | ||
2389fc13 | 312 | if (state->crtc_w < state->src_w) |
1a396789 BB |
313 | coeff_tab = heo_downscaling_ycoef; |
314 | for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++) | |
315 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
316 | 33 + i, | |
317 | 0xffffffff, | |
318 | coeff_tab[i]); | |
2389fc13 BB |
319 | factor = ((8 * 256 * state->src_w) - (256 * 4)) / |
320 | state->crtc_w; | |
1a396789 | 321 | factor++; |
2389fc13 | 322 | max_memsize = ((factor * state->crtc_w) + (256 * 4)) / |
1a396789 | 323 | 2048; |
2389fc13 | 324 | if (max_memsize > state->src_w) |
1a396789 BB |
325 | factor--; |
326 | factor_reg |= (factor << 16) | 0x80000000; | |
327 | } | |
328 | ||
329 | atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, | |
330 | factor_reg); | |
331 | } | |
332 | } | |
333 | ||
334 | static void | |
335 | atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 336 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
337 | { |
338 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
339 | &plane->layer.desc->layout; | |
340 | unsigned int cfg = ATMEL_HLCDC_LAYER_DMA; | |
341 | ||
342 | if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { | |
343 | cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | | |
344 | ATMEL_HLCDC_LAYER_ITER; | |
345 | ||
2389fc13 | 346 | if (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)) |
1a396789 BB |
347 | cfg |= ATMEL_HLCDC_LAYER_LAEN; |
348 | else | |
2389fc13 BB |
349 | cfg |= ATMEL_HLCDC_LAYER_GAEN | |
350 | ATMEL_HLCDC_LAYER_GA(state->alpha); | |
1a396789 BB |
351 | } |
352 | ||
353 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
354 | ATMEL_HLCDC_LAYER_DMA_CFG_ID, | |
355 | ATMEL_HLCDC_LAYER_DMA_BLEN_MASK, | |
356 | ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16); | |
357 | ||
358 | atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config, | |
359 | ATMEL_HLCDC_LAYER_ITER2BL | | |
360 | ATMEL_HLCDC_LAYER_ITER | | |
361 | ATMEL_HLCDC_LAYER_GAEN | | |
2389fc13 | 362 | ATMEL_HLCDC_LAYER_GA_MASK | |
1a396789 BB |
363 | ATMEL_HLCDC_LAYER_LAEN | |
364 | ATMEL_HLCDC_LAYER_OVR | | |
365 | ATMEL_HLCDC_LAYER_DMA, cfg); | |
366 | } | |
367 | ||
368 | static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 369 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
370 | { |
371 | u32 cfg; | |
372 | int ret; | |
373 | ||
2389fc13 BB |
374 | ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->pixel_format, |
375 | &cfg); | |
1a396789 BB |
376 | if (ret) |
377 | return; | |
378 | ||
2389fc13 BB |
379 | if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 || |
380 | state->base.fb->pixel_format == DRM_FORMAT_NV61) && | |
381 | (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)))) | |
1a396789 BB |
382 | cfg |= ATMEL_HLCDC_YUV422ROT; |
383 | ||
384 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
385 | ATMEL_HLCDC_LAYER_FORMAT_CFG_ID, | |
386 | 0xffffffff, | |
387 | cfg); | |
388 | ||
389 | /* | |
390 | * Rotation optimization is not working on RGB888 (rotation is still | |
391 | * working but without any optimization). | |
392 | */ | |
2389fc13 | 393 | if (state->base.fb->pixel_format == DRM_FORMAT_RGB888) |
1a396789 BB |
394 | cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS; |
395 | else | |
396 | cfg = 0; | |
397 | ||
398 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
399 | ATMEL_HLCDC_LAYER_DMA_CFG_ID, | |
400 | ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg); | |
401 | } | |
402 | ||
403 | static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 404 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
405 | { |
406 | struct atmel_hlcdc_layer *layer = &plane->layer; | |
407 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
408 | &layer->desc->layout; | |
409 | int i; | |
410 | ||
2389fc13 BB |
411 | atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb, |
412 | state->offsets); | |
1a396789 | 413 | |
2389fc13 | 414 | for (i = 0; i < state->nplanes; i++) { |
1a396789 BB |
415 | if (layout->xstride[i]) { |
416 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
417 | layout->xstride[i], | |
418 | 0xffffffff, | |
2389fc13 | 419 | state->xstride[i]); |
1a396789 BB |
420 | } |
421 | ||
422 | if (layout->pstride[i]) { | |
423 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
424 | layout->pstride[i], | |
425 | 0xffffffff, | |
2389fc13 | 426 | state->pstride[i]); |
1a396789 BB |
427 | } |
428 | } | |
429 | } | |
430 | ||
2389fc13 BB |
431 | static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, |
432 | struct drm_plane_state *s) | |
1a396789 BB |
433 | { |
434 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
2389fc13 BB |
435 | struct atmel_hlcdc_plane_state *state = |
436 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
1a396789 BB |
437 | const struct atmel_hlcdc_layer_cfg_layout *layout = |
438 | &plane->layer.desc->layout; | |
2389fc13 BB |
439 | struct drm_framebuffer *fb = state->base.fb; |
440 | const struct drm_display_mode *mode; | |
441 | struct drm_crtc_state *crtc_state; | |
1a396789 BB |
442 | unsigned int patched_crtc_w; |
443 | unsigned int patched_crtc_h; | |
444 | unsigned int patched_src_w; | |
445 | unsigned int patched_src_h; | |
446 | unsigned int tmp; | |
447 | int x_offset = 0; | |
448 | int y_offset = 0; | |
449 | int hsub = 1; | |
450 | int vsub = 1; | |
451 | int i; | |
452 | ||
2389fc13 BB |
453 | if (!state->base.crtc || !fb) |
454 | return 0; | |
455 | ||
456 | crtc_state = s->state->crtc_states[drm_crtc_index(s->crtc)]; | |
457 | mode = &crtc_state->adjusted_mode; | |
458 | ||
459 | state->src_x = s->src_x; | |
460 | state->src_y = s->src_y; | |
461 | state->src_h = s->src_h; | |
462 | state->src_w = s->src_w; | |
463 | state->crtc_x = s->crtc_x; | |
464 | state->crtc_y = s->crtc_y; | |
465 | state->crtc_h = s->crtc_h; | |
466 | state->crtc_w = s->crtc_w; | |
467 | if ((state->src_x | state->src_y | state->src_w | state->src_h) & | |
1a396789 BB |
468 | SUBPIXEL_MASK) |
469 | return -EINVAL; | |
470 | ||
2389fc13 BB |
471 | state->src_x >>= 16; |
472 | state->src_y >>= 16; | |
473 | state->src_w >>= 16; | |
474 | state->src_h >>= 16; | |
1a396789 | 475 | |
2389fc13 BB |
476 | state->nplanes = drm_format_num_planes(fb->pixel_format); |
477 | if (state->nplanes > ATMEL_HLCDC_MAX_PLANES) | |
1a396789 BB |
478 | return -EINVAL; |
479 | ||
480 | /* | |
481 | * Swap width and size in case of 90 or 270 degrees rotation | |
482 | */ | |
2389fc13 BB |
483 | if (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { |
484 | tmp = state->crtc_w; | |
485 | state->crtc_w = state->crtc_h; | |
486 | state->crtc_h = tmp; | |
487 | tmp = state->src_w; | |
488 | state->src_w = state->src_h; | |
489 | state->src_h = tmp; | |
1a396789 BB |
490 | } |
491 | ||
2389fc13 BB |
492 | if (state->crtc_x + state->crtc_w > mode->hdisplay) |
493 | patched_crtc_w = mode->hdisplay - state->crtc_x; | |
1a396789 | 494 | else |
2389fc13 | 495 | patched_crtc_w = state->crtc_w; |
1a396789 | 496 | |
2389fc13 BB |
497 | if (state->crtc_x < 0) { |
498 | patched_crtc_w += state->crtc_x; | |
499 | x_offset = -state->crtc_x; | |
500 | state->crtc_x = 0; | |
1a396789 BB |
501 | } |
502 | ||
2389fc13 BB |
503 | if (state->crtc_y + state->crtc_h > mode->vdisplay) |
504 | patched_crtc_h = mode->vdisplay - state->crtc_y; | |
1a396789 | 505 | else |
2389fc13 | 506 | patched_crtc_h = state->crtc_h; |
1a396789 | 507 | |
2389fc13 BB |
508 | if (state->crtc_y < 0) { |
509 | patched_crtc_h += state->crtc_y; | |
510 | y_offset = -state->crtc_y; | |
511 | state->crtc_y = 0; | |
1a396789 BB |
512 | } |
513 | ||
2389fc13 BB |
514 | patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w, |
515 | state->crtc_w); | |
516 | patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h, | |
517 | state->crtc_h); | |
1a396789 | 518 | |
2389fc13 BB |
519 | hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); |
520 | vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); | |
1a396789 | 521 | |
2389fc13 | 522 | for (i = 0; i < state->nplanes; i++) { |
1a396789 BB |
523 | unsigned int offset = 0; |
524 | int xdiv = i ? hsub : 1; | |
525 | int ydiv = i ? vsub : 1; | |
526 | ||
2389fc13 BB |
527 | state->bpp[i] = drm_format_plane_cpp(fb->pixel_format, i); |
528 | if (!state->bpp[i]) | |
1a396789 BB |
529 | return -EINVAL; |
530 | ||
2389fc13 | 531 | switch (state->base.rotation & 0xf) { |
1a396789 | 532 | case BIT(DRM_ROTATE_90): |
2389fc13 BB |
533 | offset = ((y_offset + state->src_y + patched_src_w - 1) / |
534 | ydiv) * fb->pitches[i]; | |
535 | offset += ((x_offset + state->src_x) / xdiv) * | |
536 | state->bpp[i]; | |
537 | state->xstride[i] = ((patched_src_w - 1) / ydiv) * | |
538 | fb->pitches[i]; | |
539 | state->pstride[i] = -fb->pitches[i] - state->bpp[i]; | |
1a396789 BB |
540 | break; |
541 | case BIT(DRM_ROTATE_180): | |
2389fc13 BB |
542 | offset = ((y_offset + state->src_y + patched_src_h - 1) / |
543 | ydiv) * fb->pitches[i]; | |
544 | offset += ((x_offset + state->src_x + patched_src_w - 1) / | |
545 | xdiv) * state->bpp[i]; | |
546 | state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) * | |
547 | state->bpp[i]) - fb->pitches[i]; | |
548 | state->pstride[i] = -2 * state->bpp[i]; | |
1a396789 BB |
549 | break; |
550 | case BIT(DRM_ROTATE_270): | |
2389fc13 BB |
551 | offset = ((y_offset + state->src_y) / ydiv) * |
552 | fb->pitches[i]; | |
553 | offset += ((x_offset + state->src_x + patched_src_h - 1) / | |
554 | xdiv) * state->bpp[i]; | |
555 | state->xstride[i] = -(((patched_src_w - 1) / ydiv) * | |
556 | fb->pitches[i]) - | |
557 | (2 * state->bpp[i]); | |
558 | state->pstride[i] = fb->pitches[i] - state->bpp[i]; | |
1a396789 BB |
559 | break; |
560 | case BIT(DRM_ROTATE_0): | |
561 | default: | |
2389fc13 BB |
562 | offset = ((y_offset + state->src_y) / ydiv) * |
563 | fb->pitches[i]; | |
564 | offset += ((x_offset + state->src_x) / xdiv) * | |
565 | state->bpp[i]; | |
566 | state->xstride[i] = fb->pitches[i] - | |
1a396789 | 567 | ((patched_src_w / xdiv) * |
2389fc13 BB |
568 | state->bpp[i]); |
569 | state->pstride[i] = 0; | |
1a396789 BB |
570 | break; |
571 | } | |
572 | ||
2389fc13 | 573 | state->offsets[i] = offset + fb->offsets[i]; |
1a396789 BB |
574 | } |
575 | ||
2389fc13 BB |
576 | state->src_w = patched_src_w; |
577 | state->src_h = patched_src_h; | |
578 | state->crtc_w = patched_crtc_w; | |
579 | state->crtc_h = patched_crtc_h; | |
1a396789 | 580 | |
2389fc13 BB |
581 | if (!layout->size && |
582 | (mode->hdisplay != state->crtc_w || | |
583 | mode->vdisplay != state->crtc_h)) | |
584 | return -EINVAL; | |
1a396789 | 585 | |
2389fc13 BB |
586 | if (plane->layer.desc->max_height && |
587 | state->crtc_h > plane->layer.desc->max_height) | |
588 | return -EINVAL; | |
1a396789 | 589 | |
2389fc13 BB |
590 | if (plane->layer.desc->max_width && |
591 | state->crtc_w > plane->layer.desc->max_width) | |
592 | return -EINVAL; | |
1a396789 | 593 | |
2389fc13 BB |
594 | if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) && |
595 | (!layout->memsize || | |
596 | atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format))) | |
597 | return -EINVAL; | |
1a396789 | 598 | |
2389fc13 BB |
599 | if (state->crtc_x < 0 || state->crtc_y < 0) |
600 | return -EINVAL; | |
601 | ||
602 | if (state->crtc_w + state->crtc_x > mode->hdisplay || | |
603 | state->crtc_h + state->crtc_y > mode->vdisplay) | |
604 | return -EINVAL; | |
1a396789 BB |
605 | |
606 | return 0; | |
607 | } | |
608 | ||
2389fc13 BB |
609 | static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, |
610 | struct drm_framebuffer *fb) | |
1a396789 BB |
611 | { |
612 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
1a396789 | 613 | |
2389fc13 | 614 | return atmel_hlcdc_layer_update_start(&plane->layer); |
1a396789 BB |
615 | } |
616 | ||
2389fc13 BB |
617 | static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, |
618 | struct drm_plane_state *old_s) | |
1a396789 | 619 | { |
2389fc13 BB |
620 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
621 | struct atmel_hlcdc_plane_state *state = | |
622 | drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
623 | ||
624 | if (!p->state->crtc || !p->state->fb) | |
625 | return; | |
626 | ||
627 | atmel_hlcdc_plane_update_pos_and_size(plane, state); | |
628 | atmel_hlcdc_plane_update_general_settings(plane, state); | |
629 | atmel_hlcdc_plane_update_format(plane, state); | |
630 | atmel_hlcdc_plane_update_buffers(plane, state); | |
631 | ||
632 | atmel_hlcdc_layer_update_commit(&plane->layer); | |
1a396789 BB |
633 | } |
634 | ||
2389fc13 BB |
635 | static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, |
636 | struct drm_plane_state *old_state) | |
1a396789 BB |
637 | { |
638 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
639 | ||
2389fc13 | 640 | atmel_hlcdc_layer_disable(&plane->layer); |
1a396789 BB |
641 | } |
642 | ||
643 | static void atmel_hlcdc_plane_destroy(struct drm_plane *p) | |
644 | { | |
645 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
646 | ||
647 | if (plane->base.fb) | |
648 | drm_framebuffer_unreference(plane->base.fb); | |
649 | ||
650 | atmel_hlcdc_layer_cleanup(p->dev, &plane->layer); | |
651 | ||
652 | drm_plane_cleanup(p); | |
653 | devm_kfree(p->dev->dev, plane); | |
654 | } | |
655 | ||
2389fc13 BB |
656 | static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p, |
657 | struct drm_plane_state *s, | |
658 | struct drm_property *property, | |
659 | uint64_t val) | |
1a396789 | 660 | { |
2389fc13 BB |
661 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
662 | struct atmel_hlcdc_plane_properties *props = plane->properties; | |
663 | struct atmel_hlcdc_plane_state *state = | |
664 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
1a396789 | 665 | |
2389fc13 BB |
666 | if (property == props->alpha) |
667 | state->alpha = val; | |
668 | else | |
669 | return -EINVAL; | |
1a396789 BB |
670 | |
671 | return 0; | |
672 | } | |
673 | ||
2389fc13 BB |
674 | static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p, |
675 | const struct drm_plane_state *s, | |
676 | struct drm_property *property, | |
677 | uint64_t *val) | |
1a396789 BB |
678 | { |
679 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
680 | struct atmel_hlcdc_plane_properties *props = plane->properties; | |
2389fc13 BB |
681 | const struct atmel_hlcdc_plane_state *state = |
682 | container_of(s, const struct atmel_hlcdc_plane_state, base); | |
1a396789 BB |
683 | |
684 | if (property == props->alpha) | |
2389fc13 | 685 | *val = state->alpha; |
1a396789 BB |
686 | else |
687 | return -EINVAL; | |
688 | ||
689 | return 0; | |
690 | } | |
691 | ||
692 | static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, | |
693 | const struct atmel_hlcdc_layer_desc *desc, | |
694 | struct atmel_hlcdc_plane_properties *props) | |
695 | { | |
696 | struct regmap *regmap = plane->layer.hlcdc->regmap; | |
697 | ||
698 | if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || | |
699 | desc->type == ATMEL_HLCDC_CURSOR_LAYER) { | |
700 | drm_object_attach_property(&plane->base.base, | |
701 | props->alpha, 255); | |
702 | ||
703 | /* Set default alpha value */ | |
704 | regmap_update_bits(regmap, | |
705 | desc->regs_offset + | |
706 | ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer), | |
707 | ATMEL_HLCDC_LAYER_GA_MASK, | |
708 | ATMEL_HLCDC_LAYER_GA_MASK); | |
709 | } | |
710 | ||
711 | if (desc->layout.xstride && desc->layout.pstride) | |
712 | drm_object_attach_property(&plane->base.base, | |
2389fc13 BB |
713 | plane->base.dev->mode_config.rotation_property, |
714 | BIT(DRM_ROTATE_0)); | |
1a396789 BB |
715 | |
716 | if (desc->layout.csc) { | |
717 | /* | |
718 | * TODO: decare a "yuv-to-rgb-conv-factors" property to let | |
719 | * userspace modify these factors (using a BLOB property ?). | |
720 | */ | |
721 | regmap_write(regmap, | |
722 | desc->regs_offset + | |
723 | ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0), | |
724 | 0x4c900091); | |
725 | regmap_write(regmap, | |
726 | desc->regs_offset + | |
727 | ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1), | |
728 | 0x7a5f5090); | |
729 | regmap_write(regmap, | |
730 | desc->regs_offset + | |
731 | ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2), | |
732 | 0x40040890); | |
733 | } | |
734 | } | |
735 | ||
2389fc13 BB |
736 | static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { |
737 | .prepare_fb = atmel_hlcdc_plane_prepare_fb, | |
738 | .atomic_check = atmel_hlcdc_plane_atomic_check, | |
739 | .atomic_update = atmel_hlcdc_plane_atomic_update, | |
740 | .atomic_disable = atmel_hlcdc_plane_atomic_disable, | |
741 | }; | |
742 | ||
743 | static void atmel_hlcdc_plane_reset(struct drm_plane *p) | |
744 | { | |
745 | struct atmel_hlcdc_plane_state *state; | |
746 | ||
747 | if (p->state) { | |
748 | state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
749 | ||
750 | if (state->base.fb) | |
751 | drm_framebuffer_unreference(state->base.fb); | |
752 | ||
753 | kfree(state); | |
754 | p->state = NULL; | |
755 | } | |
756 | ||
757 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
758 | if (state) { | |
759 | state->alpha = 255; | |
760 | p->state = &state->base; | |
761 | p->state->plane = p; | |
762 | } | |
763 | } | |
764 | ||
765 | static struct drm_plane_state * | |
766 | atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) | |
767 | { | |
768 | struct atmel_hlcdc_plane_state *state = | |
769 | drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
770 | struct atmel_hlcdc_plane_state *copy; | |
771 | ||
772 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); | |
773 | if (!copy) | |
774 | return NULL; | |
775 | ||
776 | if (copy->base.fb) | |
777 | drm_framebuffer_reference(copy->base.fb); | |
778 | ||
779 | return ©->base; | |
780 | } | |
781 | ||
782 | static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane, | |
783 | struct drm_plane_state *s) | |
784 | { | |
785 | struct atmel_hlcdc_plane_state *state = | |
786 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
787 | ||
788 | if (s->fb) | |
789 | drm_framebuffer_unreference(s->fb); | |
790 | ||
791 | kfree(state); | |
792 | } | |
793 | ||
1a396789 | 794 | static struct drm_plane_funcs layer_plane_funcs = { |
2389fc13 BB |
795 | .update_plane = drm_atomic_helper_update_plane, |
796 | .disable_plane = drm_atomic_helper_disable_plane, | |
797 | .set_property = drm_atomic_helper_plane_set_property, | |
1a396789 | 798 | .destroy = atmel_hlcdc_plane_destroy, |
2389fc13 BB |
799 | .reset = atmel_hlcdc_plane_reset, |
800 | .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state, | |
801 | .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state, | |
802 | .atomic_set_property = atmel_hlcdc_plane_atomic_set_property, | |
803 | .atomic_get_property = atmel_hlcdc_plane_atomic_get_property, | |
1a396789 BB |
804 | }; |
805 | ||
806 | static struct atmel_hlcdc_plane * | |
807 | atmel_hlcdc_plane_create(struct drm_device *dev, | |
808 | const struct atmel_hlcdc_layer_desc *desc, | |
809 | struct atmel_hlcdc_plane_properties *props) | |
810 | { | |
811 | struct atmel_hlcdc_plane *plane; | |
812 | enum drm_plane_type type; | |
813 | int ret; | |
814 | ||
815 | plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); | |
816 | if (!plane) | |
817 | return ERR_PTR(-ENOMEM); | |
818 | ||
819 | ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc); | |
820 | if (ret) | |
821 | return ERR_PTR(ret); | |
822 | ||
823 | if (desc->type == ATMEL_HLCDC_BASE_LAYER) | |
824 | type = DRM_PLANE_TYPE_PRIMARY; | |
825 | else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER) | |
826 | type = DRM_PLANE_TYPE_CURSOR; | |
827 | else | |
828 | type = DRM_PLANE_TYPE_OVERLAY; | |
829 | ||
830 | ret = drm_universal_plane_init(dev, &plane->base, 0, | |
831 | &layer_plane_funcs, | |
832 | desc->formats->formats, | |
833 | desc->formats->nformats, type); | |
834 | if (ret) | |
835 | return ERR_PTR(ret); | |
836 | ||
2389fc13 BB |
837 | drm_plane_helper_add(&plane->base, |
838 | &atmel_hlcdc_layer_plane_helper_funcs); | |
839 | ||
1a396789 BB |
840 | /* Set default property values*/ |
841 | atmel_hlcdc_plane_init_properties(plane, desc, props); | |
842 | ||
843 | return plane; | |
844 | } | |
845 | ||
846 | static struct atmel_hlcdc_plane_properties * | |
847 | atmel_hlcdc_plane_create_properties(struct drm_device *dev) | |
848 | { | |
849 | struct atmel_hlcdc_plane_properties *props; | |
850 | ||
851 | props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL); | |
852 | if (!props) | |
853 | return ERR_PTR(-ENOMEM); | |
854 | ||
855 | props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255); | |
856 | if (!props->alpha) | |
857 | return ERR_PTR(-ENOMEM); | |
858 | ||
2389fc13 BB |
859 | dev->mode_config.rotation_property = |
860 | drm_mode_create_rotation_property(dev, | |
861 | BIT(DRM_ROTATE_0) | | |
862 | BIT(DRM_ROTATE_90) | | |
863 | BIT(DRM_ROTATE_180) | | |
864 | BIT(DRM_ROTATE_270)); | |
865 | if (!dev->mode_config.rotation_property) | |
1a396789 BB |
866 | return ERR_PTR(-ENOMEM); |
867 | ||
868 | return props; | |
869 | } | |
870 | ||
871 | struct atmel_hlcdc_planes * | |
872 | atmel_hlcdc_create_planes(struct drm_device *dev) | |
873 | { | |
874 | struct atmel_hlcdc_dc *dc = dev->dev_private; | |
875 | struct atmel_hlcdc_plane_properties *props; | |
876 | struct atmel_hlcdc_planes *planes; | |
877 | const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; | |
878 | int nlayers = dc->desc->nlayers; | |
879 | int i; | |
880 | ||
881 | planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL); | |
882 | if (!planes) | |
883 | return ERR_PTR(-ENOMEM); | |
884 | ||
885 | for (i = 0; i < nlayers; i++) { | |
886 | if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER) | |
887 | planes->noverlays++; | |
888 | } | |
889 | ||
890 | if (planes->noverlays) { | |
891 | planes->overlays = devm_kzalloc(dev->dev, | |
892 | planes->noverlays * | |
893 | sizeof(*planes->overlays), | |
894 | GFP_KERNEL); | |
895 | if (!planes->overlays) | |
896 | return ERR_PTR(-ENOMEM); | |
897 | } | |
898 | ||
899 | props = atmel_hlcdc_plane_create_properties(dev); | |
900 | if (IS_ERR(props)) | |
901 | return ERR_CAST(props); | |
902 | ||
903 | planes->noverlays = 0; | |
904 | for (i = 0; i < nlayers; i++) { | |
905 | struct atmel_hlcdc_plane *plane; | |
906 | ||
907 | if (descs[i].type == ATMEL_HLCDC_PP_LAYER) | |
908 | continue; | |
909 | ||
910 | plane = atmel_hlcdc_plane_create(dev, &descs[i], props); | |
911 | if (IS_ERR(plane)) | |
912 | return ERR_CAST(plane); | |
913 | ||
914 | plane->properties = props; | |
915 | ||
916 | switch (descs[i].type) { | |
917 | case ATMEL_HLCDC_BASE_LAYER: | |
918 | if (planes->primary) | |
919 | return ERR_PTR(-EINVAL); | |
920 | planes->primary = plane; | |
921 | break; | |
922 | ||
923 | case ATMEL_HLCDC_OVERLAY_LAYER: | |
924 | planes->overlays[planes->noverlays++] = plane; | |
925 | break; | |
926 | ||
927 | case ATMEL_HLCDC_CURSOR_LAYER: | |
928 | if (planes->cursor) | |
929 | return ERR_PTR(-EINVAL); | |
930 | planes->cursor = plane; | |
931 | break; | |
932 | ||
933 | default: | |
934 | break; | |
935 | } | |
936 | } | |
937 | ||
938 | return planes; | |
939 | } |