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