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) | |
1a7b37ca | 40 | * @prepared: plane update has been prepared |
2389fc13 BB |
41 | */ |
42 | struct atmel_hlcdc_plane_state { | |
43 | struct drm_plane_state base; | |
44 | int crtc_x; | |
45 | int crtc_y; | |
46 | unsigned int crtc_w; | |
47 | unsigned int crtc_h; | |
48 | uint32_t src_x; | |
49 | uint32_t src_y; | |
50 | uint32_t src_w; | |
51 | uint32_t src_h; | |
52 | ||
53 | u8 alpha; | |
54 | ||
5957017d BB |
55 | bool disc_updated; |
56 | ||
57 | int disc_x; | |
58 | int disc_y; | |
59 | int disc_w; | |
60 | int disc_h; | |
61 | ||
2389fc13 BB |
62 | /* These fields are private and should not be touched */ |
63 | int bpp[ATMEL_HLCDC_MAX_PLANES]; | |
64 | unsigned int offsets[ATMEL_HLCDC_MAX_PLANES]; | |
65 | int xstride[ATMEL_HLCDC_MAX_PLANES]; | |
66 | int pstride[ATMEL_HLCDC_MAX_PLANES]; | |
67 | int nplanes; | |
1a7b37ca | 68 | bool prepared; |
2389fc13 BB |
69 | }; |
70 | ||
71 | static inline struct atmel_hlcdc_plane_state * | |
72 | drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s) | |
73 | { | |
74 | return container_of(s, struct atmel_hlcdc_plane_state, base); | |
75 | } | |
76 | ||
1a396789 BB |
77 | #define SUBPIXEL_MASK 0xffff |
78 | ||
79 | static uint32_t rgb_formats[] = { | |
80 | DRM_FORMAT_XRGB4444, | |
81 | DRM_FORMAT_ARGB4444, | |
82 | DRM_FORMAT_RGBA4444, | |
83 | DRM_FORMAT_ARGB1555, | |
84 | DRM_FORMAT_RGB565, | |
85 | DRM_FORMAT_RGB888, | |
86 | DRM_FORMAT_XRGB8888, | |
87 | DRM_FORMAT_ARGB8888, | |
88 | DRM_FORMAT_RGBA8888, | |
89 | }; | |
90 | ||
91 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = { | |
92 | .formats = rgb_formats, | |
93 | .nformats = ARRAY_SIZE(rgb_formats), | |
94 | }; | |
95 | ||
96 | static uint32_t rgb_and_yuv_formats[] = { | |
97 | DRM_FORMAT_XRGB4444, | |
98 | DRM_FORMAT_ARGB4444, | |
99 | DRM_FORMAT_RGBA4444, | |
100 | DRM_FORMAT_ARGB1555, | |
101 | DRM_FORMAT_RGB565, | |
102 | DRM_FORMAT_RGB888, | |
103 | DRM_FORMAT_XRGB8888, | |
104 | DRM_FORMAT_ARGB8888, | |
105 | DRM_FORMAT_RGBA8888, | |
106 | DRM_FORMAT_AYUV, | |
107 | DRM_FORMAT_YUYV, | |
108 | DRM_FORMAT_UYVY, | |
109 | DRM_FORMAT_YVYU, | |
110 | DRM_FORMAT_VYUY, | |
111 | DRM_FORMAT_NV21, | |
112 | DRM_FORMAT_NV61, | |
113 | DRM_FORMAT_YUV422, | |
114 | DRM_FORMAT_YUV420, | |
115 | }; | |
116 | ||
117 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = { | |
118 | .formats = rgb_and_yuv_formats, | |
119 | .nformats = ARRAY_SIZE(rgb_and_yuv_formats), | |
120 | }; | |
121 | ||
122 | static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) | |
123 | { | |
124 | switch (format) { | |
125 | case DRM_FORMAT_XRGB4444: | |
126 | *mode = ATMEL_HLCDC_XRGB4444_MODE; | |
127 | break; | |
128 | case DRM_FORMAT_ARGB4444: | |
129 | *mode = ATMEL_HLCDC_ARGB4444_MODE; | |
130 | break; | |
131 | case DRM_FORMAT_RGBA4444: | |
132 | *mode = ATMEL_HLCDC_RGBA4444_MODE; | |
133 | break; | |
134 | case DRM_FORMAT_RGB565: | |
135 | *mode = ATMEL_HLCDC_RGB565_MODE; | |
136 | break; | |
137 | case DRM_FORMAT_RGB888: | |
138 | *mode = ATMEL_HLCDC_RGB888_MODE; | |
139 | break; | |
140 | case DRM_FORMAT_ARGB1555: | |
141 | *mode = ATMEL_HLCDC_ARGB1555_MODE; | |
142 | break; | |
143 | case DRM_FORMAT_XRGB8888: | |
144 | *mode = ATMEL_HLCDC_XRGB8888_MODE; | |
145 | break; | |
146 | case DRM_FORMAT_ARGB8888: | |
147 | *mode = ATMEL_HLCDC_ARGB8888_MODE; | |
148 | break; | |
149 | case DRM_FORMAT_RGBA8888: | |
150 | *mode = ATMEL_HLCDC_RGBA8888_MODE; | |
151 | break; | |
152 | case DRM_FORMAT_AYUV: | |
153 | *mode = ATMEL_HLCDC_AYUV_MODE; | |
154 | break; | |
155 | case DRM_FORMAT_YUYV: | |
156 | *mode = ATMEL_HLCDC_YUYV_MODE; | |
157 | break; | |
158 | case DRM_FORMAT_UYVY: | |
159 | *mode = ATMEL_HLCDC_UYVY_MODE; | |
160 | break; | |
161 | case DRM_FORMAT_YVYU: | |
162 | *mode = ATMEL_HLCDC_YVYU_MODE; | |
163 | break; | |
164 | case DRM_FORMAT_VYUY: | |
165 | *mode = ATMEL_HLCDC_VYUY_MODE; | |
166 | break; | |
167 | case DRM_FORMAT_NV21: | |
168 | *mode = ATMEL_HLCDC_NV21_MODE; | |
169 | break; | |
170 | case DRM_FORMAT_NV61: | |
171 | *mode = ATMEL_HLCDC_NV61_MODE; | |
172 | break; | |
173 | case DRM_FORMAT_YUV420: | |
174 | *mode = ATMEL_HLCDC_YUV420_MODE; | |
175 | break; | |
176 | case DRM_FORMAT_YUV422: | |
177 | *mode = ATMEL_HLCDC_YUV422_MODE; | |
178 | break; | |
179 | default: | |
180 | return -ENOTSUPP; | |
181 | } | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
2389fc13 | 186 | static bool atmel_hlcdc_format_embeds_alpha(u32 format) |
1a396789 BB |
187 | { |
188 | int i; | |
189 | ||
190 | for (i = 0; i < sizeof(format); i++) { | |
191 | char tmp = (format >> (8 * i)) & 0xff; | |
192 | ||
193 | if (tmp == 'A') | |
194 | return true; | |
195 | } | |
196 | ||
197 | return false; | |
198 | } | |
199 | ||
200 | static u32 heo_downscaling_xcoef[] = { | |
201 | 0x11343311, | |
202 | 0x000000f7, | |
203 | 0x1635300c, | |
204 | 0x000000f9, | |
205 | 0x1b362c08, | |
206 | 0x000000fb, | |
207 | 0x1f372804, | |
208 | 0x000000fe, | |
209 | 0x24382400, | |
210 | 0x00000000, | |
211 | 0x28371ffe, | |
212 | 0x00000004, | |
213 | 0x2c361bfb, | |
214 | 0x00000008, | |
215 | 0x303516f9, | |
216 | 0x0000000c, | |
217 | }; | |
218 | ||
219 | static u32 heo_downscaling_ycoef[] = { | |
220 | 0x00123737, | |
221 | 0x00173732, | |
222 | 0x001b382d, | |
223 | 0x001f3928, | |
224 | 0x00243824, | |
225 | 0x0028391f, | |
226 | 0x002d381b, | |
227 | 0x00323717, | |
228 | }; | |
229 | ||
230 | static u32 heo_upscaling_xcoef[] = { | |
231 | 0xf74949f7, | |
232 | 0x00000000, | |
233 | 0xf55f33fb, | |
234 | 0x000000fe, | |
235 | 0xf5701efe, | |
236 | 0x000000ff, | |
237 | 0xf87c0dff, | |
238 | 0x00000000, | |
239 | 0x00800000, | |
240 | 0x00000000, | |
241 | 0x0d7cf800, | |
242 | 0x000000ff, | |
243 | 0x1e70f5ff, | |
244 | 0x000000fe, | |
245 | 0x335ff5fe, | |
246 | 0x000000fb, | |
247 | }; | |
248 | ||
249 | static u32 heo_upscaling_ycoef[] = { | |
250 | 0x00004040, | |
251 | 0x00075920, | |
252 | 0x00056f0c, | |
253 | 0x00027b03, | |
254 | 0x00008000, | |
255 | 0x00037b02, | |
256 | 0x000c6f05, | |
257 | 0x00205907, | |
258 | }; | |
259 | ||
260 | static void | |
261 | atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 262 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
263 | { |
264 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
265 | &plane->layer.desc->layout; | |
266 | ||
267 | if (layout->size) | |
268 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
269 | layout->size, | |
270 | 0xffffffff, | |
2389fc13 BB |
271 | (state->crtc_w - 1) | |
272 | ((state->crtc_h - 1) << 16)); | |
1a396789 BB |
273 | |
274 | if (layout->memsize) | |
275 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
276 | layout->memsize, | |
277 | 0xffffffff, | |
2389fc13 BB |
278 | (state->src_w - 1) | |
279 | ((state->src_h - 1) << 16)); | |
1a396789 BB |
280 | |
281 | if (layout->pos) | |
282 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
283 | layout->pos, | |
284 | 0xffffffff, | |
2389fc13 BB |
285 | state->crtc_x | |
286 | (state->crtc_y << 16)); | |
1a396789 BB |
287 | |
288 | /* TODO: rework the rescaling part */ | |
2389fc13 | 289 | if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) { |
1a396789 BB |
290 | u32 factor_reg = 0; |
291 | ||
2389fc13 | 292 | if (state->crtc_w != state->src_w) { |
1a396789 BB |
293 | int i; |
294 | u32 factor; | |
295 | u32 *coeff_tab = heo_upscaling_xcoef; | |
296 | u32 max_memsize; | |
297 | ||
2389fc13 | 298 | if (state->crtc_w < state->src_w) |
1a396789 BB |
299 | coeff_tab = heo_downscaling_xcoef; |
300 | for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++) | |
301 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
302 | 17 + i, | |
303 | 0xffffffff, | |
304 | coeff_tab[i]); | |
2389fc13 BB |
305 | factor = ((8 * 256 * state->src_w) - (256 * 4)) / |
306 | state->crtc_w; | |
1a396789 | 307 | factor++; |
2389fc13 | 308 | max_memsize = ((factor * state->crtc_w) + (256 * 4)) / |
1a396789 | 309 | 2048; |
2389fc13 | 310 | if (max_memsize > state->src_w) |
1a396789 BB |
311 | factor--; |
312 | factor_reg |= factor | 0x80000000; | |
313 | } | |
314 | ||
2389fc13 | 315 | if (state->crtc_h != state->src_h) { |
1a396789 BB |
316 | int i; |
317 | u32 factor; | |
318 | u32 *coeff_tab = heo_upscaling_ycoef; | |
319 | u32 max_memsize; | |
320 | ||
2389fc13 | 321 | if (state->crtc_w < state->src_w) |
1a396789 BB |
322 | coeff_tab = heo_downscaling_ycoef; |
323 | for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++) | |
324 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
325 | 33 + i, | |
326 | 0xffffffff, | |
327 | coeff_tab[i]); | |
2389fc13 BB |
328 | factor = ((8 * 256 * state->src_w) - (256 * 4)) / |
329 | state->crtc_w; | |
1a396789 | 330 | factor++; |
2389fc13 | 331 | max_memsize = ((factor * state->crtc_w) + (256 * 4)) / |
1a396789 | 332 | 2048; |
2389fc13 | 333 | if (max_memsize > state->src_w) |
1a396789 BB |
334 | factor--; |
335 | factor_reg |= (factor << 16) | 0x80000000; | |
336 | } | |
337 | ||
338 | atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, | |
339 | factor_reg); | |
340 | } | |
341 | } | |
342 | ||
343 | static void | |
344 | atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 345 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
346 | { |
347 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
348 | &plane->layer.desc->layout; | |
349 | unsigned int cfg = ATMEL_HLCDC_LAYER_DMA; | |
350 | ||
351 | if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { | |
352 | cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | | |
353 | ATMEL_HLCDC_LAYER_ITER; | |
354 | ||
2389fc13 | 355 | if (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)) |
1a396789 BB |
356 | cfg |= ATMEL_HLCDC_LAYER_LAEN; |
357 | else | |
2389fc13 BB |
358 | cfg |= ATMEL_HLCDC_LAYER_GAEN | |
359 | ATMEL_HLCDC_LAYER_GA(state->alpha); | |
1a396789 BB |
360 | } |
361 | ||
362 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
363 | ATMEL_HLCDC_LAYER_DMA_CFG_ID, | |
364 | ATMEL_HLCDC_LAYER_DMA_BLEN_MASK, | |
365 | ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16); | |
366 | ||
367 | atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config, | |
368 | ATMEL_HLCDC_LAYER_ITER2BL | | |
369 | ATMEL_HLCDC_LAYER_ITER | | |
370 | ATMEL_HLCDC_LAYER_GAEN | | |
2389fc13 | 371 | ATMEL_HLCDC_LAYER_GA_MASK | |
1a396789 BB |
372 | ATMEL_HLCDC_LAYER_LAEN | |
373 | ATMEL_HLCDC_LAYER_OVR | | |
374 | ATMEL_HLCDC_LAYER_DMA, cfg); | |
375 | } | |
376 | ||
377 | static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 378 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
379 | { |
380 | u32 cfg; | |
381 | int ret; | |
382 | ||
2389fc13 BB |
383 | ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->pixel_format, |
384 | &cfg); | |
1a396789 BB |
385 | if (ret) |
386 | return; | |
387 | ||
2389fc13 BB |
388 | if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 || |
389 | state->base.fb->pixel_format == DRM_FORMAT_NV61) && | |
390 | (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)))) | |
1a396789 BB |
391 | cfg |= ATMEL_HLCDC_YUV422ROT; |
392 | ||
393 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
394 | ATMEL_HLCDC_LAYER_FORMAT_CFG_ID, | |
395 | 0xffffffff, | |
396 | cfg); | |
397 | ||
398 | /* | |
399 | * Rotation optimization is not working on RGB888 (rotation is still | |
400 | * working but without any optimization). | |
401 | */ | |
2389fc13 | 402 | if (state->base.fb->pixel_format == DRM_FORMAT_RGB888) |
1a396789 BB |
403 | cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS; |
404 | else | |
405 | cfg = 0; | |
406 | ||
407 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
408 | ATMEL_HLCDC_LAYER_DMA_CFG_ID, | |
409 | ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg); | |
410 | } | |
411 | ||
412 | static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 413 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
414 | { |
415 | struct atmel_hlcdc_layer *layer = &plane->layer; | |
416 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
417 | &layer->desc->layout; | |
418 | int i; | |
419 | ||
2389fc13 BB |
420 | atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb, |
421 | state->offsets); | |
1a396789 | 422 | |
2389fc13 | 423 | for (i = 0; i < state->nplanes; i++) { |
1a396789 BB |
424 | if (layout->xstride[i]) { |
425 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
426 | layout->xstride[i], | |
427 | 0xffffffff, | |
2389fc13 | 428 | state->xstride[i]); |
1a396789 BB |
429 | } |
430 | ||
431 | if (layout->pstride[i]) { | |
432 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
433 | layout->pstride[i], | |
434 | 0xffffffff, | |
2389fc13 | 435 | state->pstride[i]); |
1a396789 BB |
436 | } |
437 | } | |
438 | } | |
439 | ||
5957017d BB |
440 | int |
441 | atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state) | |
442 | { | |
443 | int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0; | |
444 | const struct atmel_hlcdc_layer_cfg_layout *layout; | |
445 | struct atmel_hlcdc_plane_state *primary_state; | |
446 | struct drm_plane_state *primary_s; | |
447 | struct atmel_hlcdc_plane *primary; | |
448 | struct drm_plane *ovl; | |
449 | ||
450 | primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary); | |
451 | layout = &primary->layer.desc->layout; | |
452 | if (!layout->disc_pos || !layout->disc_size) | |
453 | return 0; | |
454 | ||
455 | primary_s = drm_atomic_get_plane_state(c_state->state, | |
456 | &primary->base); | |
457 | if (IS_ERR(primary_s)) | |
458 | return PTR_ERR(primary_s); | |
459 | ||
460 | primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s); | |
461 | ||
462 | drm_atomic_crtc_state_for_each_plane(ovl, c_state) { | |
463 | struct atmel_hlcdc_plane_state *ovl_state; | |
464 | struct drm_plane_state *ovl_s; | |
465 | ||
466 | if (ovl == c_state->crtc->primary) | |
467 | continue; | |
468 | ||
469 | ovl_s = drm_atomic_get_plane_state(c_state->state, ovl); | |
470 | if (IS_ERR(ovl_s)) | |
471 | return PTR_ERR(ovl_s); | |
472 | ||
473 | ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s); | |
474 | ||
475 | if (!ovl_s->fb || | |
476 | atmel_hlcdc_format_embeds_alpha(ovl_s->fb->pixel_format) || | |
477 | ovl_state->alpha != 255) | |
478 | continue; | |
479 | ||
480 | /* TODO: implement a smarter hidden area detection */ | |
481 | if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w) | |
482 | continue; | |
483 | ||
484 | disc_x = ovl_state->crtc_x; | |
485 | disc_y = ovl_state->crtc_y; | |
486 | disc_h = ovl_state->crtc_h; | |
487 | disc_w = ovl_state->crtc_w; | |
488 | } | |
489 | ||
490 | if (disc_x == primary_state->disc_x && | |
491 | disc_y == primary_state->disc_y && | |
492 | disc_w == primary_state->disc_w && | |
493 | disc_h == primary_state->disc_h) | |
494 | return 0; | |
495 | ||
496 | ||
497 | primary_state->disc_x = disc_x; | |
498 | primary_state->disc_y = disc_y; | |
499 | primary_state->disc_w = disc_w; | |
500 | primary_state->disc_h = disc_h; | |
501 | primary_state->disc_updated = true; | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | static void | |
507 | atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane, | |
508 | struct atmel_hlcdc_plane_state *state) | |
509 | { | |
510 | const struct atmel_hlcdc_layer_cfg_layout *layout = | |
511 | &plane->layer.desc->layout; | |
512 | int disc_surface = 0; | |
513 | ||
514 | if (!state->disc_updated) | |
515 | return; | |
516 | ||
517 | disc_surface = state->disc_h * state->disc_w; | |
518 | ||
519 | atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config, | |
520 | ATMEL_HLCDC_LAYER_DISCEN, | |
521 | disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0); | |
522 | ||
523 | if (!disc_surface) | |
524 | return; | |
525 | ||
526 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
527 | layout->disc_pos, | |
528 | 0xffffffff, | |
529 | state->disc_x | (state->disc_y << 16)); | |
530 | ||
531 | atmel_hlcdc_layer_update_cfg(&plane->layer, | |
532 | layout->disc_size, | |
533 | 0xffffffff, | |
534 | (state->disc_w - 1) | | |
535 | ((state->disc_h - 1) << 16)); | |
536 | } | |
537 | ||
2389fc13 BB |
538 | static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, |
539 | struct drm_plane_state *s) | |
1a396789 BB |
540 | { |
541 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
2389fc13 BB |
542 | struct atmel_hlcdc_plane_state *state = |
543 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
1a396789 BB |
544 | const struct atmel_hlcdc_layer_cfg_layout *layout = |
545 | &plane->layer.desc->layout; | |
2389fc13 BB |
546 | struct drm_framebuffer *fb = state->base.fb; |
547 | const struct drm_display_mode *mode; | |
548 | struct drm_crtc_state *crtc_state; | |
1a396789 BB |
549 | unsigned int patched_crtc_w; |
550 | unsigned int patched_crtc_h; | |
551 | unsigned int patched_src_w; | |
552 | unsigned int patched_src_h; | |
553 | unsigned int tmp; | |
554 | int x_offset = 0; | |
555 | int y_offset = 0; | |
556 | int hsub = 1; | |
557 | int vsub = 1; | |
558 | int i; | |
559 | ||
2389fc13 BB |
560 | if (!state->base.crtc || !fb) |
561 | return 0; | |
562 | ||
b47ff7e6 | 563 | crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc); |
2389fc13 BB |
564 | mode = &crtc_state->adjusted_mode; |
565 | ||
566 | state->src_x = s->src_x; | |
567 | state->src_y = s->src_y; | |
568 | state->src_h = s->src_h; | |
569 | state->src_w = s->src_w; | |
570 | state->crtc_x = s->crtc_x; | |
571 | state->crtc_y = s->crtc_y; | |
572 | state->crtc_h = s->crtc_h; | |
573 | state->crtc_w = s->crtc_w; | |
574 | if ((state->src_x | state->src_y | state->src_w | state->src_h) & | |
1a396789 BB |
575 | SUBPIXEL_MASK) |
576 | return -EINVAL; | |
577 | ||
2389fc13 BB |
578 | state->src_x >>= 16; |
579 | state->src_y >>= 16; | |
580 | state->src_w >>= 16; | |
581 | state->src_h >>= 16; | |
1a396789 | 582 | |
2389fc13 BB |
583 | state->nplanes = drm_format_num_planes(fb->pixel_format); |
584 | if (state->nplanes > ATMEL_HLCDC_MAX_PLANES) | |
1a396789 BB |
585 | return -EINVAL; |
586 | ||
587 | /* | |
588 | * Swap width and size in case of 90 or 270 degrees rotation | |
589 | */ | |
2389fc13 BB |
590 | if (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { |
591 | tmp = state->crtc_w; | |
592 | state->crtc_w = state->crtc_h; | |
593 | state->crtc_h = tmp; | |
594 | tmp = state->src_w; | |
595 | state->src_w = state->src_h; | |
596 | state->src_h = tmp; | |
1a396789 BB |
597 | } |
598 | ||
2389fc13 BB |
599 | if (state->crtc_x + state->crtc_w > mode->hdisplay) |
600 | patched_crtc_w = mode->hdisplay - state->crtc_x; | |
1a396789 | 601 | else |
2389fc13 | 602 | patched_crtc_w = state->crtc_w; |
1a396789 | 603 | |
2389fc13 BB |
604 | if (state->crtc_x < 0) { |
605 | patched_crtc_w += state->crtc_x; | |
606 | x_offset = -state->crtc_x; | |
607 | state->crtc_x = 0; | |
1a396789 BB |
608 | } |
609 | ||
2389fc13 BB |
610 | if (state->crtc_y + state->crtc_h > mode->vdisplay) |
611 | patched_crtc_h = mode->vdisplay - state->crtc_y; | |
1a396789 | 612 | else |
2389fc13 | 613 | patched_crtc_h = state->crtc_h; |
1a396789 | 614 | |
2389fc13 BB |
615 | if (state->crtc_y < 0) { |
616 | patched_crtc_h += state->crtc_y; | |
617 | y_offset = -state->crtc_y; | |
618 | state->crtc_y = 0; | |
1a396789 BB |
619 | } |
620 | ||
2389fc13 BB |
621 | patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w, |
622 | state->crtc_w); | |
623 | patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h, | |
624 | state->crtc_h); | |
1a396789 | 625 | |
2389fc13 BB |
626 | hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); |
627 | vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); | |
1a396789 | 628 | |
2389fc13 | 629 | for (i = 0; i < state->nplanes; i++) { |
1a396789 BB |
630 | unsigned int offset = 0; |
631 | int xdiv = i ? hsub : 1; | |
632 | int ydiv = i ? vsub : 1; | |
633 | ||
2389fc13 BB |
634 | state->bpp[i] = drm_format_plane_cpp(fb->pixel_format, i); |
635 | if (!state->bpp[i]) | |
1a396789 BB |
636 | return -EINVAL; |
637 | ||
14152c8d | 638 | switch (state->base.rotation & DRM_ROTATE_MASK) { |
1a396789 | 639 | case BIT(DRM_ROTATE_90): |
2389fc13 BB |
640 | offset = ((y_offset + state->src_y + patched_src_w - 1) / |
641 | ydiv) * fb->pitches[i]; | |
642 | offset += ((x_offset + state->src_x) / xdiv) * | |
643 | state->bpp[i]; | |
644 | state->xstride[i] = ((patched_src_w - 1) / ydiv) * | |
645 | fb->pitches[i]; | |
646 | state->pstride[i] = -fb->pitches[i] - state->bpp[i]; | |
1a396789 BB |
647 | break; |
648 | case BIT(DRM_ROTATE_180): | |
2389fc13 BB |
649 | offset = ((y_offset + state->src_y + patched_src_h - 1) / |
650 | ydiv) * fb->pitches[i]; | |
651 | offset += ((x_offset + state->src_x + patched_src_w - 1) / | |
652 | xdiv) * state->bpp[i]; | |
653 | state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) * | |
654 | state->bpp[i]) - fb->pitches[i]; | |
655 | state->pstride[i] = -2 * state->bpp[i]; | |
1a396789 BB |
656 | break; |
657 | case BIT(DRM_ROTATE_270): | |
2389fc13 BB |
658 | offset = ((y_offset + state->src_y) / ydiv) * |
659 | fb->pitches[i]; | |
660 | offset += ((x_offset + state->src_x + patched_src_h - 1) / | |
661 | xdiv) * state->bpp[i]; | |
662 | state->xstride[i] = -(((patched_src_w - 1) / ydiv) * | |
663 | fb->pitches[i]) - | |
664 | (2 * state->bpp[i]); | |
665 | state->pstride[i] = fb->pitches[i] - state->bpp[i]; | |
1a396789 BB |
666 | break; |
667 | case BIT(DRM_ROTATE_0): | |
668 | default: | |
2389fc13 BB |
669 | offset = ((y_offset + state->src_y) / ydiv) * |
670 | fb->pitches[i]; | |
671 | offset += ((x_offset + state->src_x) / xdiv) * | |
672 | state->bpp[i]; | |
673 | state->xstride[i] = fb->pitches[i] - | |
1a396789 | 674 | ((patched_src_w / xdiv) * |
2389fc13 BB |
675 | state->bpp[i]); |
676 | state->pstride[i] = 0; | |
1a396789 BB |
677 | break; |
678 | } | |
679 | ||
2389fc13 | 680 | state->offsets[i] = offset + fb->offsets[i]; |
1a396789 BB |
681 | } |
682 | ||
2389fc13 BB |
683 | state->src_w = patched_src_w; |
684 | state->src_h = patched_src_h; | |
685 | state->crtc_w = patched_crtc_w; | |
686 | state->crtc_h = patched_crtc_h; | |
1a396789 | 687 | |
2389fc13 BB |
688 | if (!layout->size && |
689 | (mode->hdisplay != state->crtc_w || | |
690 | mode->vdisplay != state->crtc_h)) | |
691 | return -EINVAL; | |
1a396789 | 692 | |
2389fc13 BB |
693 | if (plane->layer.desc->max_height && |
694 | state->crtc_h > plane->layer.desc->max_height) | |
695 | return -EINVAL; | |
1a396789 | 696 | |
2389fc13 BB |
697 | if (plane->layer.desc->max_width && |
698 | state->crtc_w > plane->layer.desc->max_width) | |
699 | return -EINVAL; | |
1a396789 | 700 | |
2389fc13 BB |
701 | if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) && |
702 | (!layout->memsize || | |
703 | atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format))) | |
704 | return -EINVAL; | |
1a396789 | 705 | |
2389fc13 BB |
706 | if (state->crtc_x < 0 || state->crtc_y < 0) |
707 | return -EINVAL; | |
708 | ||
709 | if (state->crtc_w + state->crtc_x > mode->hdisplay || | |
710 | state->crtc_h + state->crtc_y > mode->vdisplay) | |
711 | return -EINVAL; | |
1a396789 BB |
712 | |
713 | return 0; | |
714 | } | |
715 | ||
2389fc13 | 716 | static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, |
d136dfee | 717 | const struct drm_plane_state *new_state) |
1a396789 | 718 | { |
1a7b37ca BB |
719 | /* |
720 | * FIXME: we should avoid this const -> non-const cast but it's | |
721 | * currently the only solution we have to modify the ->prepared | |
722 | * state and rollback the update request. | |
723 | * Ideally, we should rework the code to attach all the resources | |
724 | * to atmel_hlcdc_plane_state (including the DMA desc allocation), | |
725 | * but this require a complete rework of the atmel_hlcdc_layer | |
726 | * code. | |
727 | */ | |
728 | struct drm_plane_state *s = (struct drm_plane_state *)new_state; | |
1a396789 | 729 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
1a7b37ca BB |
730 | struct atmel_hlcdc_plane_state *state = |
731 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
732 | int ret; | |
1a396789 | 733 | |
1a7b37ca BB |
734 | ret = atmel_hlcdc_layer_update_start(&plane->layer); |
735 | if (!ret) | |
736 | state->prepared = true; | |
737 | ||
738 | return ret; | |
739 | } | |
740 | ||
741 | static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p, | |
742 | const struct drm_plane_state *old_state) | |
743 | { | |
744 | /* | |
745 | * FIXME: we should avoid this const -> non-const cast but it's | |
746 | * currently the only solution we have to modify the ->prepared | |
747 | * state and rollback the update request. | |
748 | * Ideally, we should rework the code to attach all the resources | |
749 | * to atmel_hlcdc_plane_state (including the DMA desc allocation), | |
750 | * but this require a complete rework of the atmel_hlcdc_layer | |
751 | * code. | |
752 | */ | |
753 | struct drm_plane_state *s = (struct drm_plane_state *)old_state; | |
754 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
755 | struct atmel_hlcdc_plane_state *state = | |
756 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
757 | ||
758 | /* | |
759 | * The Request has already been applied or cancelled, nothing to do | |
760 | * here. | |
761 | */ | |
762 | if (!state->prepared) | |
763 | return; | |
844f9111 | 764 | |
1a7b37ca BB |
765 | atmel_hlcdc_layer_update_rollback(&plane->layer); |
766 | state->prepared = false; | |
1a396789 BB |
767 | } |
768 | ||
2389fc13 BB |
769 | static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, |
770 | struct drm_plane_state *old_s) | |
1a396789 | 771 | { |
2389fc13 BB |
772 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
773 | struct atmel_hlcdc_plane_state *state = | |
774 | drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
775 | ||
776 | if (!p->state->crtc || !p->state->fb) | |
777 | return; | |
778 | ||
779 | atmel_hlcdc_plane_update_pos_and_size(plane, state); | |
780 | atmel_hlcdc_plane_update_general_settings(plane, state); | |
781 | atmel_hlcdc_plane_update_format(plane, state); | |
782 | atmel_hlcdc_plane_update_buffers(plane, state); | |
5957017d | 783 | atmel_hlcdc_plane_update_disc_area(plane, state); |
2389fc13 BB |
784 | |
785 | atmel_hlcdc_layer_update_commit(&plane->layer); | |
1a396789 BB |
786 | } |
787 | ||
2389fc13 BB |
788 | static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, |
789 | struct drm_plane_state *old_state) | |
1a396789 BB |
790 | { |
791 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
792 | ||
2389fc13 | 793 | atmel_hlcdc_layer_disable(&plane->layer); |
1a396789 BB |
794 | } |
795 | ||
796 | static void atmel_hlcdc_plane_destroy(struct drm_plane *p) | |
797 | { | |
798 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
799 | ||
800 | if (plane->base.fb) | |
801 | drm_framebuffer_unreference(plane->base.fb); | |
802 | ||
803 | atmel_hlcdc_layer_cleanup(p->dev, &plane->layer); | |
804 | ||
805 | drm_plane_cleanup(p); | |
806 | devm_kfree(p->dev->dev, plane); | |
807 | } | |
808 | ||
2389fc13 BB |
809 | static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p, |
810 | struct drm_plane_state *s, | |
811 | struct drm_property *property, | |
812 | uint64_t val) | |
1a396789 | 813 | { |
2389fc13 BB |
814 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
815 | struct atmel_hlcdc_plane_properties *props = plane->properties; | |
816 | struct atmel_hlcdc_plane_state *state = | |
817 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
1a396789 | 818 | |
2389fc13 BB |
819 | if (property == props->alpha) |
820 | state->alpha = val; | |
821 | else | |
822 | return -EINVAL; | |
1a396789 BB |
823 | |
824 | return 0; | |
825 | } | |
826 | ||
2389fc13 BB |
827 | static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p, |
828 | const struct drm_plane_state *s, | |
829 | struct drm_property *property, | |
830 | uint64_t *val) | |
1a396789 BB |
831 | { |
832 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
833 | struct atmel_hlcdc_plane_properties *props = plane->properties; | |
2389fc13 BB |
834 | const struct atmel_hlcdc_plane_state *state = |
835 | container_of(s, const struct atmel_hlcdc_plane_state, base); | |
1a396789 BB |
836 | |
837 | if (property == props->alpha) | |
2389fc13 | 838 | *val = state->alpha; |
1a396789 BB |
839 | else |
840 | return -EINVAL; | |
841 | ||
842 | return 0; | |
843 | } | |
844 | ||
845 | static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, | |
846 | const struct atmel_hlcdc_layer_desc *desc, | |
847 | struct atmel_hlcdc_plane_properties *props) | |
848 | { | |
849 | struct regmap *regmap = plane->layer.hlcdc->regmap; | |
850 | ||
851 | if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || | |
852 | desc->type == ATMEL_HLCDC_CURSOR_LAYER) { | |
853 | drm_object_attach_property(&plane->base.base, | |
854 | props->alpha, 255); | |
855 | ||
856 | /* Set default alpha value */ | |
857 | regmap_update_bits(regmap, | |
858 | desc->regs_offset + | |
859 | ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer), | |
860 | ATMEL_HLCDC_LAYER_GA_MASK, | |
861 | ATMEL_HLCDC_LAYER_GA_MASK); | |
862 | } | |
863 | ||
864 | if (desc->layout.xstride && desc->layout.pstride) | |
865 | drm_object_attach_property(&plane->base.base, | |
2389fc13 BB |
866 | plane->base.dev->mode_config.rotation_property, |
867 | BIT(DRM_ROTATE_0)); | |
1a396789 BB |
868 | |
869 | if (desc->layout.csc) { | |
870 | /* | |
871 | * TODO: decare a "yuv-to-rgb-conv-factors" property to let | |
872 | * userspace modify these factors (using a BLOB property ?). | |
873 | */ | |
874 | regmap_write(regmap, | |
875 | desc->regs_offset + | |
876 | ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0), | |
877 | 0x4c900091); | |
878 | regmap_write(regmap, | |
879 | desc->regs_offset + | |
880 | ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1), | |
881 | 0x7a5f5090); | |
882 | regmap_write(regmap, | |
883 | desc->regs_offset + | |
884 | ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2), | |
885 | 0x40040890); | |
886 | } | |
887 | } | |
888 | ||
2389fc13 BB |
889 | static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { |
890 | .prepare_fb = atmel_hlcdc_plane_prepare_fb, | |
1a7b37ca | 891 | .cleanup_fb = atmel_hlcdc_plane_cleanup_fb, |
2389fc13 BB |
892 | .atomic_check = atmel_hlcdc_plane_atomic_check, |
893 | .atomic_update = atmel_hlcdc_plane_atomic_update, | |
894 | .atomic_disable = atmel_hlcdc_plane_atomic_disable, | |
895 | }; | |
896 | ||
897 | static void atmel_hlcdc_plane_reset(struct drm_plane *p) | |
898 | { | |
899 | struct atmel_hlcdc_plane_state *state; | |
900 | ||
901 | if (p->state) { | |
902 | state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
903 | ||
904 | if (state->base.fb) | |
905 | drm_framebuffer_unreference(state->base.fb); | |
906 | ||
907 | kfree(state); | |
908 | p->state = NULL; | |
909 | } | |
910 | ||
911 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
912 | if (state) { | |
913 | state->alpha = 255; | |
914 | p->state = &state->base; | |
915 | p->state->plane = p; | |
916 | } | |
917 | } | |
918 | ||
919 | static struct drm_plane_state * | |
920 | atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) | |
921 | { | |
922 | struct atmel_hlcdc_plane_state *state = | |
923 | drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
924 | struct atmel_hlcdc_plane_state *copy; | |
925 | ||
926 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); | |
927 | if (!copy) | |
928 | return NULL; | |
929 | ||
5957017d | 930 | copy->disc_updated = false; |
1a7b37ca | 931 | copy->prepared = false; |
5957017d | 932 | |
2389fc13 BB |
933 | if (copy->base.fb) |
934 | drm_framebuffer_reference(copy->base.fb); | |
935 | ||
936 | return ©->base; | |
937 | } | |
938 | ||
939 | static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane, | |
940 | struct drm_plane_state *s) | |
941 | { | |
942 | struct atmel_hlcdc_plane_state *state = | |
943 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
944 | ||
945 | if (s->fb) | |
946 | drm_framebuffer_unreference(s->fb); | |
947 | ||
948 | kfree(state); | |
949 | } | |
950 | ||
1a396789 | 951 | static struct drm_plane_funcs layer_plane_funcs = { |
2389fc13 BB |
952 | .update_plane = drm_atomic_helper_update_plane, |
953 | .disable_plane = drm_atomic_helper_disable_plane, | |
954 | .set_property = drm_atomic_helper_plane_set_property, | |
1a396789 | 955 | .destroy = atmel_hlcdc_plane_destroy, |
2389fc13 BB |
956 | .reset = atmel_hlcdc_plane_reset, |
957 | .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state, | |
958 | .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state, | |
959 | .atomic_set_property = atmel_hlcdc_plane_atomic_set_property, | |
960 | .atomic_get_property = atmel_hlcdc_plane_atomic_get_property, | |
1a396789 BB |
961 | }; |
962 | ||
963 | static struct atmel_hlcdc_plane * | |
964 | atmel_hlcdc_plane_create(struct drm_device *dev, | |
965 | const struct atmel_hlcdc_layer_desc *desc, | |
966 | struct atmel_hlcdc_plane_properties *props) | |
967 | { | |
968 | struct atmel_hlcdc_plane *plane; | |
969 | enum drm_plane_type type; | |
970 | int ret; | |
971 | ||
972 | plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); | |
973 | if (!plane) | |
974 | return ERR_PTR(-ENOMEM); | |
975 | ||
976 | ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc); | |
977 | if (ret) | |
978 | return ERR_PTR(ret); | |
979 | ||
980 | if (desc->type == ATMEL_HLCDC_BASE_LAYER) | |
981 | type = DRM_PLANE_TYPE_PRIMARY; | |
982 | else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER) | |
983 | type = DRM_PLANE_TYPE_CURSOR; | |
984 | else | |
985 | type = DRM_PLANE_TYPE_OVERLAY; | |
986 | ||
987 | ret = drm_universal_plane_init(dev, &plane->base, 0, | |
988 | &layer_plane_funcs, | |
989 | desc->formats->formats, | |
b0b3b795 | 990 | desc->formats->nformats, type, NULL); |
1a396789 BB |
991 | if (ret) |
992 | return ERR_PTR(ret); | |
993 | ||
2389fc13 BB |
994 | drm_plane_helper_add(&plane->base, |
995 | &atmel_hlcdc_layer_plane_helper_funcs); | |
996 | ||
1a396789 BB |
997 | /* Set default property values*/ |
998 | atmel_hlcdc_plane_init_properties(plane, desc, props); | |
999 | ||
1000 | return plane; | |
1001 | } | |
1002 | ||
1003 | static struct atmel_hlcdc_plane_properties * | |
1004 | atmel_hlcdc_plane_create_properties(struct drm_device *dev) | |
1005 | { | |
1006 | struct atmel_hlcdc_plane_properties *props; | |
1007 | ||
1008 | props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL); | |
1009 | if (!props) | |
1010 | return ERR_PTR(-ENOMEM); | |
1011 | ||
1012 | props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255); | |
1013 | if (!props->alpha) | |
1014 | return ERR_PTR(-ENOMEM); | |
1015 | ||
2389fc13 BB |
1016 | dev->mode_config.rotation_property = |
1017 | drm_mode_create_rotation_property(dev, | |
1018 | BIT(DRM_ROTATE_0) | | |
1019 | BIT(DRM_ROTATE_90) | | |
1020 | BIT(DRM_ROTATE_180) | | |
1021 | BIT(DRM_ROTATE_270)); | |
1022 | if (!dev->mode_config.rotation_property) | |
1a396789 BB |
1023 | return ERR_PTR(-ENOMEM); |
1024 | ||
1025 | return props; | |
1026 | } | |
1027 | ||
1028 | struct atmel_hlcdc_planes * | |
1029 | atmel_hlcdc_create_planes(struct drm_device *dev) | |
1030 | { | |
1031 | struct atmel_hlcdc_dc *dc = dev->dev_private; | |
1032 | struct atmel_hlcdc_plane_properties *props; | |
1033 | struct atmel_hlcdc_planes *planes; | |
1034 | const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; | |
1035 | int nlayers = dc->desc->nlayers; | |
1036 | int i; | |
1037 | ||
1038 | planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL); | |
1039 | if (!planes) | |
1040 | return ERR_PTR(-ENOMEM); | |
1041 | ||
1042 | for (i = 0; i < nlayers; i++) { | |
1043 | if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER) | |
1044 | planes->noverlays++; | |
1045 | } | |
1046 | ||
1047 | if (planes->noverlays) { | |
1048 | planes->overlays = devm_kzalloc(dev->dev, | |
1049 | planes->noverlays * | |
1050 | sizeof(*planes->overlays), | |
1051 | GFP_KERNEL); | |
1052 | if (!planes->overlays) | |
1053 | return ERR_PTR(-ENOMEM); | |
1054 | } | |
1055 | ||
1056 | props = atmel_hlcdc_plane_create_properties(dev); | |
1057 | if (IS_ERR(props)) | |
1058 | return ERR_CAST(props); | |
1059 | ||
1060 | planes->noverlays = 0; | |
1061 | for (i = 0; i < nlayers; i++) { | |
1062 | struct atmel_hlcdc_plane *plane; | |
1063 | ||
1064 | if (descs[i].type == ATMEL_HLCDC_PP_LAYER) | |
1065 | continue; | |
1066 | ||
1067 | plane = atmel_hlcdc_plane_create(dev, &descs[i], props); | |
1068 | if (IS_ERR(plane)) | |
1069 | return ERR_CAST(plane); | |
1070 | ||
1071 | plane->properties = props; | |
1072 | ||
1073 | switch (descs[i].type) { | |
1074 | case ATMEL_HLCDC_BASE_LAYER: | |
1075 | if (planes->primary) | |
1076 | return ERR_PTR(-EINVAL); | |
1077 | planes->primary = plane; | |
1078 | break; | |
1079 | ||
1080 | case ATMEL_HLCDC_OVERLAY_LAYER: | |
1081 | planes->overlays[planes->noverlays++] = plane; | |
1082 | break; | |
1083 | ||
1084 | case ATMEL_HLCDC_CURSOR_LAYER: | |
1085 | if (planes->cursor) | |
1086 | return ERR_PTR(-EINVAL); | |
1087 | planes->cursor = plane; | |
1088 | break; | |
1089 | ||
1090 | default: | |
1091 | break; | |
1092 | } | |
1093 | } | |
1094 | ||
1095 | return planes; | |
1096 | } |