11a396789SBoris Brezillon /*
21a396789SBoris Brezillon  * Copyright (C) 2014 Free Electrons
31a396789SBoris Brezillon  * Copyright (C) 2014 Atmel
41a396789SBoris Brezillon  *
51a396789SBoris Brezillon  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
61a396789SBoris Brezillon  *
71a396789SBoris Brezillon  * This program is free software; you can redistribute it and/or modify it
81a396789SBoris Brezillon  * under the terms of the GNU General Public License version 2 as published by
91a396789SBoris Brezillon  * the Free Software Foundation.
101a396789SBoris Brezillon  *
111a396789SBoris Brezillon  * This program is distributed in the hope that it will be useful, but WITHOUT
121a396789SBoris Brezillon  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131a396789SBoris Brezillon  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
141a396789SBoris Brezillon  * more details.
151a396789SBoris Brezillon  *
161a396789SBoris Brezillon  * You should have received a copy of the GNU General Public License along with
171a396789SBoris Brezillon  * this program.  If not, see <http://www.gnu.org/licenses/>.
181a396789SBoris Brezillon  */
191a396789SBoris Brezillon 
201a396789SBoris Brezillon #include "atmel_hlcdc_dc.h"
211a396789SBoris Brezillon 
222389fc13SBoris Brezillon /**
232389fc13SBoris Brezillon  * Atmel HLCDC Plane state structure.
242389fc13SBoris Brezillon  *
252389fc13SBoris Brezillon  * @base: DRM plane state
262389fc13SBoris Brezillon  * @crtc_x: x position of the plane relative to the CRTC
272389fc13SBoris Brezillon  * @crtc_y: y position of the plane relative to the CRTC
282389fc13SBoris Brezillon  * @crtc_w: visible width of the plane
292389fc13SBoris Brezillon  * @crtc_h: visible height of the plane
302389fc13SBoris Brezillon  * @src_x: x buffer position
312389fc13SBoris Brezillon  * @src_y: y buffer position
322389fc13SBoris Brezillon  * @src_w: buffer width
332389fc13SBoris Brezillon  * @src_h: buffer height
349a45d33cSBoris Brezillon  * @disc_x: x discard position
359a45d33cSBoris Brezillon  * @disc_y: y discard position
369a45d33cSBoris Brezillon  * @disc_w: discard width
379a45d33cSBoris Brezillon  * @disc_h: discard height
382389fc13SBoris Brezillon  * @bpp: bytes per pixel deduced from pixel_format
392389fc13SBoris Brezillon  * @offsets: offsets to apply to the GEM buffers
402389fc13SBoris Brezillon  * @xstride: value to add to the pixel pointer between each line
412389fc13SBoris Brezillon  * @pstride: value to add to the pixel pointer between each pixel
422389fc13SBoris Brezillon  * @nplanes: number of planes (deduced from pixel_format)
439a45d33cSBoris Brezillon  * @dscrs: DMA descriptors
442389fc13SBoris Brezillon  */
452389fc13SBoris Brezillon struct atmel_hlcdc_plane_state {
462389fc13SBoris Brezillon 	struct drm_plane_state base;
472389fc13SBoris Brezillon 	int crtc_x;
482389fc13SBoris Brezillon 	int crtc_y;
492389fc13SBoris Brezillon 	unsigned int crtc_w;
502389fc13SBoris Brezillon 	unsigned int crtc_h;
512389fc13SBoris Brezillon 	uint32_t src_x;
522389fc13SBoris Brezillon 	uint32_t src_y;
532389fc13SBoris Brezillon 	uint32_t src_w;
542389fc13SBoris Brezillon 	uint32_t src_h;
552389fc13SBoris Brezillon 
565957017dSBoris Brezillon 	int disc_x;
575957017dSBoris Brezillon 	int disc_y;
585957017dSBoris Brezillon 	int disc_w;
595957017dSBoris Brezillon 	int disc_h;
605957017dSBoris Brezillon 
61ebab87abSBoris Brezillon 	int ahb_id;
62ebab87abSBoris Brezillon 
632389fc13SBoris Brezillon 	/* These fields are private and should not be touched */
649a45d33cSBoris Brezillon 	int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
659a45d33cSBoris Brezillon 	unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
669a45d33cSBoris Brezillon 	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
679a45d33cSBoris Brezillon 	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
682389fc13SBoris Brezillon 	int nplanes;
699a45d33cSBoris Brezillon 
709a45d33cSBoris Brezillon 	/* DMA descriptors. */
719a45d33cSBoris Brezillon 	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
722389fc13SBoris Brezillon };
732389fc13SBoris Brezillon 
742389fc13SBoris Brezillon static inline struct atmel_hlcdc_plane_state *
752389fc13SBoris Brezillon drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
762389fc13SBoris Brezillon {
772389fc13SBoris Brezillon 	return container_of(s, struct atmel_hlcdc_plane_state, base);
782389fc13SBoris Brezillon }
792389fc13SBoris Brezillon 
801a396789SBoris Brezillon #define SUBPIXEL_MASK			0xffff
811a396789SBoris Brezillon 
821a396789SBoris Brezillon static uint32_t rgb_formats[] = {
83364a7bf5SPeter Rosin 	DRM_FORMAT_C8,
841a396789SBoris Brezillon 	DRM_FORMAT_XRGB4444,
851a396789SBoris Brezillon 	DRM_FORMAT_ARGB4444,
861a396789SBoris Brezillon 	DRM_FORMAT_RGBA4444,
871a396789SBoris Brezillon 	DRM_FORMAT_ARGB1555,
881a396789SBoris Brezillon 	DRM_FORMAT_RGB565,
891a396789SBoris Brezillon 	DRM_FORMAT_RGB888,
901a396789SBoris Brezillon 	DRM_FORMAT_XRGB8888,
911a396789SBoris Brezillon 	DRM_FORMAT_ARGB8888,
921a396789SBoris Brezillon 	DRM_FORMAT_RGBA8888,
931a396789SBoris Brezillon };
941a396789SBoris Brezillon 
951a396789SBoris Brezillon struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
961a396789SBoris Brezillon 	.formats = rgb_formats,
971a396789SBoris Brezillon 	.nformats = ARRAY_SIZE(rgb_formats),
981a396789SBoris Brezillon };
991a396789SBoris Brezillon 
1001a396789SBoris Brezillon static uint32_t rgb_and_yuv_formats[] = {
101364a7bf5SPeter Rosin 	DRM_FORMAT_C8,
1021a396789SBoris Brezillon 	DRM_FORMAT_XRGB4444,
1031a396789SBoris Brezillon 	DRM_FORMAT_ARGB4444,
1041a396789SBoris Brezillon 	DRM_FORMAT_RGBA4444,
1051a396789SBoris Brezillon 	DRM_FORMAT_ARGB1555,
1061a396789SBoris Brezillon 	DRM_FORMAT_RGB565,
1071a396789SBoris Brezillon 	DRM_FORMAT_RGB888,
1081a396789SBoris Brezillon 	DRM_FORMAT_XRGB8888,
1091a396789SBoris Brezillon 	DRM_FORMAT_ARGB8888,
1101a396789SBoris Brezillon 	DRM_FORMAT_RGBA8888,
1111a396789SBoris Brezillon 	DRM_FORMAT_AYUV,
1121a396789SBoris Brezillon 	DRM_FORMAT_YUYV,
1131a396789SBoris Brezillon 	DRM_FORMAT_UYVY,
1141a396789SBoris Brezillon 	DRM_FORMAT_YVYU,
1151a396789SBoris Brezillon 	DRM_FORMAT_VYUY,
1161a396789SBoris Brezillon 	DRM_FORMAT_NV21,
1171a396789SBoris Brezillon 	DRM_FORMAT_NV61,
1181a396789SBoris Brezillon 	DRM_FORMAT_YUV422,
1191a396789SBoris Brezillon 	DRM_FORMAT_YUV420,
1201a396789SBoris Brezillon };
1211a396789SBoris Brezillon 
1221a396789SBoris Brezillon struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
1231a396789SBoris Brezillon 	.formats = rgb_and_yuv_formats,
1241a396789SBoris Brezillon 	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
1251a396789SBoris Brezillon };
1261a396789SBoris Brezillon 
1271a396789SBoris Brezillon static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
1281a396789SBoris Brezillon {
1291a396789SBoris Brezillon 	switch (format) {
130364a7bf5SPeter Rosin 	case DRM_FORMAT_C8:
131364a7bf5SPeter Rosin 		*mode = ATMEL_HLCDC_C8_MODE;
132364a7bf5SPeter Rosin 		break;
1331a396789SBoris Brezillon 	case DRM_FORMAT_XRGB4444:
1341a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_XRGB4444_MODE;
1351a396789SBoris Brezillon 		break;
1361a396789SBoris Brezillon 	case DRM_FORMAT_ARGB4444:
1371a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_ARGB4444_MODE;
1381a396789SBoris Brezillon 		break;
1391a396789SBoris Brezillon 	case DRM_FORMAT_RGBA4444:
1401a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_RGBA4444_MODE;
1411a396789SBoris Brezillon 		break;
1421a396789SBoris Brezillon 	case DRM_FORMAT_RGB565:
1431a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_RGB565_MODE;
1441a396789SBoris Brezillon 		break;
1451a396789SBoris Brezillon 	case DRM_FORMAT_RGB888:
1461a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_RGB888_MODE;
1471a396789SBoris Brezillon 		break;
1481a396789SBoris Brezillon 	case DRM_FORMAT_ARGB1555:
1491a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_ARGB1555_MODE;
1501a396789SBoris Brezillon 		break;
1511a396789SBoris Brezillon 	case DRM_FORMAT_XRGB8888:
1521a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_XRGB8888_MODE;
1531a396789SBoris Brezillon 		break;
1541a396789SBoris Brezillon 	case DRM_FORMAT_ARGB8888:
1551a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_ARGB8888_MODE;
1561a396789SBoris Brezillon 		break;
1571a396789SBoris Brezillon 	case DRM_FORMAT_RGBA8888:
1581a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_RGBA8888_MODE;
1591a396789SBoris Brezillon 		break;
1601a396789SBoris Brezillon 	case DRM_FORMAT_AYUV:
1611a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_AYUV_MODE;
1621a396789SBoris Brezillon 		break;
1631a396789SBoris Brezillon 	case DRM_FORMAT_YUYV:
1641a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_YUYV_MODE;
1651a396789SBoris Brezillon 		break;
1661a396789SBoris Brezillon 	case DRM_FORMAT_UYVY:
1671a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_UYVY_MODE;
1681a396789SBoris Brezillon 		break;
1691a396789SBoris Brezillon 	case DRM_FORMAT_YVYU:
1701a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_YVYU_MODE;
1711a396789SBoris Brezillon 		break;
1721a396789SBoris Brezillon 	case DRM_FORMAT_VYUY:
1731a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_VYUY_MODE;
1741a396789SBoris Brezillon 		break;
1751a396789SBoris Brezillon 	case DRM_FORMAT_NV21:
1761a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_NV21_MODE;
1771a396789SBoris Brezillon 		break;
1781a396789SBoris Brezillon 	case DRM_FORMAT_NV61:
1791a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_NV61_MODE;
1801a396789SBoris Brezillon 		break;
1811a396789SBoris Brezillon 	case DRM_FORMAT_YUV420:
1821a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_YUV420_MODE;
1831a396789SBoris Brezillon 		break;
1841a396789SBoris Brezillon 	case DRM_FORMAT_YUV422:
1851a396789SBoris Brezillon 		*mode = ATMEL_HLCDC_YUV422_MODE;
1861a396789SBoris Brezillon 		break;
1871a396789SBoris Brezillon 	default:
1881a396789SBoris Brezillon 		return -ENOTSUPP;
1891a396789SBoris Brezillon 	}
1901a396789SBoris Brezillon 
1911a396789SBoris Brezillon 	return 0;
1921a396789SBoris Brezillon }
1931a396789SBoris Brezillon 
1941a396789SBoris Brezillon static u32 heo_downscaling_xcoef[] = {
1951a396789SBoris Brezillon 	0x11343311,
1961a396789SBoris Brezillon 	0x000000f7,
1971a396789SBoris Brezillon 	0x1635300c,
1981a396789SBoris Brezillon 	0x000000f9,
1991a396789SBoris Brezillon 	0x1b362c08,
2001a396789SBoris Brezillon 	0x000000fb,
2011a396789SBoris Brezillon 	0x1f372804,
2021a396789SBoris Brezillon 	0x000000fe,
2031a396789SBoris Brezillon 	0x24382400,
2041a396789SBoris Brezillon 	0x00000000,
2051a396789SBoris Brezillon 	0x28371ffe,
2061a396789SBoris Brezillon 	0x00000004,
2071a396789SBoris Brezillon 	0x2c361bfb,
2081a396789SBoris Brezillon 	0x00000008,
2091a396789SBoris Brezillon 	0x303516f9,
2101a396789SBoris Brezillon 	0x0000000c,
2111a396789SBoris Brezillon };
2121a396789SBoris Brezillon 
2131a396789SBoris Brezillon static u32 heo_downscaling_ycoef[] = {
2141a396789SBoris Brezillon 	0x00123737,
2151a396789SBoris Brezillon 	0x00173732,
2161a396789SBoris Brezillon 	0x001b382d,
2171a396789SBoris Brezillon 	0x001f3928,
2181a396789SBoris Brezillon 	0x00243824,
2191a396789SBoris Brezillon 	0x0028391f,
2201a396789SBoris Brezillon 	0x002d381b,
2211a396789SBoris Brezillon 	0x00323717,
2221a396789SBoris Brezillon };
2231a396789SBoris Brezillon 
2241a396789SBoris Brezillon static u32 heo_upscaling_xcoef[] = {
2251a396789SBoris Brezillon 	0xf74949f7,
2261a396789SBoris Brezillon 	0x00000000,
2271a396789SBoris Brezillon 	0xf55f33fb,
2281a396789SBoris Brezillon 	0x000000fe,
2291a396789SBoris Brezillon 	0xf5701efe,
2301a396789SBoris Brezillon 	0x000000ff,
2311a396789SBoris Brezillon 	0xf87c0dff,
2321a396789SBoris Brezillon 	0x00000000,
2331a396789SBoris Brezillon 	0x00800000,
2341a396789SBoris Brezillon 	0x00000000,
2351a396789SBoris Brezillon 	0x0d7cf800,
2361a396789SBoris Brezillon 	0x000000ff,
2371a396789SBoris Brezillon 	0x1e70f5ff,
2381a396789SBoris Brezillon 	0x000000fe,
2391a396789SBoris Brezillon 	0x335ff5fe,
2401a396789SBoris Brezillon 	0x000000fb,
2411a396789SBoris Brezillon };
2421a396789SBoris Brezillon 
2431a396789SBoris Brezillon static u32 heo_upscaling_ycoef[] = {
2441a396789SBoris Brezillon 	0x00004040,
2451a396789SBoris Brezillon 	0x00075920,
2461a396789SBoris Brezillon 	0x00056f0c,
2471a396789SBoris Brezillon 	0x00027b03,
2481a396789SBoris Brezillon 	0x00008000,
2491a396789SBoris Brezillon 	0x00037b02,
2501a396789SBoris Brezillon 	0x000c6f05,
2511a396789SBoris Brezillon 	0x00205907,
2521a396789SBoris Brezillon };
2531a396789SBoris Brezillon 
2549a45d33cSBoris Brezillon #define ATMEL_HLCDC_XPHIDEF	4
2559a45d33cSBoris Brezillon #define ATMEL_HLCDC_YPHIDEF	4
2569a45d33cSBoris Brezillon 
2579a45d33cSBoris Brezillon static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
2589a45d33cSBoris Brezillon 						  u32 dstsize,
2599a45d33cSBoris Brezillon 						  u32 phidef)
2609a45d33cSBoris Brezillon {
2619a45d33cSBoris Brezillon 	u32 factor, max_memsize;
2629a45d33cSBoris Brezillon 
2639a45d33cSBoris Brezillon 	factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
2649a45d33cSBoris Brezillon 	max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
2659a45d33cSBoris Brezillon 
2669a45d33cSBoris Brezillon 	if (max_memsize > srcsize - 1)
2679a45d33cSBoris Brezillon 		factor--;
2689a45d33cSBoris Brezillon 
2699a45d33cSBoris Brezillon 	return factor;
2709a45d33cSBoris Brezillon }
2719a45d33cSBoris Brezillon 
2729a45d33cSBoris Brezillon static void
2739a45d33cSBoris Brezillon atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
2749a45d33cSBoris Brezillon 				      const u32 *coeff_tab, int size,
2759a45d33cSBoris Brezillon 				      unsigned int cfg_offs)
2769a45d33cSBoris Brezillon {
2779a45d33cSBoris Brezillon 	int i;
2789a45d33cSBoris Brezillon 
2799a45d33cSBoris Brezillon 	for (i = 0; i < size; i++)
2809a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
2819a45d33cSBoris Brezillon 					    coeff_tab[i]);
2829a45d33cSBoris Brezillon }
2839a45d33cSBoris Brezillon 
2849a45d33cSBoris Brezillon void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
2859a45d33cSBoris Brezillon 				    struct atmel_hlcdc_plane_state *state)
2869a45d33cSBoris Brezillon {
2879a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
2889a45d33cSBoris Brezillon 	u32 xfactor, yfactor;
2899a45d33cSBoris Brezillon 
2909a45d33cSBoris Brezillon 	if (!desc->layout.scaler_config)
2919a45d33cSBoris Brezillon 		return;
2929a45d33cSBoris Brezillon 
2939a45d33cSBoris Brezillon 	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
2949a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer,
2959a45d33cSBoris Brezillon 					    desc->layout.scaler_config, 0);
2969a45d33cSBoris Brezillon 		return;
2979a45d33cSBoris Brezillon 	}
2989a45d33cSBoris Brezillon 
2999a45d33cSBoris Brezillon 	if (desc->layout.phicoeffs.x) {
3009a45d33cSBoris Brezillon 		xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
3019a45d33cSBoris Brezillon 							state->crtc_w,
3029a45d33cSBoris Brezillon 							ATMEL_HLCDC_XPHIDEF);
3039a45d33cSBoris Brezillon 
3049a45d33cSBoris Brezillon 		yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
3059a45d33cSBoris Brezillon 							state->crtc_h,
3069a45d33cSBoris Brezillon 							ATMEL_HLCDC_YPHIDEF);
3079a45d33cSBoris Brezillon 
3089a45d33cSBoris Brezillon 		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
3099a45d33cSBoris Brezillon 				state->crtc_w < state->src_w ?
3109a45d33cSBoris Brezillon 				heo_downscaling_xcoef :
3119a45d33cSBoris Brezillon 				heo_upscaling_xcoef,
3129a45d33cSBoris Brezillon 				ARRAY_SIZE(heo_upscaling_xcoef),
3139a45d33cSBoris Brezillon 				desc->layout.phicoeffs.x);
3149a45d33cSBoris Brezillon 
3159a45d33cSBoris Brezillon 		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
3169a45d33cSBoris Brezillon 				state->crtc_h < state->src_h ?
3179a45d33cSBoris Brezillon 				heo_downscaling_ycoef :
3189a45d33cSBoris Brezillon 				heo_upscaling_ycoef,
3199a45d33cSBoris Brezillon 				ARRAY_SIZE(heo_upscaling_ycoef),
3209a45d33cSBoris Brezillon 				desc->layout.phicoeffs.y);
3219a45d33cSBoris Brezillon 	} else {
3229a45d33cSBoris Brezillon 		xfactor = (1024 * state->src_w) / state->crtc_w;
3239a45d33cSBoris Brezillon 		yfactor = (1024 * state->src_h) / state->crtc_h;
3249a45d33cSBoris Brezillon 	}
3259a45d33cSBoris Brezillon 
3269a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
3279a45d33cSBoris Brezillon 				    ATMEL_HLCDC_LAYER_SCALER_ENABLE |
3289a45d33cSBoris Brezillon 				    ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
3299a45d33cSBoris Brezillon 								     yfactor));
3309a45d33cSBoris Brezillon }
3319a45d33cSBoris Brezillon 
3321a396789SBoris Brezillon static void
3331a396789SBoris Brezillon atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
3342389fc13SBoris Brezillon 				      struct atmel_hlcdc_plane_state *state)
3351a396789SBoris Brezillon {
3369a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
3371a396789SBoris Brezillon 
3389a45d33cSBoris Brezillon 	if (desc->layout.size)
3399a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
3409a45d33cSBoris Brezillon 					ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
3419a45d33cSBoris Brezillon 							       state->crtc_h));
3421a396789SBoris Brezillon 
3439a45d33cSBoris Brezillon 	if (desc->layout.memsize)
3449a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer,
3459a45d33cSBoris Brezillon 					desc->layout.memsize,
3469a45d33cSBoris Brezillon 					ATMEL_HLCDC_LAYER_SIZE(state->src_w,
3479a45d33cSBoris Brezillon 							       state->src_h));
3481a396789SBoris Brezillon 
3499a45d33cSBoris Brezillon 	if (desc->layout.pos)
3509a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
3519a45d33cSBoris Brezillon 					ATMEL_HLCDC_LAYER_POS(state->crtc_x,
3529a45d33cSBoris Brezillon 							      state->crtc_y));
3531a396789SBoris Brezillon 
3549a45d33cSBoris Brezillon 	atmel_hlcdc_plane_setup_scaler(plane, state);
3551a396789SBoris Brezillon }
3561a396789SBoris Brezillon 
3571a396789SBoris Brezillon static void
3581a396789SBoris Brezillon atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
3592389fc13SBoris Brezillon 					struct atmel_hlcdc_plane_state *state)
3601a396789SBoris Brezillon {
3619a45d33cSBoris Brezillon 	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
3629a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
363e2e287faSMaxime Ripard 	const struct drm_format_info *format = state->base.fb->format;
3649a45d33cSBoris Brezillon 
3659a45d33cSBoris Brezillon 	/*
3669a45d33cSBoris Brezillon 	 * Rotation optimization is not working on RGB888 (rotation is still
3679a45d33cSBoris Brezillon 	 * working but without any optimization).
3689a45d33cSBoris Brezillon 	 */
369e2e287faSMaxime Ripard 	if (format->format == DRM_FORMAT_RGB888)
3709a45d33cSBoris Brezillon 		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
3719a45d33cSBoris Brezillon 
3729a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
3739a45d33cSBoris Brezillon 				    cfg);
3749a45d33cSBoris Brezillon 
3759a45d33cSBoris Brezillon 	cfg = ATMEL_HLCDC_LAYER_DMA;
3761a396789SBoris Brezillon 
3771a396789SBoris Brezillon 	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
3781a396789SBoris Brezillon 		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
3791a396789SBoris Brezillon 		       ATMEL_HLCDC_LAYER_ITER;
3801a396789SBoris Brezillon 
381e2e287faSMaxime Ripard 		if (format->has_alpha)
3821a396789SBoris Brezillon 			cfg |= ATMEL_HLCDC_LAYER_LAEN;
3831a396789SBoris Brezillon 		else
3842389fc13SBoris Brezillon 			cfg |= ATMEL_HLCDC_LAYER_GAEN |
385cbb32079SClaudiu Beznea 			       ATMEL_HLCDC_LAYER_GA(state->base.alpha);
3861a396789SBoris Brezillon 	}
3871a396789SBoris Brezillon 
3889a45d33cSBoris Brezillon 	if (state->disc_h && state->disc_w)
3899a45d33cSBoris Brezillon 		cfg |= ATMEL_HLCDC_LAYER_DISCEN;
3901a396789SBoris Brezillon 
3919a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
3929a45d33cSBoris Brezillon 				    cfg);
3931a396789SBoris Brezillon }
3941a396789SBoris Brezillon 
3951a396789SBoris Brezillon static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
3962389fc13SBoris Brezillon 					struct atmel_hlcdc_plane_state *state)
3971a396789SBoris Brezillon {
3981a396789SBoris Brezillon 	u32 cfg;
3991a396789SBoris Brezillon 	int ret;
4001a396789SBoris Brezillon 
401438b74a5SVille Syrjälä 	ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,
4022389fc13SBoris Brezillon 					       &cfg);
4031a396789SBoris Brezillon 	if (ret)
4041a396789SBoris Brezillon 		return;
4051a396789SBoris Brezillon 
406438b74a5SVille Syrjälä 	if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||
407438b74a5SVille Syrjälä 	     state->base.fb->format->format == DRM_FORMAT_NV61) &&
408bd2ef25dSVille Syrjälä 	    drm_rotation_90_or_270(state->base.rotation))
4091a396789SBoris Brezillon 		cfg |= ATMEL_HLCDC_YUV422ROT;
4101a396789SBoris Brezillon 
4119a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_cfg(&plane->layer,
4129a45d33cSBoris Brezillon 				    ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
4131a396789SBoris Brezillon }
4141a396789SBoris Brezillon 
4150010ac3fSVille Syrjälä static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane,
4160010ac3fSVille Syrjälä 					  struct atmel_hlcdc_plane_state *state)
417364a7bf5SPeter Rosin {
4180010ac3fSVille Syrjälä 	struct drm_crtc *crtc = state->base.crtc;
419364a7bf5SPeter Rosin 	struct drm_color_lut *lut;
420364a7bf5SPeter Rosin 	int idx;
421364a7bf5SPeter Rosin 
422364a7bf5SPeter Rosin 	if (!crtc || !crtc->state)
423364a7bf5SPeter Rosin 		return;
424364a7bf5SPeter Rosin 
425364a7bf5SPeter Rosin 	if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
426364a7bf5SPeter Rosin 		return;
427364a7bf5SPeter Rosin 
428364a7bf5SPeter Rosin 	lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
429364a7bf5SPeter Rosin 
430364a7bf5SPeter Rosin 	for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {
431364a7bf5SPeter Rosin 		u32 val = ((lut->red << 8) & 0xff0000) |
432364a7bf5SPeter Rosin 			(lut->green & 0xff00) |
433364a7bf5SPeter Rosin 			(lut->blue >> 8);
434364a7bf5SPeter Rosin 
435364a7bf5SPeter Rosin 		atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);
436364a7bf5SPeter Rosin 	}
437364a7bf5SPeter Rosin }
438364a7bf5SPeter Rosin 
4391a396789SBoris Brezillon static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
4402389fc13SBoris Brezillon 					struct atmel_hlcdc_plane_state *state)
4411a396789SBoris Brezillon {
4429a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
4439a45d33cSBoris Brezillon 	struct drm_framebuffer *fb = state->base.fb;
4449a45d33cSBoris Brezillon 	u32 sr;
4451a396789SBoris Brezillon 	int i;
4461a396789SBoris Brezillon 
4479a45d33cSBoris Brezillon 	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
4481a396789SBoris Brezillon 
4492389fc13SBoris Brezillon 	for (i = 0; i < state->nplanes; i++) {
4509a45d33cSBoris Brezillon 		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
4519a45d33cSBoris Brezillon 
4529a45d33cSBoris Brezillon 		state->dscrs[i]->addr = gem->paddr + state->offsets[i];
4539a45d33cSBoris Brezillon 
4549a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_reg(&plane->layer,
4559a45d33cSBoris Brezillon 					    ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
4569a45d33cSBoris Brezillon 					    state->dscrs[i]->self);
4579a45d33cSBoris Brezillon 
4589a45d33cSBoris Brezillon 		if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
4599a45d33cSBoris Brezillon 			atmel_hlcdc_layer_write_reg(&plane->layer,
4609a45d33cSBoris Brezillon 					ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
4619a45d33cSBoris Brezillon 					state->dscrs[i]->addr);
4629a45d33cSBoris Brezillon 			atmel_hlcdc_layer_write_reg(&plane->layer,
4639a45d33cSBoris Brezillon 					ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
4649a45d33cSBoris Brezillon 					state->dscrs[i]->ctrl);
4659a45d33cSBoris Brezillon 			atmel_hlcdc_layer_write_reg(&plane->layer,
4669a45d33cSBoris Brezillon 					ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
4679a45d33cSBoris Brezillon 					state->dscrs[i]->self);
4681a396789SBoris Brezillon 		}
4691a396789SBoris Brezillon 
4709a45d33cSBoris Brezillon 		if (desc->layout.xstride[i])
4719a45d33cSBoris Brezillon 			atmel_hlcdc_layer_write_cfg(&plane->layer,
4729a45d33cSBoris Brezillon 						    desc->layout.xstride[i],
4739a45d33cSBoris Brezillon 						    state->xstride[i]);
4749a45d33cSBoris Brezillon 
4759a45d33cSBoris Brezillon 		if (desc->layout.pstride[i])
4769a45d33cSBoris Brezillon 			atmel_hlcdc_layer_write_cfg(&plane->layer,
4779a45d33cSBoris Brezillon 						    desc->layout.pstride[i],
4782389fc13SBoris Brezillon 						    state->pstride[i]);
4791a396789SBoris Brezillon 	}
4801a396789SBoris Brezillon }
4811a396789SBoris Brezillon 
482ebab87abSBoris Brezillon int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)
483ebab87abSBoris Brezillon {
484ebab87abSBoris Brezillon 	unsigned int ahb_load[2] = { };
485ebab87abSBoris Brezillon 	struct drm_plane *plane;
486ebab87abSBoris Brezillon 
487ebab87abSBoris Brezillon 	drm_atomic_crtc_state_for_each_plane(plane, c_state) {
488ebab87abSBoris Brezillon 		struct atmel_hlcdc_plane_state *plane_state;
489ebab87abSBoris Brezillon 		struct drm_plane_state *plane_s;
490ebab87abSBoris Brezillon 		unsigned int pixels, load = 0;
491ebab87abSBoris Brezillon 		int i;
492ebab87abSBoris Brezillon 
493ebab87abSBoris Brezillon 		plane_s = drm_atomic_get_plane_state(c_state->state, plane);
494ebab87abSBoris Brezillon 		if (IS_ERR(plane_s))
495ebab87abSBoris Brezillon 			return PTR_ERR(plane_s);
496ebab87abSBoris Brezillon 
497ebab87abSBoris Brezillon 		plane_state =
498ebab87abSBoris Brezillon 			drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);
499ebab87abSBoris Brezillon 
500ebab87abSBoris Brezillon 		pixels = (plane_state->src_w * plane_state->src_h) -
501ebab87abSBoris Brezillon 			 (plane_state->disc_w * plane_state->disc_h);
502ebab87abSBoris Brezillon 
503ebab87abSBoris Brezillon 		for (i = 0; i < plane_state->nplanes; i++)
504ebab87abSBoris Brezillon 			load += pixels * plane_state->bpp[i];
505ebab87abSBoris Brezillon 
506ebab87abSBoris Brezillon 		if (ahb_load[0] <= ahb_load[1])
507ebab87abSBoris Brezillon 			plane_state->ahb_id = 0;
508ebab87abSBoris Brezillon 		else
509ebab87abSBoris Brezillon 			plane_state->ahb_id = 1;
510ebab87abSBoris Brezillon 
511ebab87abSBoris Brezillon 		ahb_load[plane_state->ahb_id] += load;
512ebab87abSBoris Brezillon 	}
513ebab87abSBoris Brezillon 
514ebab87abSBoris Brezillon 	return 0;
515ebab87abSBoris Brezillon }
516ebab87abSBoris Brezillon 
5175957017dSBoris Brezillon int
5185957017dSBoris Brezillon atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
5195957017dSBoris Brezillon {
5205957017dSBoris Brezillon 	int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
5215957017dSBoris Brezillon 	const struct atmel_hlcdc_layer_cfg_layout *layout;
5225957017dSBoris Brezillon 	struct atmel_hlcdc_plane_state *primary_state;
5235957017dSBoris Brezillon 	struct drm_plane_state *primary_s;
5245957017dSBoris Brezillon 	struct atmel_hlcdc_plane *primary;
5255957017dSBoris Brezillon 	struct drm_plane *ovl;
5265957017dSBoris Brezillon 
5275957017dSBoris Brezillon 	primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
5285957017dSBoris Brezillon 	layout = &primary->layer.desc->layout;
5295957017dSBoris Brezillon 	if (!layout->disc_pos || !layout->disc_size)
5305957017dSBoris Brezillon 		return 0;
5315957017dSBoris Brezillon 
5325957017dSBoris Brezillon 	primary_s = drm_atomic_get_plane_state(c_state->state,
5335957017dSBoris Brezillon 					       &primary->base);
5345957017dSBoris Brezillon 	if (IS_ERR(primary_s))
5355957017dSBoris Brezillon 		return PTR_ERR(primary_s);
5365957017dSBoris Brezillon 
5375957017dSBoris Brezillon 	primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
5385957017dSBoris Brezillon 
5395957017dSBoris Brezillon 	drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
5405957017dSBoris Brezillon 		struct atmel_hlcdc_plane_state *ovl_state;
5415957017dSBoris Brezillon 		struct drm_plane_state *ovl_s;
5425957017dSBoris Brezillon 
5435957017dSBoris Brezillon 		if (ovl == c_state->crtc->primary)
5445957017dSBoris Brezillon 			continue;
5455957017dSBoris Brezillon 
5465957017dSBoris Brezillon 		ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
5475957017dSBoris Brezillon 		if (IS_ERR(ovl_s))
5485957017dSBoris Brezillon 			return PTR_ERR(ovl_s);
5495957017dSBoris Brezillon 
5505957017dSBoris Brezillon 		ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
5515957017dSBoris Brezillon 
552ac109c82SPeter Rosin 		if (!ovl_s->visible ||
553ac109c82SPeter Rosin 		    !ovl_s->fb ||
554e2e287faSMaxime Ripard 		    ovl_s->fb->format->has_alpha ||
5557f73c10bSMaxime Ripard 		    ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)
5565957017dSBoris Brezillon 			continue;
5575957017dSBoris Brezillon 
5585957017dSBoris Brezillon 		/* TODO: implement a smarter hidden area detection */
5595957017dSBoris Brezillon 		if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
5605957017dSBoris Brezillon 			continue;
5615957017dSBoris Brezillon 
5625957017dSBoris Brezillon 		disc_x = ovl_state->crtc_x;
5635957017dSBoris Brezillon 		disc_y = ovl_state->crtc_y;
5645957017dSBoris Brezillon 		disc_h = ovl_state->crtc_h;
5655957017dSBoris Brezillon 		disc_w = ovl_state->crtc_w;
5665957017dSBoris Brezillon 	}
5675957017dSBoris Brezillon 
5685957017dSBoris Brezillon 	primary_state->disc_x = disc_x;
5695957017dSBoris Brezillon 	primary_state->disc_y = disc_y;
5705957017dSBoris Brezillon 	primary_state->disc_w = disc_w;
5715957017dSBoris Brezillon 	primary_state->disc_h = disc_h;
5725957017dSBoris Brezillon 
5735957017dSBoris Brezillon 	return 0;
5745957017dSBoris Brezillon }
5755957017dSBoris Brezillon 
5765957017dSBoris Brezillon static void
5775957017dSBoris Brezillon atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
5785957017dSBoris Brezillon 				   struct atmel_hlcdc_plane_state *state)
5795957017dSBoris Brezillon {
5809a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_cfg_layout *layout;
5815957017dSBoris Brezillon 
5829a45d33cSBoris Brezillon 	layout = &plane->layer.desc->layout;
5839a45d33cSBoris Brezillon 	if (!layout->disc_pos || !layout->disc_size)
5845957017dSBoris Brezillon 		return;
5855957017dSBoris Brezillon 
5869a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
5879a45d33cSBoris Brezillon 				ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
5889a45d33cSBoris Brezillon 							   state->disc_y));
5895957017dSBoris Brezillon 
5909a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
5919a45d33cSBoris Brezillon 				ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
5929a45d33cSBoris Brezillon 							    state->disc_h));
5935957017dSBoris Brezillon }
5945957017dSBoris Brezillon 
5952389fc13SBoris Brezillon static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
5962389fc13SBoris Brezillon 					  struct drm_plane_state *s)
5971a396789SBoris Brezillon {
5981a396789SBoris Brezillon 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
5992389fc13SBoris Brezillon 	struct atmel_hlcdc_plane_state *state =
6002389fc13SBoris Brezillon 				drm_plane_state_to_atmel_hlcdc_plane_state(s);
6019a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
6022389fc13SBoris Brezillon 	struct drm_framebuffer *fb = state->base.fb;
6032389fc13SBoris Brezillon 	const struct drm_display_mode *mode;
6042389fc13SBoris Brezillon 	struct drm_crtc_state *crtc_state;
6051a396789SBoris Brezillon 	unsigned int tmp;
606ac109c82SPeter Rosin 	int ret;
6071a396789SBoris Brezillon 	int i;
6081a396789SBoris Brezillon 
6092389fc13SBoris Brezillon 	if (!state->base.crtc || !fb)
6102389fc13SBoris Brezillon 		return 0;
6112389fc13SBoris Brezillon 
612b47ff7e6SAndrzej Hajda 	crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc);
6132389fc13SBoris Brezillon 	mode = &crtc_state->adjusted_mode;
6142389fc13SBoris Brezillon 
615ac109c82SPeter Rosin 	ret = drm_atomic_helper_check_plane_state(s, crtc_state,
616ac109c82SPeter Rosin 						  (1 << 16) / 2048,
617ac109c82SPeter Rosin 						  INT_MAX, true, true);
618ac109c82SPeter Rosin 	if (ret || !s->visible)
619ac109c82SPeter Rosin 		return ret;
620ac109c82SPeter Rosin 
621ac109c82SPeter Rosin 	state->src_x = s->src.x1;
622ac109c82SPeter Rosin 	state->src_y = s->src.y1;
623ac109c82SPeter Rosin 	state->src_w = drm_rect_width(&s->src);
624ac109c82SPeter Rosin 	state->src_h = drm_rect_height(&s->src);
625ac109c82SPeter Rosin 	state->crtc_x = s->dst.x1;
626ac109c82SPeter Rosin 	state->crtc_y = s->dst.y1;
627ac109c82SPeter Rosin 	state->crtc_w = drm_rect_width(&s->dst);
628ac109c82SPeter Rosin 	state->crtc_h = drm_rect_height(&s->dst);
629ac109c82SPeter Rosin 
6302389fc13SBoris Brezillon 	if ((state->src_x | state->src_y | state->src_w | state->src_h) &
6311a396789SBoris Brezillon 	    SUBPIXEL_MASK)
6321a396789SBoris Brezillon 		return -EINVAL;
6331a396789SBoris Brezillon 
6342389fc13SBoris Brezillon 	state->src_x >>= 16;
6352389fc13SBoris Brezillon 	state->src_y >>= 16;
6362389fc13SBoris Brezillon 	state->src_w >>= 16;
6372389fc13SBoris Brezillon 	state->src_h >>= 16;
6381a396789SBoris Brezillon 
639bcb0b461SVille Syrjälä 	state->nplanes = fb->format->num_planes;
6409a45d33cSBoris Brezillon 	if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
6411a396789SBoris Brezillon 		return -EINVAL;
6421a396789SBoris Brezillon 
6432389fc13SBoris Brezillon 	for (i = 0; i < state->nplanes; i++) {
6441a396789SBoris Brezillon 		unsigned int offset = 0;
645f3e9632cSMaxime Ripard 		int xdiv = i ? fb->format->hsub : 1;
646f3e9632cSMaxime Ripard 		int ydiv = i ? fb->format->vsub : 1;
6471a396789SBoris Brezillon 
648353c8598SVille Syrjälä 		state->bpp[i] = fb->format->cpp[i];
6492389fc13SBoris Brezillon 		if (!state->bpp[i])
6501a396789SBoris Brezillon 			return -EINVAL;
6511a396789SBoris Brezillon 
652c2c446adSRobert Foss 		switch (state->base.rotation & DRM_MODE_ROTATE_MASK) {
653c2c446adSRobert Foss 		case DRM_MODE_ROTATE_90:
654ac109c82SPeter Rosin 			offset = (state->src_y / ydiv) *
6552389fc13SBoris Brezillon 				 fb->pitches[i];
656ac109c82SPeter Rosin 			offset += ((state->src_x + state->src_w - 1) /
6578cdb00a5SPeter Rosin 				   xdiv) * state->bpp[i];
658ac109c82SPeter Rosin 			state->xstride[i] = -(((state->src_h - 1) / ydiv) *
6598cdb00a5SPeter Rosin 					    fb->pitches[i]) -
6608cdb00a5SPeter Rosin 					  (2 * state->bpp[i]);
6618cdb00a5SPeter Rosin 			state->pstride[i] = fb->pitches[i] - state->bpp[i];
6621a396789SBoris Brezillon 			break;
663c2c446adSRobert Foss 		case DRM_MODE_ROTATE_180:
664ac109c82SPeter Rosin 			offset = ((state->src_y + state->src_h - 1) /
6652389fc13SBoris Brezillon 				  ydiv) * fb->pitches[i];
666ac109c82SPeter Rosin 			offset += ((state->src_x + state->src_w - 1) /
6672389fc13SBoris Brezillon 				   xdiv) * state->bpp[i];
668ac109c82SPeter Rosin 			state->xstride[i] = ((((state->src_w - 1) / xdiv) - 1) *
6692389fc13SBoris Brezillon 					   state->bpp[i]) - fb->pitches[i];
6702389fc13SBoris Brezillon 			state->pstride[i] = -2 * state->bpp[i];
6711a396789SBoris Brezillon 			break;
672c2c446adSRobert Foss 		case DRM_MODE_ROTATE_270:
673ac109c82SPeter Rosin 			offset = ((state->src_y + state->src_h - 1) /
6748cdb00a5SPeter Rosin 				  ydiv) * fb->pitches[i];
675ac109c82SPeter Rosin 			offset += (state->src_x / xdiv) * state->bpp[i];
676ac109c82SPeter Rosin 			state->xstride[i] = ((state->src_h - 1) / ydiv) *
6772389fc13SBoris Brezillon 					  fb->pitches[i];
6788cdb00a5SPeter Rosin 			state->pstride[i] = -fb->pitches[i] - state->bpp[i];
6791a396789SBoris Brezillon 			break;
680c2c446adSRobert Foss 		case DRM_MODE_ROTATE_0:
6811a396789SBoris Brezillon 		default:
682ac109c82SPeter Rosin 			offset = (state->src_y / ydiv) * fb->pitches[i];
683ac109c82SPeter Rosin 			offset += (state->src_x / xdiv) * state->bpp[i];
6842389fc13SBoris Brezillon 			state->xstride[i] = fb->pitches[i] -
685ac109c82SPeter Rosin 					  ((state->src_w / xdiv) *
6862389fc13SBoris Brezillon 					   state->bpp[i]);
6872389fc13SBoris Brezillon 			state->pstride[i] = 0;
6881a396789SBoris Brezillon 			break;
6891a396789SBoris Brezillon 		}
6901a396789SBoris Brezillon 
6912389fc13SBoris Brezillon 		state->offsets[i] = offset + fb->offsets[i];
6921a396789SBoris Brezillon 	}
6931a396789SBoris Brezillon 
694ac109c82SPeter Rosin 	/*
695ac109c82SPeter Rosin 	 * Swap width and size in case of 90 or 270 degrees rotation
696ac109c82SPeter Rosin 	 */
697ac109c82SPeter Rosin 	if (drm_rotation_90_or_270(state->base.rotation)) {
698ac109c82SPeter Rosin 		tmp = state->src_w;
699ac109c82SPeter Rosin 		state->src_w = state->src_h;
700ac109c82SPeter Rosin 		state->src_h = tmp;
701ac109c82SPeter Rosin 	}
7021a396789SBoris Brezillon 
7039a45d33cSBoris Brezillon 	if (!desc->layout.size &&
7042389fc13SBoris Brezillon 	    (mode->hdisplay != state->crtc_w ||
7052389fc13SBoris Brezillon 	     mode->vdisplay != state->crtc_h))
7062389fc13SBoris Brezillon 		return -EINVAL;
7071a396789SBoris Brezillon 
7082389fc13SBoris Brezillon 	if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
7099a45d33cSBoris Brezillon 	    (!desc->layout.memsize ||
710e2e287faSMaxime Ripard 	     state->base.fb->format->has_alpha))
7112389fc13SBoris Brezillon 		return -EINVAL;
7121a396789SBoris Brezillon 
7131a396789SBoris Brezillon 	return 0;
7141a396789SBoris Brezillon }
7151a396789SBoris Brezillon 
716ac109c82SPeter Rosin static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
717ac109c82SPeter Rosin 					     struct drm_plane_state *old_state)
718ac109c82SPeter Rosin {
719ac109c82SPeter Rosin 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
720ac109c82SPeter Rosin 
721ac109c82SPeter Rosin 	/* Disable interrupts */
722ac109c82SPeter Rosin 	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
723ac109c82SPeter Rosin 				    0xffffffff);
724ac109c82SPeter Rosin 
725ac109c82SPeter Rosin 	/* Disable the layer */
726ac109c82SPeter Rosin 	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
727ac109c82SPeter Rosin 				    ATMEL_HLCDC_LAYER_RST |
728ac109c82SPeter Rosin 				    ATMEL_HLCDC_LAYER_A2Q |
729ac109c82SPeter Rosin 				    ATMEL_HLCDC_LAYER_UPDATE);
730ac109c82SPeter Rosin 
731ac109c82SPeter Rosin 	/* Clear all pending interrupts */
732ac109c82SPeter Rosin 	atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
733ac109c82SPeter Rosin }
734ac109c82SPeter Rosin 
7352389fc13SBoris Brezillon static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
7362389fc13SBoris Brezillon 					    struct drm_plane_state *old_s)
7372389fc13SBoris Brezillon {
7382389fc13SBoris Brezillon 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
7392389fc13SBoris Brezillon 	struct atmel_hlcdc_plane_state *state =
7402389fc13SBoris Brezillon 			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
7419a45d33cSBoris Brezillon 	u32 sr;
7422389fc13SBoris Brezillon 
7432389fc13SBoris Brezillon 	if (!p->state->crtc || !p->state->fb)
7442389fc13SBoris Brezillon 		return;
7452389fc13SBoris Brezillon 
746ac109c82SPeter Rosin 	if (!state->base.visible) {
747ac109c82SPeter Rosin 		atmel_hlcdc_plane_atomic_disable(p, old_s);
748ac109c82SPeter Rosin 		return;
749ac109c82SPeter Rosin 	}
750ac109c82SPeter Rosin 
7512389fc13SBoris Brezillon 	atmel_hlcdc_plane_update_pos_and_size(plane, state);
7522389fc13SBoris Brezillon 	atmel_hlcdc_plane_update_general_settings(plane, state);
7532389fc13SBoris Brezillon 	atmel_hlcdc_plane_update_format(plane, state);
7540010ac3fSVille Syrjälä 	atmel_hlcdc_plane_update_clut(plane, state);
7552389fc13SBoris Brezillon 	atmel_hlcdc_plane_update_buffers(plane, state);
7565957017dSBoris Brezillon 	atmel_hlcdc_plane_update_disc_area(plane, state);
7572389fc13SBoris Brezillon 
7589a45d33cSBoris Brezillon 	/* Enable the overrun interrupts. */
7599a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
7609a45d33cSBoris Brezillon 				    ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
7619a45d33cSBoris Brezillon 				    ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
7629a45d33cSBoris Brezillon 				    ATMEL_HLCDC_LAYER_OVR_IRQ(2));
7639a45d33cSBoris Brezillon 
7649a45d33cSBoris Brezillon 	/* Apply the new config at the next SOF event. */
7659a45d33cSBoris Brezillon 	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
7669a45d33cSBoris Brezillon 	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
7679a45d33cSBoris Brezillon 			ATMEL_HLCDC_LAYER_UPDATE |
7689a45d33cSBoris Brezillon 			(sr & ATMEL_HLCDC_LAYER_EN ?
7699a45d33cSBoris Brezillon 			 ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
7702389fc13SBoris Brezillon }
7712389fc13SBoris Brezillon 
7727f73c10bSMaxime Ripard static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)
7731a396789SBoris Brezillon {
7749a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
7751a396789SBoris Brezillon 
7761a396789SBoris Brezillon 	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
7777f73c10bSMaxime Ripard 	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
7787f73c10bSMaxime Ripard 		int ret;
7797f73c10bSMaxime Ripard 
7807f73c10bSMaxime Ripard 		ret = drm_plane_create_alpha_property(&plane->base);
7817f73c10bSMaxime Ripard 		if (ret)
7827f73c10bSMaxime Ripard 			return ret;
7837f73c10bSMaxime Ripard 	}
7841a396789SBoris Brezillon 
7859fcf2b3cSStefan Agner 	if (desc->layout.xstride[0] && desc->layout.pstride[0]) {
7869fe58f01SVille Syrjälä 		int ret;
7879fe58f01SVille Syrjälä 
7889fe58f01SVille Syrjälä 		ret = drm_plane_create_rotation_property(&plane->base,
789c2c446adSRobert Foss 							 DRM_MODE_ROTATE_0,
790c2c446adSRobert Foss 							 DRM_MODE_ROTATE_0 |
791c2c446adSRobert Foss 							 DRM_MODE_ROTATE_90 |
792c2c446adSRobert Foss 							 DRM_MODE_ROTATE_180 |
793c2c446adSRobert Foss 							 DRM_MODE_ROTATE_270);
7949fe58f01SVille Syrjälä 		if (ret)
7959fe58f01SVille Syrjälä 			return ret;
7969fe58f01SVille Syrjälä 	}
7971a396789SBoris Brezillon 
7981a396789SBoris Brezillon 	if (desc->layout.csc) {
7991a396789SBoris Brezillon 		/*
8001a396789SBoris Brezillon 		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
8011a396789SBoris Brezillon 		 * userspace modify these factors (using a BLOB property ?).
8021a396789SBoris Brezillon 		 */
8039a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer,
8049a45d33cSBoris Brezillon 					    desc->layout.csc,
8051a396789SBoris Brezillon 					    0x4c900091);
8069a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer,
8079a45d33cSBoris Brezillon 					    desc->layout.csc + 1,
8081a396789SBoris Brezillon 					    0x7a5f5090);
8099a45d33cSBoris Brezillon 		atmel_hlcdc_layer_write_cfg(&plane->layer,
8109a45d33cSBoris Brezillon 					    desc->layout.csc + 2,
8111a396789SBoris Brezillon 					    0x40040890);
8121a396789SBoris Brezillon 	}
8139fe58f01SVille Syrjälä 
8149fe58f01SVille Syrjälä 	return 0;
8151a396789SBoris Brezillon }
8161a396789SBoris Brezillon 
8179a45d33cSBoris Brezillon void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
8189a45d33cSBoris Brezillon {
8199a45d33cSBoris Brezillon 	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
8209a45d33cSBoris Brezillon 	u32 isr;
8219a45d33cSBoris Brezillon 
8229a45d33cSBoris Brezillon 	isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
8239a45d33cSBoris Brezillon 
8249a45d33cSBoris Brezillon 	/*
8259a45d33cSBoris Brezillon 	 * There's not much we can do in case of overrun except informing
8269a45d33cSBoris Brezillon 	 * the user. However, we are in interrupt context here, hence the
8279a45d33cSBoris Brezillon 	 * use of dev_dbg().
8289a45d33cSBoris Brezillon 	 */
8299a45d33cSBoris Brezillon 	if (isr &
8309a45d33cSBoris Brezillon 	    (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
8319a45d33cSBoris Brezillon 	     ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
8329a45d33cSBoris Brezillon 		dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
8339a45d33cSBoris Brezillon 			desc->name);
8349a45d33cSBoris Brezillon }
8359a45d33cSBoris Brezillon 
836d95a8e7bSArvind Yadav static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
8372389fc13SBoris Brezillon 	.atomic_check = atmel_hlcdc_plane_atomic_check,
8382389fc13SBoris Brezillon 	.atomic_update = atmel_hlcdc_plane_atomic_update,
8392389fc13SBoris Brezillon 	.atomic_disable = atmel_hlcdc_plane_atomic_disable,
8402389fc13SBoris Brezillon };
8412389fc13SBoris Brezillon 
8429a45d33cSBoris Brezillon static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
8439a45d33cSBoris Brezillon 					 struct atmel_hlcdc_plane_state *state)
8449a45d33cSBoris Brezillon {
8459a45d33cSBoris Brezillon 	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
8469a45d33cSBoris Brezillon 	int i;
8479a45d33cSBoris Brezillon 
8489a45d33cSBoris Brezillon 	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
8499a45d33cSBoris Brezillon 		struct atmel_hlcdc_dma_channel_dscr *dscr;
8509a45d33cSBoris Brezillon 		dma_addr_t dscr_dma;
8519a45d33cSBoris Brezillon 
8529a45d33cSBoris Brezillon 		dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
8539a45d33cSBoris Brezillon 		if (!dscr)
8549a45d33cSBoris Brezillon 			goto err;
8559a45d33cSBoris Brezillon 
8569a45d33cSBoris Brezillon 		dscr->addr = 0;
8579a45d33cSBoris Brezillon 		dscr->next = dscr_dma;
8589a45d33cSBoris Brezillon 		dscr->self = dscr_dma;
8599a45d33cSBoris Brezillon 		dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
8609a45d33cSBoris Brezillon 
8619a45d33cSBoris Brezillon 		state->dscrs[i] = dscr;
8629a45d33cSBoris Brezillon 	}
8639a45d33cSBoris Brezillon 
8649a45d33cSBoris Brezillon 	return 0;
8659a45d33cSBoris Brezillon 
8669a45d33cSBoris Brezillon err:
8679a45d33cSBoris Brezillon 	for (i--; i >= 0; i--) {
8689a45d33cSBoris Brezillon 		dma_pool_free(dc->dscrpool, state->dscrs[i],
8699a45d33cSBoris Brezillon 			      state->dscrs[i]->self);
8709a45d33cSBoris Brezillon 	}
8719a45d33cSBoris Brezillon 
8729a45d33cSBoris Brezillon 	return -ENOMEM;
8739a45d33cSBoris Brezillon }
8749a45d33cSBoris Brezillon 
8752389fc13SBoris Brezillon static void atmel_hlcdc_plane_reset(struct drm_plane *p)
8762389fc13SBoris Brezillon {
8772389fc13SBoris Brezillon 	struct atmel_hlcdc_plane_state *state;
8782389fc13SBoris Brezillon 
8792389fc13SBoris Brezillon 	if (p->state) {
8802389fc13SBoris Brezillon 		state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
8812389fc13SBoris Brezillon 
8822389fc13SBoris Brezillon 		if (state->base.fb)
883f3a73544SCihangir Akturk 			drm_framebuffer_put(state->base.fb);
8842389fc13SBoris Brezillon 
8852389fc13SBoris Brezillon 		kfree(state);
8862389fc13SBoris Brezillon 		p->state = NULL;
8872389fc13SBoris Brezillon 	}
8882389fc13SBoris Brezillon 
8892389fc13SBoris Brezillon 	state = kzalloc(sizeof(*state), GFP_KERNEL);
8902389fc13SBoris Brezillon 	if (state) {
8919a45d33cSBoris Brezillon 		if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
8929a45d33cSBoris Brezillon 			kfree(state);
8939a45d33cSBoris Brezillon 			dev_err(p->dev->dev,
8949a45d33cSBoris Brezillon 				"Failed to allocate initial plane state\n");
8959a45d33cSBoris Brezillon 			return;
8969a45d33cSBoris Brezillon 		}
897e2512172SAlexandru Gheorghe 		__drm_atomic_helper_plane_reset(p, &state->base);
8982389fc13SBoris Brezillon 	}
8992389fc13SBoris Brezillon }
9002389fc13SBoris Brezillon 
9012389fc13SBoris Brezillon static struct drm_plane_state *
9022389fc13SBoris Brezillon atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
9032389fc13SBoris Brezillon {
9042389fc13SBoris Brezillon 	struct atmel_hlcdc_plane_state *state =
9052389fc13SBoris Brezillon 			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
9062389fc13SBoris Brezillon 	struct atmel_hlcdc_plane_state *copy;
9072389fc13SBoris Brezillon 
9082389fc13SBoris Brezillon 	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
9092389fc13SBoris Brezillon 	if (!copy)
9102389fc13SBoris Brezillon 		return NULL;
9112389fc13SBoris Brezillon 
9129a45d33cSBoris Brezillon 	if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
9139a45d33cSBoris Brezillon 		kfree(copy);
9149a45d33cSBoris Brezillon 		return NULL;
9159a45d33cSBoris Brezillon 	}
9165957017dSBoris Brezillon 
9172389fc13SBoris Brezillon 	if (copy->base.fb)
918f3a73544SCihangir Akturk 		drm_framebuffer_get(copy->base.fb);
9192389fc13SBoris Brezillon 
9202389fc13SBoris Brezillon 	return &copy->base;
9212389fc13SBoris Brezillon }
9222389fc13SBoris Brezillon 
9239a45d33cSBoris Brezillon static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
9242389fc13SBoris Brezillon 						   struct drm_plane_state *s)
9252389fc13SBoris Brezillon {
9262389fc13SBoris Brezillon 	struct atmel_hlcdc_plane_state *state =
9272389fc13SBoris Brezillon 			drm_plane_state_to_atmel_hlcdc_plane_state(s);
9289a45d33cSBoris Brezillon 	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
9299a45d33cSBoris Brezillon 	int i;
9309a45d33cSBoris Brezillon 
9319a45d33cSBoris Brezillon 	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
9329a45d33cSBoris Brezillon 		dma_pool_free(dc->dscrpool, state->dscrs[i],
9339a45d33cSBoris Brezillon 			      state->dscrs[i]->self);
9349a45d33cSBoris Brezillon 	}
9352389fc13SBoris Brezillon 
9362389fc13SBoris Brezillon 	if (s->fb)
937f3a73544SCihangir Akturk 		drm_framebuffer_put(s->fb);
9382389fc13SBoris Brezillon 
9392389fc13SBoris Brezillon 	kfree(state);
9402389fc13SBoris Brezillon }
9412389fc13SBoris Brezillon 
942d95a8e7bSArvind Yadav static const struct drm_plane_funcs layer_plane_funcs = {
9432389fc13SBoris Brezillon 	.update_plane = drm_atomic_helper_update_plane,
9442389fc13SBoris Brezillon 	.disable_plane = drm_atomic_helper_disable_plane,
945952a08a2SVille Syrjälä 	.destroy = drm_plane_cleanup,
9462389fc13SBoris Brezillon 	.reset = atmel_hlcdc_plane_reset,
9472389fc13SBoris Brezillon 	.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
9482389fc13SBoris Brezillon 	.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
9491a396789SBoris Brezillon };
9501a396789SBoris Brezillon 
9519a45d33cSBoris Brezillon static int atmel_hlcdc_plane_create(struct drm_device *dev,
9527f73c10bSMaxime Ripard 				    const struct atmel_hlcdc_layer_desc *desc)
9531a396789SBoris Brezillon {
9549a45d33cSBoris Brezillon 	struct atmel_hlcdc_dc *dc = dev->dev_private;
9551a396789SBoris Brezillon 	struct atmel_hlcdc_plane *plane;
9561a396789SBoris Brezillon 	enum drm_plane_type type;
9571a396789SBoris Brezillon 	int ret;
9581a396789SBoris Brezillon 
9591a396789SBoris Brezillon 	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
9601a396789SBoris Brezillon 	if (!plane)
9619a45d33cSBoris Brezillon 		return -ENOMEM;
9621a396789SBoris Brezillon 
9639a45d33cSBoris Brezillon 	atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
9641a396789SBoris Brezillon 
9651a396789SBoris Brezillon 	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
9661a396789SBoris Brezillon 		type = DRM_PLANE_TYPE_PRIMARY;
9671a396789SBoris Brezillon 	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
9681a396789SBoris Brezillon 		type = DRM_PLANE_TYPE_CURSOR;
9691a396789SBoris Brezillon 	else
9701a396789SBoris Brezillon 		type = DRM_PLANE_TYPE_OVERLAY;
9711a396789SBoris Brezillon 
9721a396789SBoris Brezillon 	ret = drm_universal_plane_init(dev, &plane->base, 0,
9731a396789SBoris Brezillon 				       &layer_plane_funcs,
9741a396789SBoris Brezillon 				       desc->formats->formats,
975e6fc3b68SBen Widawsky 				       desc->formats->nformats,
976e6fc3b68SBen Widawsky 				       NULL, type, NULL);
9771a396789SBoris Brezillon 	if (ret)
9789a45d33cSBoris Brezillon 		return ret;
9791a396789SBoris Brezillon 
9802389fc13SBoris Brezillon 	drm_plane_helper_add(&plane->base,
9812389fc13SBoris Brezillon 			     &atmel_hlcdc_layer_plane_helper_funcs);
9822389fc13SBoris Brezillon 
9831a396789SBoris Brezillon 	/* Set default property values*/
9847f73c10bSMaxime Ripard 	ret = atmel_hlcdc_plane_init_properties(plane);
9859fe58f01SVille Syrjälä 	if (ret)
9869a45d33cSBoris Brezillon 		return ret;
9871a396789SBoris Brezillon 
9889a45d33cSBoris Brezillon 	dc->layers[desc->id] = &plane->layer;
9899a45d33cSBoris Brezillon 
9909a45d33cSBoris Brezillon 	return 0;
9911a396789SBoris Brezillon }
9921a396789SBoris Brezillon 
9939a45d33cSBoris Brezillon int atmel_hlcdc_create_planes(struct drm_device *dev)
9941a396789SBoris Brezillon {
9951a396789SBoris Brezillon 	struct atmel_hlcdc_dc *dc = dev->dev_private;
9961a396789SBoris Brezillon 	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
9971a396789SBoris Brezillon 	int nlayers = dc->desc->nlayers;
9989a45d33cSBoris Brezillon 	int i, ret;
9991a396789SBoris Brezillon 
10009a45d33cSBoris Brezillon 	dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
10019a45d33cSBoris Brezillon 				sizeof(struct atmel_hlcdc_dma_channel_dscr),
10029a45d33cSBoris Brezillon 				sizeof(u64), 0);
10039a45d33cSBoris Brezillon 	if (!dc->dscrpool)
10049a45d33cSBoris Brezillon 		return -ENOMEM;
10059a45d33cSBoris Brezillon 
10061a396789SBoris Brezillon 	for (i = 0; i < nlayers; i++) {
10079a45d33cSBoris Brezillon 		if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
10089a45d33cSBoris Brezillon 		    descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
10099a45d33cSBoris Brezillon 		    descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
10101a396789SBoris Brezillon 			continue;
10111a396789SBoris Brezillon 
10127f73c10bSMaxime Ripard 		ret = atmel_hlcdc_plane_create(dev, &descs[i]);
10139a45d33cSBoris Brezillon 		if (ret)
10149a45d33cSBoris Brezillon 			return ret;
10151a396789SBoris Brezillon 	}
10161a396789SBoris Brezillon 
10179a45d33cSBoris Brezillon 	return 0;
10181a396789SBoris Brezillon }
1019