1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2014 Free Electrons |
4 | * Copyright (C) 2014 Atmel |
5 | * |
6 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> |
7 | */ |
8 | |
9 | #include <linux/dmapool.h> |
10 | #include <linux/mfd/atmel-hlcdc.h> |
11 | |
12 | #include <drm/drm_atomic.h> |
13 | #include <drm/drm_atomic_helper.h> |
14 | #include <drm/drm_blend.h> |
15 | #include <drm/drm_fb_dma_helper.h> |
16 | #include <drm/drm_fourcc.h> |
17 | #include <drm/drm_framebuffer.h> |
18 | #include <drm/drm_gem_dma_helper.h> |
19 | |
20 | #include "atmel_hlcdc_dc.h" |
21 | |
22 | /** |
23 | * struct atmel_hlcdc_plane_state - 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 | * @disc_x: x discard position |
35 | * @disc_y: y discard position |
36 | * @disc_w: discard width |
37 | * @disc_h: discard height |
38 | * @ahb_id: AHB identification number |
39 | * @bpp: bytes per pixel deduced from pixel_format |
40 | * @offsets: offsets to apply to the GEM buffers |
41 | * @xstride: value to add to the pixel pointer between each line |
42 | * @pstride: value to add to the pixel pointer between each pixel |
43 | * @nplanes: number of planes (deduced from pixel_format) |
44 | * @dscrs: DMA descriptors |
45 | */ |
46 | struct atmel_hlcdc_plane_state { |
47 | struct drm_plane_state base; |
48 | int crtc_x; |
49 | int crtc_y; |
50 | unsigned int crtc_w; |
51 | unsigned int crtc_h; |
52 | uint32_t src_x; |
53 | uint32_t src_y; |
54 | uint32_t src_w; |
55 | uint32_t src_h; |
56 | |
57 | int disc_x; |
58 | int disc_y; |
59 | int disc_w; |
60 | int disc_h; |
61 | |
62 | int ahb_id; |
63 | |
64 | /* These fields are private and should not be touched */ |
65 | int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES]; |
66 | unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES]; |
67 | int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; |
68 | int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; |
69 | int nplanes; |
70 | |
71 | /* DMA descriptors. */ |
72 | struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES]; |
73 | }; |
74 | |
75 | static inline struct atmel_hlcdc_plane_state * |
76 | drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s) |
77 | { |
78 | return container_of(s, struct atmel_hlcdc_plane_state, base); |
79 | } |
80 | |
81 | #define SUBPIXEL_MASK 0xffff |
82 | |
83 | static uint32_t rgb_formats[] = { |
84 | DRM_FORMAT_C8, |
85 | DRM_FORMAT_XRGB4444, |
86 | DRM_FORMAT_ARGB4444, |
87 | DRM_FORMAT_RGBA4444, |
88 | DRM_FORMAT_ARGB1555, |
89 | DRM_FORMAT_RGB565, |
90 | DRM_FORMAT_RGB888, |
91 | DRM_FORMAT_XRGB8888, |
92 | DRM_FORMAT_ARGB8888, |
93 | DRM_FORMAT_RGBA8888, |
94 | }; |
95 | |
96 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = { |
97 | .formats = rgb_formats, |
98 | .nformats = ARRAY_SIZE(rgb_formats), |
99 | }; |
100 | |
101 | static uint32_t rgb_and_yuv_formats[] = { |
102 | DRM_FORMAT_C8, |
103 | DRM_FORMAT_XRGB4444, |
104 | DRM_FORMAT_ARGB4444, |
105 | DRM_FORMAT_RGBA4444, |
106 | DRM_FORMAT_ARGB1555, |
107 | DRM_FORMAT_RGB565, |
108 | DRM_FORMAT_RGB888, |
109 | DRM_FORMAT_XRGB8888, |
110 | DRM_FORMAT_ARGB8888, |
111 | DRM_FORMAT_RGBA8888, |
112 | DRM_FORMAT_AYUV, |
113 | DRM_FORMAT_YUYV, |
114 | DRM_FORMAT_UYVY, |
115 | DRM_FORMAT_YVYU, |
116 | DRM_FORMAT_VYUY, |
117 | DRM_FORMAT_NV21, |
118 | DRM_FORMAT_NV61, |
119 | DRM_FORMAT_YUV422, |
120 | DRM_FORMAT_YUV420, |
121 | }; |
122 | |
123 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = { |
124 | .formats = rgb_and_yuv_formats, |
125 | .nformats = ARRAY_SIZE(rgb_and_yuv_formats), |
126 | }; |
127 | |
128 | static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) |
129 | { |
130 | switch (format) { |
131 | case DRM_FORMAT_C8: |
132 | *mode = ATMEL_HLCDC_C8_MODE; |
133 | break; |
134 | case DRM_FORMAT_XRGB4444: |
135 | *mode = ATMEL_HLCDC_XRGB4444_MODE; |
136 | break; |
137 | case DRM_FORMAT_ARGB4444: |
138 | *mode = ATMEL_HLCDC_ARGB4444_MODE; |
139 | break; |
140 | case DRM_FORMAT_RGBA4444: |
141 | *mode = ATMEL_HLCDC_RGBA4444_MODE; |
142 | break; |
143 | case DRM_FORMAT_RGB565: |
144 | *mode = ATMEL_HLCDC_RGB565_MODE; |
145 | break; |
146 | case DRM_FORMAT_RGB888: |
147 | *mode = ATMEL_HLCDC_RGB888_MODE; |
148 | break; |
149 | case DRM_FORMAT_ARGB1555: |
150 | *mode = ATMEL_HLCDC_ARGB1555_MODE; |
151 | break; |
152 | case DRM_FORMAT_XRGB8888: |
153 | *mode = ATMEL_HLCDC_XRGB8888_MODE; |
154 | break; |
155 | case DRM_FORMAT_ARGB8888: |
156 | *mode = ATMEL_HLCDC_ARGB8888_MODE; |
157 | break; |
158 | case DRM_FORMAT_RGBA8888: |
159 | *mode = ATMEL_HLCDC_RGBA8888_MODE; |
160 | break; |
161 | case DRM_FORMAT_AYUV: |
162 | *mode = ATMEL_HLCDC_AYUV_MODE; |
163 | break; |
164 | case DRM_FORMAT_YUYV: |
165 | *mode = ATMEL_HLCDC_YUYV_MODE; |
166 | break; |
167 | case DRM_FORMAT_UYVY: |
168 | *mode = ATMEL_HLCDC_UYVY_MODE; |
169 | break; |
170 | case DRM_FORMAT_YVYU: |
171 | *mode = ATMEL_HLCDC_YVYU_MODE; |
172 | break; |
173 | case DRM_FORMAT_VYUY: |
174 | *mode = ATMEL_HLCDC_VYUY_MODE; |
175 | break; |
176 | case DRM_FORMAT_NV21: |
177 | *mode = ATMEL_HLCDC_NV21_MODE; |
178 | break; |
179 | case DRM_FORMAT_NV61: |
180 | *mode = ATMEL_HLCDC_NV61_MODE; |
181 | break; |
182 | case DRM_FORMAT_YUV420: |
183 | *mode = ATMEL_HLCDC_YUV420_MODE; |
184 | break; |
185 | case DRM_FORMAT_YUV422: |
186 | *mode = ATMEL_HLCDC_YUV422_MODE; |
187 | break; |
188 | default: |
189 | return -ENOTSUPP; |
190 | } |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | static u32 heo_downscaling_xcoef[] = { |
196 | 0x11343311, |
197 | 0x000000f7, |
198 | 0x1635300c, |
199 | 0x000000f9, |
200 | 0x1b362c08, |
201 | 0x000000fb, |
202 | 0x1f372804, |
203 | 0x000000fe, |
204 | 0x24382400, |
205 | 0x00000000, |
206 | 0x28371ffe, |
207 | 0x00000004, |
208 | 0x2c361bfb, |
209 | 0x00000008, |
210 | 0x303516f9, |
211 | 0x0000000c, |
212 | }; |
213 | |
214 | static u32 heo_downscaling_ycoef[] = { |
215 | 0x00123737, |
216 | 0x00173732, |
217 | 0x001b382d, |
218 | 0x001f3928, |
219 | 0x00243824, |
220 | 0x0028391f, |
221 | 0x002d381b, |
222 | 0x00323717, |
223 | }; |
224 | |
225 | static u32 heo_upscaling_xcoef[] = { |
226 | 0xf74949f7, |
227 | 0x00000000, |
228 | 0xf55f33fb, |
229 | 0x000000fe, |
230 | 0xf5701efe, |
231 | 0x000000ff, |
232 | 0xf87c0dff, |
233 | 0x00000000, |
234 | 0x00800000, |
235 | 0x00000000, |
236 | 0x0d7cf800, |
237 | 0x000000ff, |
238 | 0x1e70f5ff, |
239 | 0x000000fe, |
240 | 0x335ff5fe, |
241 | 0x000000fb, |
242 | }; |
243 | |
244 | static u32 heo_upscaling_ycoef[] = { |
245 | 0x00004040, |
246 | 0x00075920, |
247 | 0x00056f0c, |
248 | 0x00027b03, |
249 | 0x00008000, |
250 | 0x00037b02, |
251 | 0x000c6f05, |
252 | 0x00205907, |
253 | }; |
254 | |
255 | #define ATMEL_HLCDC_XPHIDEF 4 |
256 | #define ATMEL_HLCDC_YPHIDEF 4 |
257 | |
258 | static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize, |
259 | u32 dstsize, |
260 | u32 phidef) |
261 | { |
262 | u32 factor, max_memsize; |
263 | |
264 | factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1); |
265 | max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048; |
266 | |
267 | if (max_memsize > srcsize - 1) |
268 | factor--; |
269 | |
270 | return factor; |
271 | } |
272 | |
273 | static void |
274 | atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane, |
275 | const u32 *coeff_tab, int size, |
276 | unsigned int cfg_offs) |
277 | { |
278 | int i; |
279 | |
280 | for (i = 0; i < size; i++) |
281 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: cfg_offs + i, |
282 | val: coeff_tab[i]); |
283 | } |
284 | |
285 | static void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, |
286 | struct atmel_hlcdc_plane_state *state) |
287 | { |
288 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
289 | u32 xfactor, yfactor; |
290 | |
291 | if (!desc->layout.scaler_config) |
292 | return; |
293 | |
294 | if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) { |
295 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
296 | cfgid: desc->layout.scaler_config, val: 0); |
297 | return; |
298 | } |
299 | |
300 | if (desc->layout.phicoeffs.x) { |
301 | xfactor = atmel_hlcdc_plane_phiscaler_get_factor(srcsize: state->src_w, |
302 | dstsize: state->crtc_w, |
303 | ATMEL_HLCDC_XPHIDEF); |
304 | |
305 | yfactor = atmel_hlcdc_plane_phiscaler_get_factor(srcsize: state->src_h, |
306 | dstsize: state->crtc_h, |
307 | ATMEL_HLCDC_YPHIDEF); |
308 | |
309 | atmel_hlcdc_plane_scaler_set_phicoeff(plane, |
310 | coeff_tab: state->crtc_w < state->src_w ? |
311 | heo_downscaling_xcoef : |
312 | heo_upscaling_xcoef, |
313 | ARRAY_SIZE(heo_upscaling_xcoef), |
314 | cfg_offs: desc->layout.phicoeffs.x); |
315 | |
316 | atmel_hlcdc_plane_scaler_set_phicoeff(plane, |
317 | coeff_tab: state->crtc_h < state->src_h ? |
318 | heo_downscaling_ycoef : |
319 | heo_upscaling_ycoef, |
320 | ARRAY_SIZE(heo_upscaling_ycoef), |
321 | cfg_offs: desc->layout.phicoeffs.y); |
322 | } else { |
323 | xfactor = (1024 * state->src_w) / state->crtc_w; |
324 | yfactor = (1024 * state->src_h) / state->crtc_h; |
325 | } |
326 | |
327 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: desc->layout.scaler_config, |
328 | ATMEL_HLCDC_LAYER_SCALER_ENABLE | |
329 | ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor, |
330 | yfactor)); |
331 | } |
332 | |
333 | static void |
334 | atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, |
335 | struct atmel_hlcdc_plane_state *state) |
336 | { |
337 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
338 | |
339 | if (desc->layout.size) |
340 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: desc->layout.size, |
341 | ATMEL_HLCDC_LAYER_SIZE(state->crtc_w, |
342 | state->crtc_h)); |
343 | |
344 | if (desc->layout.memsize) |
345 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
346 | cfgid: desc->layout.memsize, |
347 | ATMEL_HLCDC_LAYER_SIZE(state->src_w, |
348 | state->src_h)); |
349 | |
350 | if (desc->layout.pos) |
351 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: desc->layout.pos, |
352 | ATMEL_HLCDC_LAYER_POS(state->crtc_x, |
353 | state->crtc_y)); |
354 | |
355 | atmel_hlcdc_plane_setup_scaler(plane, state); |
356 | } |
357 | |
358 | static void |
359 | atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, |
360 | struct atmel_hlcdc_plane_state *state) |
361 | { |
362 | unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id; |
363 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
364 | const struct drm_format_info *format = state->base.fb->format; |
365 | |
366 | /* |
367 | * Rotation optimization is not working on RGB888 (rotation is still |
368 | * working but without any optimization). |
369 | */ |
370 | if (format->format == DRM_FORMAT_RGB888) |
371 | cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS; |
372 | |
373 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG, |
374 | val: cfg); |
375 | |
376 | cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP; |
377 | |
378 | if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { |
379 | cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | |
380 | ATMEL_HLCDC_LAYER_ITER; |
381 | |
382 | if (format->has_alpha) |
383 | cfg |= ATMEL_HLCDC_LAYER_LAEN; |
384 | else |
385 | cfg |= ATMEL_HLCDC_LAYER_GAEN | |
386 | ATMEL_HLCDC_LAYER_GA(state->base.alpha); |
387 | } |
388 | |
389 | if (state->disc_h && state->disc_w) |
390 | cfg |= ATMEL_HLCDC_LAYER_DISCEN; |
391 | |
392 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: desc->layout.general_config, |
393 | val: cfg); |
394 | } |
395 | |
396 | static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, |
397 | struct atmel_hlcdc_plane_state *state) |
398 | { |
399 | u32 cfg; |
400 | int ret; |
401 | |
402 | ret = atmel_hlcdc_format_to_plane_mode(format: state->base.fb->format->format, |
403 | mode: &cfg); |
404 | if (ret) |
405 | return; |
406 | |
407 | if ((state->base.fb->format->format == DRM_FORMAT_YUV422 || |
408 | state->base.fb->format->format == DRM_FORMAT_NV61) && |
409 | drm_rotation_90_or_270(rotation: state->base.rotation)) |
410 | cfg |= ATMEL_HLCDC_YUV422ROT; |
411 | |
412 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
413 | ATMEL_HLCDC_LAYER_FORMAT_CFG, val: cfg); |
414 | } |
415 | |
416 | static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane, |
417 | struct atmel_hlcdc_plane_state *state) |
418 | { |
419 | struct drm_crtc *crtc = state->base.crtc; |
420 | struct drm_color_lut *lut; |
421 | int idx; |
422 | |
423 | if (!crtc || !crtc->state) |
424 | return; |
425 | |
426 | if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) |
427 | return; |
428 | |
429 | lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; |
430 | |
431 | for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) { |
432 | u32 val = ((lut->red << 8) & 0xff0000) | |
433 | (lut->green & 0xff00) | |
434 | (lut->blue >> 8); |
435 | |
436 | atmel_hlcdc_layer_write_clut(layer: &plane->layer, c: idx, val); |
437 | } |
438 | } |
439 | |
440 | static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, |
441 | struct atmel_hlcdc_plane_state *state) |
442 | { |
443 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
444 | struct drm_framebuffer *fb = state->base.fb; |
445 | u32 sr; |
446 | int i; |
447 | |
448 | sr = atmel_hlcdc_layer_read_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_CHSR); |
449 | |
450 | for (i = 0; i < state->nplanes; i++) { |
451 | struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, plane: i); |
452 | |
453 | state->dscrs[i]->addr = gem->dma_addr + state->offsets[i]; |
454 | |
455 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, |
456 | ATMEL_HLCDC_LAYER_PLANE_HEAD(i), |
457 | val: state->dscrs[i]->self); |
458 | |
459 | if (!(sr & ATMEL_HLCDC_LAYER_EN)) { |
460 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, |
461 | ATMEL_HLCDC_LAYER_PLANE_ADDR(i), |
462 | val: state->dscrs[i]->addr); |
463 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, |
464 | ATMEL_HLCDC_LAYER_PLANE_CTRL(i), |
465 | val: state->dscrs[i]->ctrl); |
466 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, |
467 | ATMEL_HLCDC_LAYER_PLANE_NEXT(i), |
468 | val: state->dscrs[i]->self); |
469 | } |
470 | |
471 | if (desc->layout.xstride[i]) |
472 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
473 | cfgid: desc->layout.xstride[i], |
474 | val: state->xstride[i]); |
475 | |
476 | if (desc->layout.pstride[i]) |
477 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
478 | cfgid: desc->layout.pstride[i], |
479 | val: state->pstride[i]); |
480 | } |
481 | } |
482 | |
483 | int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state) |
484 | { |
485 | unsigned int ahb_load[2] = { }; |
486 | struct drm_plane *plane; |
487 | |
488 | drm_atomic_crtc_state_for_each_plane(plane, c_state) { |
489 | struct atmel_hlcdc_plane_state *plane_state; |
490 | struct drm_plane_state *plane_s; |
491 | unsigned int pixels, load = 0; |
492 | int i; |
493 | |
494 | plane_s = drm_atomic_get_plane_state(state: c_state->state, plane); |
495 | if (IS_ERR(ptr: plane_s)) |
496 | return PTR_ERR(ptr: plane_s); |
497 | |
498 | plane_state = |
499 | drm_plane_state_to_atmel_hlcdc_plane_state(s: plane_s); |
500 | |
501 | pixels = (plane_state->src_w * plane_state->src_h) - |
502 | (plane_state->disc_w * plane_state->disc_h); |
503 | |
504 | for (i = 0; i < plane_state->nplanes; i++) |
505 | load += pixels * plane_state->bpp[i]; |
506 | |
507 | if (ahb_load[0] <= ahb_load[1]) |
508 | plane_state->ahb_id = 0; |
509 | else |
510 | plane_state->ahb_id = 1; |
511 | |
512 | ahb_load[plane_state->ahb_id] += load; |
513 | } |
514 | |
515 | return 0; |
516 | } |
517 | |
518 | int |
519 | atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state) |
520 | { |
521 | int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0; |
522 | const struct atmel_hlcdc_layer_cfg_layout *layout; |
523 | struct atmel_hlcdc_plane_state *primary_state; |
524 | struct drm_plane_state *primary_s; |
525 | struct atmel_hlcdc_plane *primary; |
526 | struct drm_plane *ovl; |
527 | |
528 | primary = drm_plane_to_atmel_hlcdc_plane(p: c_state->crtc->primary); |
529 | layout = &primary->layer.desc->layout; |
530 | if (!layout->disc_pos || !layout->disc_size) |
531 | return 0; |
532 | |
533 | primary_s = drm_atomic_get_plane_state(state: c_state->state, |
534 | plane: &primary->base); |
535 | if (IS_ERR(ptr: primary_s)) |
536 | return PTR_ERR(ptr: primary_s); |
537 | |
538 | primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(s: primary_s); |
539 | |
540 | drm_atomic_crtc_state_for_each_plane(ovl, c_state) { |
541 | struct atmel_hlcdc_plane_state *ovl_state; |
542 | struct drm_plane_state *ovl_s; |
543 | |
544 | if (ovl == c_state->crtc->primary) |
545 | continue; |
546 | |
547 | ovl_s = drm_atomic_get_plane_state(state: c_state->state, plane: ovl); |
548 | if (IS_ERR(ptr: ovl_s)) |
549 | return PTR_ERR(ptr: ovl_s); |
550 | |
551 | ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(s: ovl_s); |
552 | |
553 | if (!ovl_s->visible || |
554 | !ovl_s->fb || |
555 | ovl_s->fb->format->has_alpha || |
556 | ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE) |
557 | continue; |
558 | |
559 | /* TODO: implement a smarter hidden area detection */ |
560 | if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w) |
561 | continue; |
562 | |
563 | disc_x = ovl_state->crtc_x; |
564 | disc_y = ovl_state->crtc_y; |
565 | disc_h = ovl_state->crtc_h; |
566 | disc_w = ovl_state->crtc_w; |
567 | } |
568 | |
569 | primary_state->disc_x = disc_x; |
570 | primary_state->disc_y = disc_y; |
571 | primary_state->disc_w = disc_w; |
572 | primary_state->disc_h = disc_h; |
573 | |
574 | return 0; |
575 | } |
576 | |
577 | static void |
578 | atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane, |
579 | struct atmel_hlcdc_plane_state *state) |
580 | { |
581 | const struct atmel_hlcdc_layer_cfg_layout *layout; |
582 | |
583 | layout = &plane->layer.desc->layout; |
584 | if (!layout->disc_pos || !layout->disc_size) |
585 | return; |
586 | |
587 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: layout->disc_pos, |
588 | ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x, |
589 | state->disc_y)); |
590 | |
591 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, cfgid: layout->disc_size, |
592 | ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w, |
593 | state->disc_h)); |
594 | } |
595 | |
596 | static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, |
597 | struct drm_atomic_state *state) |
598 | { |
599 | struct drm_plane_state *s = drm_atomic_get_new_plane_state(state, plane: p); |
600 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
601 | struct atmel_hlcdc_plane_state *hstate = |
602 | drm_plane_state_to_atmel_hlcdc_plane_state(s); |
603 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
604 | struct drm_framebuffer *fb = hstate->base.fb; |
605 | const struct drm_display_mode *mode; |
606 | struct drm_crtc_state *crtc_state; |
607 | int ret; |
608 | int i; |
609 | |
610 | if (!hstate->base.crtc || WARN_ON(!fb)) |
611 | return 0; |
612 | |
613 | crtc_state = drm_atomic_get_existing_crtc_state(state, crtc: s->crtc); |
614 | mode = &crtc_state->adjusted_mode; |
615 | |
616 | ret = drm_atomic_helper_check_plane_state(plane_state: s, crtc_state, |
617 | min_scale: (1 << 16) / 2048, |
618 | INT_MAX, can_position: true, can_update_disabled: true); |
619 | if (ret || !s->visible) |
620 | return ret; |
621 | |
622 | hstate->src_x = s->src.x1; |
623 | hstate->src_y = s->src.y1; |
624 | hstate->src_w = drm_rect_width(r: &s->src); |
625 | hstate->src_h = drm_rect_height(r: &s->src); |
626 | hstate->crtc_x = s->dst.x1; |
627 | hstate->crtc_y = s->dst.y1; |
628 | hstate->crtc_w = drm_rect_width(r: &s->dst); |
629 | hstate->crtc_h = drm_rect_height(r: &s->dst); |
630 | |
631 | if ((hstate->src_x | hstate->src_y | hstate->src_w | hstate->src_h) & |
632 | SUBPIXEL_MASK) |
633 | return -EINVAL; |
634 | |
635 | hstate->src_x >>= 16; |
636 | hstate->src_y >>= 16; |
637 | hstate->src_w >>= 16; |
638 | hstate->src_h >>= 16; |
639 | |
640 | hstate->nplanes = fb->format->num_planes; |
641 | if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES) |
642 | return -EINVAL; |
643 | |
644 | for (i = 0; i < hstate->nplanes; i++) { |
645 | unsigned int offset = 0; |
646 | int xdiv = i ? fb->format->hsub : 1; |
647 | int ydiv = i ? fb->format->vsub : 1; |
648 | |
649 | hstate->bpp[i] = fb->format->cpp[i]; |
650 | if (!hstate->bpp[i]) |
651 | return -EINVAL; |
652 | |
653 | switch (hstate->base.rotation & DRM_MODE_ROTATE_MASK) { |
654 | case DRM_MODE_ROTATE_90: |
655 | offset = (hstate->src_y / ydiv) * |
656 | fb->pitches[i]; |
657 | offset += ((hstate->src_x + hstate->src_w - 1) / |
658 | xdiv) * hstate->bpp[i]; |
659 | hstate->xstride[i] = -(((hstate->src_h - 1) / ydiv) * |
660 | fb->pitches[i]) - |
661 | (2 * hstate->bpp[i]); |
662 | hstate->pstride[i] = fb->pitches[i] - hstate->bpp[i]; |
663 | break; |
664 | case DRM_MODE_ROTATE_180: |
665 | offset = ((hstate->src_y + hstate->src_h - 1) / |
666 | ydiv) * fb->pitches[i]; |
667 | offset += ((hstate->src_x + hstate->src_w - 1) / |
668 | xdiv) * hstate->bpp[i]; |
669 | hstate->xstride[i] = ((((hstate->src_w - 1) / xdiv) - 1) * |
670 | hstate->bpp[i]) - fb->pitches[i]; |
671 | hstate->pstride[i] = -2 * hstate->bpp[i]; |
672 | break; |
673 | case DRM_MODE_ROTATE_270: |
674 | offset = ((hstate->src_y + hstate->src_h - 1) / |
675 | ydiv) * fb->pitches[i]; |
676 | offset += (hstate->src_x / xdiv) * hstate->bpp[i]; |
677 | hstate->xstride[i] = ((hstate->src_h - 1) / ydiv) * |
678 | fb->pitches[i]; |
679 | hstate->pstride[i] = -fb->pitches[i] - hstate->bpp[i]; |
680 | break; |
681 | case DRM_MODE_ROTATE_0: |
682 | default: |
683 | offset = (hstate->src_y / ydiv) * fb->pitches[i]; |
684 | offset += (hstate->src_x / xdiv) * hstate->bpp[i]; |
685 | hstate->xstride[i] = fb->pitches[i] - |
686 | ((hstate->src_w / xdiv) * |
687 | hstate->bpp[i]); |
688 | hstate->pstride[i] = 0; |
689 | break; |
690 | } |
691 | |
692 | hstate->offsets[i] = offset + fb->offsets[i]; |
693 | } |
694 | |
695 | /* |
696 | * Swap width and size in case of 90 or 270 degrees rotation |
697 | */ |
698 | if (drm_rotation_90_or_270(rotation: hstate->base.rotation)) { |
699 | swap(hstate->src_w, hstate->src_h); |
700 | } |
701 | |
702 | if (!desc->layout.size && |
703 | (mode->hdisplay != hstate->crtc_w || |
704 | mode->vdisplay != hstate->crtc_h)) |
705 | return -EINVAL; |
706 | |
707 | if ((hstate->crtc_h != hstate->src_h || hstate->crtc_w != hstate->src_w) && |
708 | (!desc->layout.memsize || |
709 | hstate->base.fb->format->has_alpha)) |
710 | return -EINVAL; |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, |
716 | struct drm_atomic_state *state) |
717 | { |
718 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
719 | |
720 | /* Disable interrupts */ |
721 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_IDR, |
722 | val: 0xffffffff); |
723 | |
724 | /* Disable the layer */ |
725 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_CHDR, |
726 | ATMEL_HLCDC_LAYER_RST | |
727 | ATMEL_HLCDC_LAYER_A2Q | |
728 | ATMEL_HLCDC_LAYER_UPDATE); |
729 | |
730 | /* Clear all pending interrupts */ |
731 | atmel_hlcdc_layer_read_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_ISR); |
732 | } |
733 | |
734 | static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, |
735 | struct drm_atomic_state *state) |
736 | { |
737 | struct drm_plane_state *new_s = drm_atomic_get_new_plane_state(state, |
738 | plane: p); |
739 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
740 | struct atmel_hlcdc_plane_state *hstate = |
741 | drm_plane_state_to_atmel_hlcdc_plane_state(s: new_s); |
742 | u32 sr; |
743 | |
744 | if (!new_s->crtc || !new_s->fb) |
745 | return; |
746 | |
747 | if (!hstate->base.visible) { |
748 | atmel_hlcdc_plane_atomic_disable(p, state); |
749 | return; |
750 | } |
751 | |
752 | atmel_hlcdc_plane_update_pos_and_size(plane, state: hstate); |
753 | atmel_hlcdc_plane_update_general_settings(plane, state: hstate); |
754 | atmel_hlcdc_plane_update_format(plane, state: hstate); |
755 | atmel_hlcdc_plane_update_clut(plane, state: hstate); |
756 | atmel_hlcdc_plane_update_buffers(plane, state: hstate); |
757 | atmel_hlcdc_plane_update_disc_area(plane, state: hstate); |
758 | |
759 | /* Enable the overrun interrupts. */ |
760 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_IER, |
761 | ATMEL_HLCDC_LAYER_OVR_IRQ(0) | |
762 | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | |
763 | ATMEL_HLCDC_LAYER_OVR_IRQ(2)); |
764 | |
765 | /* Apply the new config at the next SOF event. */ |
766 | sr = atmel_hlcdc_layer_read_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_CHSR); |
767 | atmel_hlcdc_layer_write_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_CHER, |
768 | ATMEL_HLCDC_LAYER_UPDATE | |
769 | (sr & ATMEL_HLCDC_LAYER_EN ? |
770 | ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); |
771 | } |
772 | |
773 | static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane) |
774 | { |
775 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
776 | |
777 | if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || |
778 | desc->type == ATMEL_HLCDC_CURSOR_LAYER) { |
779 | int ret; |
780 | |
781 | ret = drm_plane_create_alpha_property(plane: &plane->base); |
782 | if (ret) |
783 | return ret; |
784 | } |
785 | |
786 | if (desc->layout.xstride[0] && desc->layout.pstride[0]) { |
787 | int ret; |
788 | |
789 | ret = drm_plane_create_rotation_property(plane: &plane->base, |
790 | DRM_MODE_ROTATE_0, |
791 | DRM_MODE_ROTATE_0 | |
792 | DRM_MODE_ROTATE_90 | |
793 | DRM_MODE_ROTATE_180 | |
794 | DRM_MODE_ROTATE_270); |
795 | if (ret) |
796 | return ret; |
797 | } |
798 | |
799 | if (desc->layout.csc) { |
800 | /* |
801 | * TODO: decare a "yuv-to-rgb-conv-factors" property to let |
802 | * userspace modify these factors (using a BLOB property ?). |
803 | */ |
804 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
805 | cfgid: desc->layout.csc, |
806 | val: 0x4c900091); |
807 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
808 | cfgid: desc->layout.csc + 1, |
809 | val: 0x7a5f5090); |
810 | atmel_hlcdc_layer_write_cfg(layer: &plane->layer, |
811 | cfgid: desc->layout.csc + 2, |
812 | val: 0x40040890); |
813 | } |
814 | |
815 | return 0; |
816 | } |
817 | |
818 | void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane) |
819 | { |
820 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
821 | u32 isr; |
822 | |
823 | isr = atmel_hlcdc_layer_read_reg(layer: &plane->layer, ATMEL_HLCDC_LAYER_ISR); |
824 | |
825 | /* |
826 | * There's not much we can do in case of overrun except informing |
827 | * the user. However, we are in interrupt context here, hence the |
828 | * use of dev_dbg(). |
829 | */ |
830 | if (isr & |
831 | (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | |
832 | ATMEL_HLCDC_LAYER_OVR_IRQ(2))) |
833 | dev_dbg(plane->base.dev->dev, "overrun on plane %s\n" , |
834 | desc->name); |
835 | } |
836 | |
837 | static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { |
838 | .atomic_check = atmel_hlcdc_plane_atomic_check, |
839 | .atomic_update = atmel_hlcdc_plane_atomic_update, |
840 | .atomic_disable = atmel_hlcdc_plane_atomic_disable, |
841 | }; |
842 | |
843 | static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p, |
844 | struct atmel_hlcdc_plane_state *state) |
845 | { |
846 | struct atmel_hlcdc_dc *dc = p->dev->dev_private; |
847 | int i; |
848 | |
849 | for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { |
850 | struct atmel_hlcdc_dma_channel_dscr *dscr; |
851 | dma_addr_t dscr_dma; |
852 | |
853 | dscr = dma_pool_alloc(pool: dc->dscrpool, GFP_KERNEL, handle: &dscr_dma); |
854 | if (!dscr) |
855 | goto err; |
856 | |
857 | dscr->addr = 0; |
858 | dscr->next = dscr_dma; |
859 | dscr->self = dscr_dma; |
860 | dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH; |
861 | |
862 | state->dscrs[i] = dscr; |
863 | } |
864 | |
865 | return 0; |
866 | |
867 | err: |
868 | for (i--; i >= 0; i--) { |
869 | dma_pool_free(pool: dc->dscrpool, vaddr: state->dscrs[i], |
870 | addr: state->dscrs[i]->self); |
871 | } |
872 | |
873 | return -ENOMEM; |
874 | } |
875 | |
876 | static void atmel_hlcdc_plane_reset(struct drm_plane *p) |
877 | { |
878 | struct atmel_hlcdc_plane_state *state; |
879 | |
880 | if (p->state) { |
881 | state = drm_plane_state_to_atmel_hlcdc_plane_state(s: p->state); |
882 | |
883 | if (state->base.fb) |
884 | drm_framebuffer_put(fb: state->base.fb); |
885 | |
886 | kfree(objp: state); |
887 | p->state = NULL; |
888 | } |
889 | |
890 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
891 | if (state) { |
892 | if (atmel_hlcdc_plane_alloc_dscrs(p, state)) { |
893 | kfree(objp: state); |
894 | dev_err(p->dev->dev, |
895 | "Failed to allocate initial plane state\n" ); |
896 | return; |
897 | } |
898 | __drm_atomic_helper_plane_reset(plane: p, state: &state->base); |
899 | } |
900 | } |
901 | |
902 | static struct drm_plane_state * |
903 | atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) |
904 | { |
905 | struct atmel_hlcdc_plane_state *state = |
906 | drm_plane_state_to_atmel_hlcdc_plane_state(s: p->state); |
907 | struct atmel_hlcdc_plane_state *copy; |
908 | |
909 | copy = kmemdup(p: state, size: sizeof(*state), GFP_KERNEL); |
910 | if (!copy) |
911 | return NULL; |
912 | |
913 | if (atmel_hlcdc_plane_alloc_dscrs(p, state: copy)) { |
914 | kfree(objp: copy); |
915 | return NULL; |
916 | } |
917 | |
918 | if (copy->base.fb) |
919 | drm_framebuffer_get(fb: copy->base.fb); |
920 | |
921 | return ©->base; |
922 | } |
923 | |
924 | static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p, |
925 | struct drm_plane_state *s) |
926 | { |
927 | struct atmel_hlcdc_plane_state *state = |
928 | drm_plane_state_to_atmel_hlcdc_plane_state(s); |
929 | struct atmel_hlcdc_dc *dc = p->dev->dev_private; |
930 | int i; |
931 | |
932 | for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { |
933 | dma_pool_free(pool: dc->dscrpool, vaddr: state->dscrs[i], |
934 | addr: state->dscrs[i]->self); |
935 | } |
936 | |
937 | if (s->fb) |
938 | drm_framebuffer_put(fb: s->fb); |
939 | |
940 | kfree(objp: state); |
941 | } |
942 | |
943 | static const struct drm_plane_funcs layer_plane_funcs = { |
944 | .update_plane = drm_atomic_helper_update_plane, |
945 | .disable_plane = drm_atomic_helper_disable_plane, |
946 | .destroy = drm_plane_cleanup, |
947 | .reset = atmel_hlcdc_plane_reset, |
948 | .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state, |
949 | .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state, |
950 | }; |
951 | |
952 | static int atmel_hlcdc_plane_create(struct drm_device *dev, |
953 | const struct atmel_hlcdc_layer_desc *desc) |
954 | { |
955 | struct atmel_hlcdc_dc *dc = dev->dev_private; |
956 | struct atmel_hlcdc_plane *plane; |
957 | enum drm_plane_type type; |
958 | int ret; |
959 | |
960 | plane = devm_kzalloc(dev: dev->dev, size: sizeof(*plane), GFP_KERNEL); |
961 | if (!plane) |
962 | return -ENOMEM; |
963 | |
964 | atmel_hlcdc_layer_init(layer: &plane->layer, desc, regmap: dc->hlcdc->regmap); |
965 | |
966 | if (desc->type == ATMEL_HLCDC_BASE_LAYER) |
967 | type = DRM_PLANE_TYPE_PRIMARY; |
968 | else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER) |
969 | type = DRM_PLANE_TYPE_CURSOR; |
970 | else |
971 | type = DRM_PLANE_TYPE_OVERLAY; |
972 | |
973 | ret = drm_universal_plane_init(dev, plane: &plane->base, possible_crtcs: 0, |
974 | funcs: &layer_plane_funcs, |
975 | formats: desc->formats->formats, |
976 | format_count: desc->formats->nformats, |
977 | NULL, type, NULL); |
978 | if (ret) |
979 | return ret; |
980 | |
981 | drm_plane_helper_add(plane: &plane->base, |
982 | funcs: &atmel_hlcdc_layer_plane_helper_funcs); |
983 | |
984 | /* Set default property values*/ |
985 | ret = atmel_hlcdc_plane_init_properties(plane); |
986 | if (ret) |
987 | return ret; |
988 | |
989 | dc->layers[desc->id] = &plane->layer; |
990 | |
991 | return 0; |
992 | } |
993 | |
994 | int atmel_hlcdc_create_planes(struct drm_device *dev) |
995 | { |
996 | struct atmel_hlcdc_dc *dc = dev->dev_private; |
997 | const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; |
998 | int nlayers = dc->desc->nlayers; |
999 | int i, ret; |
1000 | |
1001 | dc->dscrpool = dmam_pool_create(name: "atmel-hlcdc-dscr" , dev: dev->dev, |
1002 | size: sizeof(struct atmel_hlcdc_dma_channel_dscr), |
1003 | align: sizeof(u64), allocation: 0); |
1004 | if (!dc->dscrpool) |
1005 | return -ENOMEM; |
1006 | |
1007 | for (i = 0; i < nlayers; i++) { |
1008 | if (descs[i].type != ATMEL_HLCDC_BASE_LAYER && |
1009 | descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER && |
1010 | descs[i].type != ATMEL_HLCDC_CURSOR_LAYER) |
1011 | continue; |
1012 | |
1013 | ret = atmel_hlcdc_plane_create(dev, desc: &descs[i]); |
1014 | if (ret) |
1015 | return ret; |
1016 | } |
1017 | |
1018 | return 0; |
1019 | } |
1020 | |