1269 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Free Electrons4 * Copyright (C) 2014 Atmel5 *6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>7 */8 9#include <linux/dmapool.h>10#include <linux/mfd/atmel-hlcdc.h>11 12#include <drm/drm_atomic.h>13#include <drm/drm_atomic_helper.h>14#include <drm/drm_blend.h>15#include <drm/drm_fb_dma_helper.h>16#include <drm/drm_fourcc.h>17#include <drm/drm_framebuffer.h>18#include <drm/drm_gem_dma_helper.h>19 20#include "atmel_hlcdc_dc.h"21 22/**23 * struct atmel_hlcdc_plane_state - Atmel HLCDC Plane state structure.24 *25 * @base: DRM plane state26 * @crtc_x: x position of the plane relative to the CRTC27 * @crtc_y: y position of the plane relative to the CRTC28 * @crtc_w: visible width of the plane29 * @crtc_h: visible height of the plane30 * @src_x: x buffer position31 * @src_y: y buffer position32 * @src_w: buffer width33 * @src_h: buffer height34 * @disc_x: x discard position35 * @disc_y: y discard position36 * @disc_w: discard width37 * @disc_h: discard height38 * @ahb_id: AHB identification number39 * @bpp: bytes per pixel deduced from pixel_format40 * @offsets: offsets to apply to the GEM buffers41 * @xstride: value to add to the pixel pointer between each line42 * @pstride: value to add to the pixel pointer between each pixel43 * @nplanes: number of planes (deduced from pixel_format)44 * @dscrs: DMA descriptors45 */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 int disc_x;58 int disc_y;59 int disc_w;60 int disc_h;61 62 int ahb_id;63 64 /* These fields are private and should not be touched */65 int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];66 unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];67 int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];68 int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];69 int nplanes;70 71 /* DMA descriptors. */72 struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];73};74 75static inline struct atmel_hlcdc_plane_state *76drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)77{78 return container_of(s, struct atmel_hlcdc_plane_state, base);79}80 81#define SUBPIXEL_MASK 0xffff82 83static uint32_t rgb_formats[] = {84 DRM_FORMAT_C8,85 DRM_FORMAT_XRGB4444,86 DRM_FORMAT_ARGB4444,87 DRM_FORMAT_RGBA4444,88 DRM_FORMAT_ARGB1555,89 DRM_FORMAT_RGB565,90 DRM_FORMAT_RGB888,91 DRM_FORMAT_XRGB8888,92 DRM_FORMAT_ARGB8888,93 DRM_FORMAT_RGBA8888,94};95 96struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {97 .formats = rgb_formats,98 .nformats = ARRAY_SIZE(rgb_formats),99};100 101static uint32_t rgb_and_yuv_formats[] = {102 DRM_FORMAT_C8,103 DRM_FORMAT_XRGB4444,104 DRM_FORMAT_ARGB4444,105 DRM_FORMAT_RGBA4444,106 DRM_FORMAT_ARGB1555,107 DRM_FORMAT_RGB565,108 DRM_FORMAT_RGB888,109 DRM_FORMAT_XRGB8888,110 DRM_FORMAT_ARGB8888,111 DRM_FORMAT_RGBA8888,112 DRM_FORMAT_AYUV,113 DRM_FORMAT_YUYV,114 DRM_FORMAT_UYVY,115 DRM_FORMAT_YVYU,116 DRM_FORMAT_VYUY,117 DRM_FORMAT_NV21,118 DRM_FORMAT_NV61,119 DRM_FORMAT_YUV422,120 DRM_FORMAT_YUV420,121};122 123struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {124 .formats = rgb_and_yuv_formats,125 .nformats = ARRAY_SIZE(rgb_and_yuv_formats),126};127 128static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)129{130 switch (format) {131 case DRM_FORMAT_C8:132 *mode = ATMEL_HLCDC_C8_MODE;133 break;134 case DRM_FORMAT_XRGB4444:135 *mode = ATMEL_HLCDC_XRGB4444_MODE;136 break;137 case DRM_FORMAT_ARGB4444:138 *mode = ATMEL_HLCDC_ARGB4444_MODE;139 break;140 case DRM_FORMAT_RGBA4444:141 *mode = ATMEL_HLCDC_RGBA4444_MODE;142 break;143 case DRM_FORMAT_RGB565:144 *mode = ATMEL_HLCDC_RGB565_MODE;145 break;146 case DRM_FORMAT_RGB888:147 *mode = ATMEL_HLCDC_RGB888_MODE;148 break;149 case DRM_FORMAT_ARGB1555:150 *mode = ATMEL_HLCDC_ARGB1555_MODE;151 break;152 case DRM_FORMAT_XRGB8888:153 *mode = ATMEL_HLCDC_XRGB8888_MODE;154 break;155 case DRM_FORMAT_ARGB8888:156 *mode = ATMEL_HLCDC_ARGB8888_MODE;157 break;158 case DRM_FORMAT_RGBA8888:159 *mode = ATMEL_HLCDC_RGBA8888_MODE;160 break;161 case DRM_FORMAT_AYUV:162 *mode = ATMEL_HLCDC_AYUV_MODE;163 break;164 case DRM_FORMAT_YUYV:165 *mode = ATMEL_HLCDC_YUYV_MODE;166 break;167 case DRM_FORMAT_UYVY:168 *mode = ATMEL_HLCDC_UYVY_MODE;169 break;170 case DRM_FORMAT_YVYU:171 *mode = ATMEL_HLCDC_YVYU_MODE;172 break;173 case DRM_FORMAT_VYUY:174 *mode = ATMEL_HLCDC_VYUY_MODE;175 break;176 case DRM_FORMAT_NV21:177 *mode = ATMEL_HLCDC_NV21_MODE;178 break;179 case DRM_FORMAT_NV61:180 *mode = ATMEL_HLCDC_NV61_MODE;181 break;182 case DRM_FORMAT_YUV420:183 *mode = ATMEL_HLCDC_YUV420_MODE;184 break;185 case DRM_FORMAT_YUV422:186 *mode = ATMEL_HLCDC_YUV422_MODE;187 break;188 default:189 return -ENOTSUPP;190 }191 192 return 0;193}194 195static u32 heo_downscaling_xcoef[] = {196 0x11343311,197 0x000000f7,198 0x1635300c,199 0x000000f9,200 0x1b362c08,201 0x000000fb,202 0x1f372804,203 0x000000fe,204 0x24382400,205 0x00000000,206 0x28371ffe,207 0x00000004,208 0x2c361bfb,209 0x00000008,210 0x303516f9,211 0x0000000c,212};213 214static u32 heo_downscaling_ycoef[] = {215 0x00123737,216 0x00173732,217 0x001b382d,218 0x001f3928,219 0x00243824,220 0x0028391f,221 0x002d381b,222 0x00323717,223};224 225static u32 heo_upscaling_xcoef[] = {226 0xf74949f7,227 0x00000000,228 0xf55f33fb,229 0x000000fe,230 0xf5701efe,231 0x000000ff,232 0xf87c0dff,233 0x00000000,234 0x00800000,235 0x00000000,236 0x0d7cf800,237 0x000000ff,238 0x1e70f5ff,239 0x000000fe,240 0x335ff5fe,241 0x000000fb,242};243 244static u32 heo_upscaling_ycoef[] = {245 0x00004040,246 0x00075920,247 0x00056f0c,248 0x00027b03,249 0x00008000,250 0x00037b02,251 0x000c6f05,252 0x00205907,253};254 255#define ATMEL_HLCDC_XPHIDEF 4256#define ATMEL_HLCDC_YPHIDEF 4257 258static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,259 u32 dstsize,260 u32 phidef)261{262 u32 factor, max_memsize;263 264 factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);265 max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;266 267 if (max_memsize > srcsize - 1)268 factor--;269 270 return factor;271}272 273static void274atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,275 const u32 *coeff_tab, int size,276 unsigned int cfg_offs)277{278 int i;279 280 for (i = 0; i < size; i++)281 atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,282 coeff_tab[i]);283}284 285static286void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,287 struct atmel_hlcdc_plane_state *state)288{289 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;290 u32 xfactor, yfactor;291 292 if (!desc->layout.scaler_config)293 return;294 295 if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {296 atmel_hlcdc_layer_write_cfg(&plane->layer,297 desc->layout.scaler_config, 0);298 return;299 }300 301 if (desc->layout.phicoeffs.x) {302 xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,303 state->crtc_w,304 ATMEL_HLCDC_XPHIDEF);305 306 yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,307 state->crtc_h,308 ATMEL_HLCDC_YPHIDEF);309 310 atmel_hlcdc_plane_scaler_set_phicoeff(plane,311 state->crtc_w < state->src_w ?312 heo_downscaling_xcoef :313 heo_upscaling_xcoef,314 ARRAY_SIZE(heo_upscaling_xcoef),315 desc->layout.phicoeffs.x);316 317 atmel_hlcdc_plane_scaler_set_phicoeff(plane,318 state->crtc_h < state->src_h ?319 heo_downscaling_ycoef :320 heo_upscaling_ycoef,321 ARRAY_SIZE(heo_upscaling_ycoef),322 desc->layout.phicoeffs.y);323 } else {324 xfactor = (1024 * state->src_w) / state->crtc_w;325 yfactor = (1024 * state->src_h) / state->crtc_h;326 }327 328 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,329 ATMEL_HLCDC_LAYER_SCALER_ENABLE |330 ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,331 yfactor));332}333 334static335void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,336 struct atmel_hlcdc_plane_state *state)337{338 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;339 u32 xfactor, yfactor;340 341 if (!desc->layout.scaler_config)342 return;343 344 if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {345 atmel_hlcdc_layer_write_cfg(&plane->layer,346 desc->layout.scaler_config, 0);347 return;348 }349 350 /* xfactor = round[(2^20 * XMEMSIZE)/XSIZE)] */351 xfactor = (u32)(((1 << 20) * state->src_w) / state->crtc_w);352 353 /* yfactor = round[(2^20 * YMEMSIZE)/YSIZE)] */354 yfactor = (u32)(((1 << 20) * state->src_h) / state->crtc_h);355 356 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,357 ATMEL_XLCDC_LAYER_VSCALER_LUMA_ENABLE |358 ATMEL_XLCDC_LAYER_VSCALER_CHROMA_ENABLE |359 ATMEL_XLCDC_LAYER_HSCALER_LUMA_ENABLE |360 ATMEL_XLCDC_LAYER_HSCALER_CHROMA_ENABLE);361 362 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 1,363 yfactor);364 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 3,365 xfactor);366 367 /*368 * With YCbCr 4:2:2 and YCbYcr 4:2:0 window resampling, configuration369 * register LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT is half370 * the value of yfactor and xfactor.371 */372 if (state->base.fb->format->format == DRM_FORMAT_YUV420) {373 yfactor /= 2;374 xfactor /= 2;375 }376 377 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2,378 yfactor);379 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 4,380 xfactor);381}382 383static void384atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,385 struct atmel_hlcdc_plane_state *state)386{387 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;388 struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;389 390 if (desc->layout.size)391 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,392 ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,393 state->crtc_h));394 395 if (desc->layout.memsize)396 atmel_hlcdc_layer_write_cfg(&plane->layer,397 desc->layout.memsize,398 ATMEL_HLCDC_LAYER_SIZE(state->src_w,399 state->src_h));400 401 if (desc->layout.pos)402 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,403 ATMEL_HLCDC_LAYER_POS(state->crtc_x,404 state->crtc_y));405 406 dc->desc->ops->plane_setup_scaler(plane, state);407}408 409static410void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,411 struct atmel_hlcdc_plane_state *state)412{413 unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;414 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;415 const struct drm_format_info *format = state->base.fb->format;416 417 /*418 * Rotation optimization is not working on RGB888 (rotation is still419 * working but without any optimization).420 */421 if (format->format == DRM_FORMAT_RGB888)422 cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;423 424 atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,425 cfg);426 427 cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP;428 429 if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {430 cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |431 ATMEL_HLCDC_LAYER_ITER;432 433 if (format->has_alpha)434 cfg |= ATMEL_HLCDC_LAYER_LAEN;435 else436 cfg |= ATMEL_HLCDC_LAYER_GAEN |437 ATMEL_HLCDC_LAYER_GA(state->base.alpha);438 }439 440 if (state->disc_h && state->disc_w)441 cfg |= ATMEL_HLCDC_LAYER_DISCEN;442 443 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,444 cfg);445}446 447static448void atmel_xlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,449 struct atmel_hlcdc_plane_state *state)450{451 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;452 const struct drm_format_info *format = state->base.fb->format;453 unsigned int cfg;454 455 atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_XLCDC_LAYER_DMA_CFG,456 ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id);457 458 cfg = ATMEL_XLCDC_LAYER_DMA | ATMEL_XLCDC_LAYER_REP;459 460 if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {461 /*462 * Alpha Blending bits specific to SAM9X7 SoC463 */464 cfg |= ATMEL_XLCDC_LAYER_SFACTC_A0_MULT_AS |465 ATMEL_XLCDC_LAYER_SFACTA_ONE |466 ATMEL_XLCDC_LAYER_DFACTC_M_A0_MULT_AS |467 ATMEL_XLCDC_LAYER_DFACTA_ONE;468 if (format->has_alpha)469 cfg |= ATMEL_XLCDC_LAYER_A0(0xff);470 else471 cfg |= ATMEL_XLCDC_LAYER_A0(state->base.alpha);472 }473 474 if (state->disc_h && state->disc_w)475 cfg |= ATMEL_XLCDC_LAYER_DISCEN;476 477 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,478 cfg);479}480 481static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,482 struct atmel_hlcdc_plane_state *state)483{484 u32 cfg;485 int ret;486 487 ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,488 &cfg);489 if (ret)490 return;491 492 if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||493 state->base.fb->format->format == DRM_FORMAT_NV61) &&494 drm_rotation_90_or_270(state->base.rotation))495 cfg |= ATMEL_HLCDC_YUV422ROT;496 497 atmel_hlcdc_layer_write_cfg(&plane->layer,498 ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);499}500 501static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane,502 struct atmel_hlcdc_plane_state *state)503{504 struct drm_crtc *crtc = state->base.crtc;505 struct drm_color_lut *lut;506 int idx;507 508 if (!crtc || !crtc->state)509 return;510 511 if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)512 return;513 514 lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;515 516 for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {517 u32 val = ((lut->red << 8) & 0xff0000) |518 (lut->green & 0xff00) |519 (lut->blue >> 8);520 521 atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);522 }523}524 525static void atmel_hlcdc_update_buffers(struct atmel_hlcdc_plane *plane,526 struct atmel_hlcdc_plane_state *state,527 u32 sr, int i)528{529 atmel_hlcdc_layer_write_reg(&plane->layer,530 ATMEL_HLCDC_LAYER_PLANE_HEAD(i),531 state->dscrs[i]->self);532 533 if (sr & ATMEL_HLCDC_LAYER_EN)534 return;535 536 atmel_hlcdc_layer_write_reg(&plane->layer,537 ATMEL_HLCDC_LAYER_PLANE_ADDR(i),538 state->dscrs[i]->addr);539 atmel_hlcdc_layer_write_reg(&plane->layer,540 ATMEL_HLCDC_LAYER_PLANE_CTRL(i),541 state->dscrs[i]->ctrl);542 atmel_hlcdc_layer_write_reg(&plane->layer,543 ATMEL_HLCDC_LAYER_PLANE_NEXT(i),544 state->dscrs[i]->self);545}546 547static void atmel_xlcdc_update_buffers(struct atmel_hlcdc_plane *plane,548 struct atmel_hlcdc_plane_state *state,549 u32 sr, int i)550{551 atmel_hlcdc_layer_write_reg(&plane->layer,552 ATMEL_XLCDC_LAYER_PLANE_ADDR(i),553 state->dscrs[i]->addr);554}555 556static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,557 struct atmel_hlcdc_plane_state *state)558{559 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;560 struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;561 struct drm_framebuffer *fb = state->base.fb;562 u32 sr;563 int i;564 565 if (!dc->desc->is_xlcdc)566 sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);567 568 for (i = 0; i < state->nplanes; i++) {569 struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);570 571 state->dscrs[i]->addr = gem->dma_addr + state->offsets[i];572 573 dc->desc->ops->lcdc_update_buffers(plane, state, sr, i);574 575 if (desc->layout.xstride[i])576 atmel_hlcdc_layer_write_cfg(&plane->layer,577 desc->layout.xstride[i],578 state->xstride[i]);579 580 if (desc->layout.pstride[i])581 atmel_hlcdc_layer_write_cfg(&plane->layer,582 desc->layout.pstride[i],583 state->pstride[i]);584 }585}586 587int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)588{589 unsigned int ahb_load[2] = { };590 struct drm_plane *plane;591 592 drm_atomic_crtc_state_for_each_plane(plane, c_state) {593 struct atmel_hlcdc_plane_state *plane_state;594 struct drm_plane_state *plane_s;595 unsigned int pixels, load = 0;596 int i;597 598 plane_s = drm_atomic_get_plane_state(c_state->state, plane);599 if (IS_ERR(plane_s))600 return PTR_ERR(plane_s);601 602 plane_state =603 drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);604 605 pixels = (plane_state->src_w * plane_state->src_h) -606 (plane_state->disc_w * plane_state->disc_h);607 608 for (i = 0; i < plane_state->nplanes; i++)609 load += pixels * plane_state->bpp[i];610 611 if (ahb_load[0] <= ahb_load[1])612 plane_state->ahb_id = 0;613 else614 plane_state->ahb_id = 1;615 616 ahb_load[plane_state->ahb_id] += load;617 }618 619 return 0;620}621 622int623atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)624{625 int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;626 const struct atmel_hlcdc_layer_cfg_layout *layout;627 struct atmel_hlcdc_plane_state *primary_state;628 struct drm_plane_state *primary_s;629 struct atmel_hlcdc_plane *primary;630 struct drm_plane *ovl;631 632 primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);633 layout = &primary->layer.desc->layout;634 if (!layout->disc_pos || !layout->disc_size)635 return 0;636 637 primary_s = drm_atomic_get_plane_state(c_state->state,638 &primary->base);639 if (IS_ERR(primary_s))640 return PTR_ERR(primary_s);641 642 primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);643 644 drm_atomic_crtc_state_for_each_plane(ovl, c_state) {645 struct atmel_hlcdc_plane_state *ovl_state;646 struct drm_plane_state *ovl_s;647 648 if (ovl == c_state->crtc->primary)649 continue;650 651 ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);652 if (IS_ERR(ovl_s))653 return PTR_ERR(ovl_s);654 655 ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);656 657 if (!ovl_s->visible ||658 !ovl_s->fb ||659 ovl_s->fb->format->has_alpha ||660 ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)661 continue;662 663 /* TODO: implement a smarter hidden area detection */664 if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)665 continue;666 667 disc_x = ovl_state->crtc_x;668 disc_y = ovl_state->crtc_y;669 disc_h = ovl_state->crtc_h;670 disc_w = ovl_state->crtc_w;671 }672 673 primary_state->disc_x = disc_x;674 primary_state->disc_y = disc_y;675 primary_state->disc_w = disc_w;676 primary_state->disc_h = disc_h;677 678 return 0;679}680 681static void682atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,683 struct atmel_hlcdc_plane_state *state)684{685 const struct atmel_hlcdc_layer_cfg_layout *layout;686 687 layout = &plane->layer.desc->layout;688 if (!layout->disc_pos || !layout->disc_size)689 return;690 691 atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,692 ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,693 state->disc_y));694 695 atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,696 ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,697 state->disc_h));698}699 700static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,701 struct drm_atomic_state *state)702{703 struct drm_plane_state *s = drm_atomic_get_new_plane_state(state, p);704 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);705 struct atmel_hlcdc_plane_state *hstate =706 drm_plane_state_to_atmel_hlcdc_plane_state(s);707 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;708 struct drm_framebuffer *fb = hstate->base.fb;709 const struct drm_display_mode *mode;710 struct drm_crtc_state *crtc_state;711 int ret;712 int i;713 714 if (!hstate->base.crtc || WARN_ON(!fb))715 return 0;716 717 crtc_state = drm_atomic_get_existing_crtc_state(state, s->crtc);718 mode = &crtc_state->adjusted_mode;719 720 ret = drm_atomic_helper_check_plane_state(s, crtc_state,721 (1 << 16) / 2048,722 INT_MAX, true, true);723 if (ret || !s->visible)724 return ret;725 726 hstate->src_x = s->src.x1;727 hstate->src_y = s->src.y1;728 hstate->src_w = drm_rect_width(&s->src);729 hstate->src_h = drm_rect_height(&s->src);730 hstate->crtc_x = s->dst.x1;731 hstate->crtc_y = s->dst.y1;732 hstate->crtc_w = drm_rect_width(&s->dst);733 hstate->crtc_h = drm_rect_height(&s->dst);734 735 if ((hstate->src_x | hstate->src_y | hstate->src_w | hstate->src_h) &736 SUBPIXEL_MASK)737 return -EINVAL;738 739 hstate->src_x >>= 16;740 hstate->src_y >>= 16;741 hstate->src_w >>= 16;742 hstate->src_h >>= 16;743 744 hstate->nplanes = fb->format->num_planes;745 if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)746 return -EINVAL;747 748 for (i = 0; i < hstate->nplanes; i++) {749 unsigned int offset = 0;750 int xdiv = i ? fb->format->hsub : 1;751 int ydiv = i ? fb->format->vsub : 1;752 753 hstate->bpp[i] = fb->format->cpp[i];754 if (!hstate->bpp[i])755 return -EINVAL;756 757 switch (hstate->base.rotation & DRM_MODE_ROTATE_MASK) {758 case DRM_MODE_ROTATE_90:759 offset = (hstate->src_y / ydiv) *760 fb->pitches[i];761 offset += ((hstate->src_x + hstate->src_w - 1) /762 xdiv) * hstate->bpp[i];763 hstate->xstride[i] = -(((hstate->src_h - 1) / ydiv) *764 fb->pitches[i]) -765 (2 * hstate->bpp[i]);766 hstate->pstride[i] = fb->pitches[i] - hstate->bpp[i];767 break;768 case DRM_MODE_ROTATE_180:769 offset = ((hstate->src_y + hstate->src_h - 1) /770 ydiv) * fb->pitches[i];771 offset += ((hstate->src_x + hstate->src_w - 1) /772 xdiv) * hstate->bpp[i];773 hstate->xstride[i] = ((((hstate->src_w - 1) / xdiv) - 1) *774 hstate->bpp[i]) - fb->pitches[i];775 hstate->pstride[i] = -2 * hstate->bpp[i];776 break;777 case DRM_MODE_ROTATE_270:778 offset = ((hstate->src_y + hstate->src_h - 1) /779 ydiv) * fb->pitches[i];780 offset += (hstate->src_x / xdiv) * hstate->bpp[i];781 hstate->xstride[i] = ((hstate->src_h - 1) / ydiv) *782 fb->pitches[i];783 hstate->pstride[i] = -fb->pitches[i] - hstate->bpp[i];784 break;785 case DRM_MODE_ROTATE_0:786 default:787 offset = (hstate->src_y / ydiv) * fb->pitches[i];788 offset += (hstate->src_x / xdiv) * hstate->bpp[i];789 hstate->xstride[i] = fb->pitches[i] -790 ((hstate->src_w / xdiv) *791 hstate->bpp[i]);792 hstate->pstride[i] = 0;793 break;794 }795 796 hstate->offsets[i] = offset + fb->offsets[i];797 }798 799 /*800 * Swap width and size in case of 90 or 270 degrees rotation801 */802 if (drm_rotation_90_or_270(hstate->base.rotation)) {803 swap(hstate->src_w, hstate->src_h);804 }805 806 if (!desc->layout.size &&807 (mode->hdisplay != hstate->crtc_w ||808 mode->vdisplay != hstate->crtc_h))809 return -EINVAL;810 811 if ((hstate->crtc_h != hstate->src_h || hstate->crtc_w != hstate->src_w) &&812 (!desc->layout.memsize ||813 hstate->base.fb->format->has_alpha))814 return -EINVAL;815 816 return 0;817}818 819static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)820{821 /* Disable interrupts */822 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,823 0xffffffff);824 825 /* Disable the layer */826 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,827 ATMEL_HLCDC_LAYER_RST |828 ATMEL_HLCDC_LAYER_A2Q |829 ATMEL_HLCDC_LAYER_UPDATE);830 831 /* Clear all pending interrupts */832 atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);833}834 835static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)836{837 /* Disable interrupts */838 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR,839 0xffffffff);840 841 /* Disable the layer */842 atmel_hlcdc_layer_write_reg(&plane->layer,843 ATMEL_XLCDC_LAYER_ENR, 0);844 845 /* Clear all pending interrupts */846 atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);847}848 849static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,850 struct drm_atomic_state *state)851{852 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);853 struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;854 855 dc->desc->ops->lcdc_atomic_disable(plane);856}857 858static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane,859 struct atmel_hlcdc_dc *dc)860{861 u32 sr;862 863 /* Enable the overrun interrupts. */864 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,865 ATMEL_HLCDC_LAYER_OVR_IRQ(0) |866 ATMEL_HLCDC_LAYER_OVR_IRQ(1) |867 ATMEL_HLCDC_LAYER_OVR_IRQ(2));868 869 /* Apply the new config at the next SOF event. */870 sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);871 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,872 ATMEL_HLCDC_LAYER_UPDATE |873 (sr & ATMEL_HLCDC_LAYER_EN ?874 ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));875}876 877static void atmel_xlcdc_atomic_update(struct atmel_hlcdc_plane *plane,878 struct atmel_hlcdc_dc *dc)879{880 /* Enable the overrun interrupts. */881 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IER,882 ATMEL_XLCDC_LAYER_OVR_IRQ(0) |883 ATMEL_XLCDC_LAYER_OVR_IRQ(1) |884 ATMEL_XLCDC_LAYER_OVR_IRQ(2));885 886 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_ENR,887 ATMEL_XLCDC_LAYER_EN);888 889 /*890 * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,891 * (where xxx indicates each layer) requires writing one to the892 * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.893 */894 regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |895 ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |896 ATMEL_XLCDC_HEO_UPDATE);897}898 899static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,900 struct drm_atomic_state *state)901{902 struct drm_plane_state *new_s = drm_atomic_get_new_plane_state(state,903 p);904 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);905 struct atmel_hlcdc_plane_state *hstate =906 drm_plane_state_to_atmel_hlcdc_plane_state(new_s);907 struct atmel_hlcdc_dc *dc = p->dev->dev_private;908 909 if (!new_s->crtc || !new_s->fb)910 return;911 912 if (!hstate->base.visible) {913 atmel_hlcdc_plane_atomic_disable(p, state);914 return;915 }916 917 atmel_hlcdc_plane_update_pos_and_size(plane, hstate);918 dc->desc->ops->lcdc_update_general_settings(plane, hstate);919 atmel_hlcdc_plane_update_format(plane, hstate);920 atmel_hlcdc_plane_update_clut(plane, hstate);921 atmel_hlcdc_plane_update_buffers(plane, hstate);922 atmel_hlcdc_plane_update_disc_area(plane, hstate);923 924 dc->desc->ops->lcdc_atomic_update(plane, dc);925}926 927static void atmel_hlcdc_csc_init(struct atmel_hlcdc_plane *plane,928 const struct atmel_hlcdc_layer_desc *desc)929{930 /*931 * TODO: declare a "yuv-to-rgb-conv-factors" property to let932 * userspace modify these factors (using a BLOB property ?).933 */934 static const u32 hlcdc_csc_coeffs[] = {935 0x4c900091,936 0x7a5f5090,937 0x40040890938 };939 940 for (int i = 0; i < ARRAY_SIZE(hlcdc_csc_coeffs); i++) {941 atmel_hlcdc_layer_write_cfg(&plane->layer,942 desc->layout.csc + i,943 hlcdc_csc_coeffs[i]);944 }945}946 947static void atmel_xlcdc_csc_init(struct atmel_hlcdc_plane *plane,948 const struct atmel_hlcdc_layer_desc *desc)949{950 /*951 * yuv-to-rgb-conv-factors are now defined from LCDC_HEOCFG16 to952 * LCDC_HEOCFG21 registers in SAM9X7.953 */954 static const u32 xlcdc_csc_coeffs[] = {955 0x00000488,956 0x00000648,957 0x1EA00480,958 0x00001D28,959 0x08100480,960 0x00000000,961 0x00000007962 };963 964 for (int i = 0; i < ARRAY_SIZE(xlcdc_csc_coeffs); i++) {965 atmel_hlcdc_layer_write_cfg(&plane->layer,966 desc->layout.csc + i,967 xlcdc_csc_coeffs[i]);968 }969 970 if (desc->layout.vxs_config && desc->layout.hxs_config) {971 /*972 * Updating vxs.config and hxs.config fixes the973 * Green Color Issue in SAM9X7 EGT Video Player App974 */975 atmel_hlcdc_layer_write_cfg(&plane->layer,976 desc->layout.vxs_config,977 ATMEL_XLCDC_LAYER_VXSYCFG_ONE |978 ATMEL_XLCDC_LAYER_VXSYTAP2_ENABLE |979 ATMEL_XLCDC_LAYER_VXSCCFG_ONE |980 ATMEL_XLCDC_LAYER_VXSCTAP2_ENABLE);981 982 atmel_hlcdc_layer_write_cfg(&plane->layer,983 desc->layout.hxs_config,984 ATMEL_XLCDC_LAYER_HXSYCFG_ONE |985 ATMEL_XLCDC_LAYER_HXSYTAP2_ENABLE |986 ATMEL_XLCDC_LAYER_HXSCCFG_ONE |987 ATMEL_XLCDC_LAYER_HXSCTAP2_ENABLE);988 }989}990 991static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)992{993 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;994 struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;995 996 if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||997 desc->type == ATMEL_HLCDC_CURSOR_LAYER) {998 int ret;999 1000 ret = drm_plane_create_alpha_property(&plane->base);1001 if (ret)1002 return ret;1003 }1004 1005 if (desc->layout.xstride[0] && desc->layout.pstride[0]) {1006 int ret;1007 1008 ret = drm_plane_create_rotation_property(&plane->base,1009 DRM_MODE_ROTATE_0,1010 DRM_MODE_ROTATE_0 |1011 DRM_MODE_ROTATE_90 |1012 DRM_MODE_ROTATE_180 |1013 DRM_MODE_ROTATE_270);1014 if (ret)1015 return ret;1016 }1017 1018 if (desc->layout.csc)1019 dc->desc->ops->lcdc_csc_init(plane, desc);1020 1021 return 0;1022}1023 1024static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,1025 const struct atmel_hlcdc_layer_desc *desc)1026{1027 u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);1028 1029 /*1030 * There's not much we can do in case of overrun except informing1031 * the user. However, we are in interrupt context here, hence the1032 * use of dev_dbg().1033 */1034 if (isr &1035 (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |1036 ATMEL_HLCDC_LAYER_OVR_IRQ(2)))1037 dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",1038 desc->name);1039}1040 1041static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,1042 const struct atmel_hlcdc_layer_desc *desc)1043{1044 u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);1045 1046 /*1047 * There's not much we can do in case of overrun except informing1048 * the user. However, we are in interrupt context here, hence the1049 * use of dev_dbg().1050 */1051 if (isr &1052 (ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) |1053 ATMEL_XLCDC_LAYER_OVR_IRQ(2)))1054 dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",1055 desc->name);1056}1057 1058void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)1059{1060 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;1061 struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;1062 1063 dc->desc->ops->lcdc_irq_dbg(plane, desc);1064}1065 1066const struct atmel_lcdc_dc_ops atmel_hlcdc_ops = {1067 .plane_setup_scaler = atmel_hlcdc_plane_setup_scaler,1068 .lcdc_update_buffers = atmel_hlcdc_update_buffers,1069 .lcdc_atomic_disable = atmel_hlcdc_atomic_disable,1070 .lcdc_update_general_settings = atmel_hlcdc_plane_update_general_settings,1071 .lcdc_atomic_update = atmel_hlcdc_atomic_update,1072 .lcdc_csc_init = atmel_hlcdc_csc_init,1073 .lcdc_irq_dbg = atmel_hlcdc_irq_dbg,1074};1075 1076const struct atmel_lcdc_dc_ops atmel_xlcdc_ops = {1077 .plane_setup_scaler = atmel_xlcdc_plane_setup_scaler,1078 .lcdc_update_buffers = atmel_xlcdc_update_buffers,1079 .lcdc_atomic_disable = atmel_xlcdc_atomic_disable,1080 .lcdc_update_general_settings = atmel_xlcdc_plane_update_general_settings,1081 .lcdc_atomic_update = atmel_xlcdc_atomic_update,1082 .lcdc_csc_init = atmel_xlcdc_csc_init,1083 .lcdc_irq_dbg = atmel_xlcdc_irq_dbg,1084};1085 1086static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {1087 .atomic_check = atmel_hlcdc_plane_atomic_check,1088 .atomic_update = atmel_hlcdc_plane_atomic_update,1089 .atomic_disable = atmel_hlcdc_plane_atomic_disable,1090};1091 1092static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,1093 struct atmel_hlcdc_plane_state *state)1094{1095 struct atmel_hlcdc_dc *dc = p->dev->dev_private;1096 int i;1097 1098 for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {1099 struct atmel_hlcdc_dma_channel_dscr *dscr;1100 dma_addr_t dscr_dma;1101 1102 dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);1103 if (!dscr)1104 goto err;1105 1106 dscr->addr = 0;1107 dscr->next = dscr_dma;1108 dscr->self = dscr_dma;1109 dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;1110 1111 state->dscrs[i] = dscr;1112 }1113 1114 return 0;1115 1116err:1117 for (i--; i >= 0; i--) {1118 dma_pool_free(dc->dscrpool, state->dscrs[i],1119 state->dscrs[i]->self);1120 }1121 1122 return -ENOMEM;1123}1124 1125static void atmel_hlcdc_plane_reset(struct drm_plane *p)1126{1127 struct atmel_hlcdc_plane_state *state;1128 1129 if (p->state) {1130 state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);1131 1132 if (state->base.fb)1133 drm_framebuffer_put(state->base.fb);1134 1135 kfree(state);1136 p->state = NULL;1137 }1138 1139 state = kzalloc(sizeof(*state), GFP_KERNEL);1140 if (state) {1141 if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {1142 kfree(state);1143 dev_err(p->dev->dev,1144 "Failed to allocate initial plane state\n");1145 return;1146 }1147 __drm_atomic_helper_plane_reset(p, &state->base);1148 }1149}1150 1151static struct drm_plane_state *1152atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)1153{1154 struct atmel_hlcdc_plane_state *state =1155 drm_plane_state_to_atmel_hlcdc_plane_state(p->state);1156 struct atmel_hlcdc_plane_state *copy;1157 1158 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);1159 if (!copy)1160 return NULL;1161 1162 if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {1163 kfree(copy);1164 return NULL;1165 }1166 1167 if (copy->base.fb)1168 drm_framebuffer_get(copy->base.fb);1169 1170 return ©->base;1171}1172 1173static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,1174 struct drm_plane_state *s)1175{1176 struct atmel_hlcdc_plane_state *state =1177 drm_plane_state_to_atmel_hlcdc_plane_state(s);1178 struct atmel_hlcdc_dc *dc = p->dev->dev_private;1179 int i;1180 1181 for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {1182 dma_pool_free(dc->dscrpool, state->dscrs[i],1183 state->dscrs[i]->self);1184 }1185 1186 if (s->fb)1187 drm_framebuffer_put(s->fb);1188 1189 kfree(state);1190}1191 1192static const struct drm_plane_funcs layer_plane_funcs = {1193 .update_plane = drm_atomic_helper_update_plane,1194 .disable_plane = drm_atomic_helper_disable_plane,1195 .destroy = drm_plane_cleanup,1196 .reset = atmel_hlcdc_plane_reset,1197 .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,1198 .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,1199};1200 1201static int atmel_hlcdc_plane_create(struct drm_device *dev,1202 const struct atmel_hlcdc_layer_desc *desc)1203{1204 struct atmel_hlcdc_dc *dc = dev->dev_private;1205 struct atmel_hlcdc_plane *plane;1206 enum drm_plane_type type;1207 int ret;1208 1209 plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);1210 if (!plane)1211 return -ENOMEM;1212 1213 atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);1214 1215 if (desc->type == ATMEL_HLCDC_BASE_LAYER)1216 type = DRM_PLANE_TYPE_PRIMARY;1217 else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)1218 type = DRM_PLANE_TYPE_CURSOR;1219 else1220 type = DRM_PLANE_TYPE_OVERLAY;1221 1222 ret = drm_universal_plane_init(dev, &plane->base, 0,1223 &layer_plane_funcs,1224 desc->formats->formats,1225 desc->formats->nformats,1226 NULL, type, NULL);1227 if (ret)1228 return ret;1229 1230 drm_plane_helper_add(&plane->base,1231 &atmel_hlcdc_layer_plane_helper_funcs);1232 1233 /* Set default property values*/1234 ret = atmel_hlcdc_plane_init_properties(plane);1235 if (ret)1236 return ret;1237 1238 dc->layers[desc->id] = &plane->layer;1239 1240 return 0;1241}1242 1243int atmel_hlcdc_create_planes(struct drm_device *dev)1244{1245 struct atmel_hlcdc_dc *dc = dev->dev_private;1246 const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;1247 int nlayers = dc->desc->nlayers;1248 int i, ret;1249 1250 dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,1251 sizeof(struct atmel_hlcdc_dma_channel_dscr),1252 sizeof(u64), 0);1253 if (!dc->dscrpool)1254 return -ENOMEM;1255 1256 for (i = 0; i < nlayers; i++) {1257 if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&1258 descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&1259 descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)1260 continue;1261 1262 ret = atmel_hlcdc_plane_create(dev, &descs[i]);1263 if (ret)1264 return ret;1265 }1266 1267 return 0;1268}1269