brintos

brintos / linux-shallow public Read only

0
0
Text · 33.6 KiB · 4a7ba09 Raw
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 &copy->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