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