1 /*
2  * Copyright (C) 2014 Free Electrons
3  * Copyright (C) 2014 Atmel
4  *
5  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "atmel_hlcdc_dc.h"
21 
22 #define SUBPIXEL_MASK			0xffff
23 
24 static uint32_t rgb_formats[] = {
25 	DRM_FORMAT_XRGB4444,
26 	DRM_FORMAT_ARGB4444,
27 	DRM_FORMAT_RGBA4444,
28 	DRM_FORMAT_ARGB1555,
29 	DRM_FORMAT_RGB565,
30 	DRM_FORMAT_RGB888,
31 	DRM_FORMAT_XRGB8888,
32 	DRM_FORMAT_ARGB8888,
33 	DRM_FORMAT_RGBA8888,
34 };
35 
36 struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
37 	.formats = rgb_formats,
38 	.nformats = ARRAY_SIZE(rgb_formats),
39 };
40 
41 static uint32_t rgb_and_yuv_formats[] = {
42 	DRM_FORMAT_XRGB4444,
43 	DRM_FORMAT_ARGB4444,
44 	DRM_FORMAT_RGBA4444,
45 	DRM_FORMAT_ARGB1555,
46 	DRM_FORMAT_RGB565,
47 	DRM_FORMAT_RGB888,
48 	DRM_FORMAT_XRGB8888,
49 	DRM_FORMAT_ARGB8888,
50 	DRM_FORMAT_RGBA8888,
51 	DRM_FORMAT_AYUV,
52 	DRM_FORMAT_YUYV,
53 	DRM_FORMAT_UYVY,
54 	DRM_FORMAT_YVYU,
55 	DRM_FORMAT_VYUY,
56 	DRM_FORMAT_NV21,
57 	DRM_FORMAT_NV61,
58 	DRM_FORMAT_YUV422,
59 	DRM_FORMAT_YUV420,
60 };
61 
62 struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
63 	.formats = rgb_and_yuv_formats,
64 	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
65 };
66 
67 static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
68 {
69 	switch (format) {
70 	case DRM_FORMAT_XRGB4444:
71 		*mode = ATMEL_HLCDC_XRGB4444_MODE;
72 		break;
73 	case DRM_FORMAT_ARGB4444:
74 		*mode = ATMEL_HLCDC_ARGB4444_MODE;
75 		break;
76 	case DRM_FORMAT_RGBA4444:
77 		*mode = ATMEL_HLCDC_RGBA4444_MODE;
78 		break;
79 	case DRM_FORMAT_RGB565:
80 		*mode = ATMEL_HLCDC_RGB565_MODE;
81 		break;
82 	case DRM_FORMAT_RGB888:
83 		*mode = ATMEL_HLCDC_RGB888_MODE;
84 		break;
85 	case DRM_FORMAT_ARGB1555:
86 		*mode = ATMEL_HLCDC_ARGB1555_MODE;
87 		break;
88 	case DRM_FORMAT_XRGB8888:
89 		*mode = ATMEL_HLCDC_XRGB8888_MODE;
90 		break;
91 	case DRM_FORMAT_ARGB8888:
92 		*mode = ATMEL_HLCDC_ARGB8888_MODE;
93 		break;
94 	case DRM_FORMAT_RGBA8888:
95 		*mode = ATMEL_HLCDC_RGBA8888_MODE;
96 		break;
97 	case DRM_FORMAT_AYUV:
98 		*mode = ATMEL_HLCDC_AYUV_MODE;
99 		break;
100 	case DRM_FORMAT_YUYV:
101 		*mode = ATMEL_HLCDC_YUYV_MODE;
102 		break;
103 	case DRM_FORMAT_UYVY:
104 		*mode = ATMEL_HLCDC_UYVY_MODE;
105 		break;
106 	case DRM_FORMAT_YVYU:
107 		*mode = ATMEL_HLCDC_YVYU_MODE;
108 		break;
109 	case DRM_FORMAT_VYUY:
110 		*mode = ATMEL_HLCDC_VYUY_MODE;
111 		break;
112 	case DRM_FORMAT_NV21:
113 		*mode = ATMEL_HLCDC_NV21_MODE;
114 		break;
115 	case DRM_FORMAT_NV61:
116 		*mode = ATMEL_HLCDC_NV61_MODE;
117 		break;
118 	case DRM_FORMAT_YUV420:
119 		*mode = ATMEL_HLCDC_YUV420_MODE;
120 		break;
121 	case DRM_FORMAT_YUV422:
122 		*mode = ATMEL_HLCDC_YUV422_MODE;
123 		break;
124 	default:
125 		return -ENOTSUPP;
126 	}
127 
128 	return 0;
129 }
130 
131 static bool atmel_hlcdc_format_embedds_alpha(u32 format)
132 {
133 	int i;
134 
135 	for (i = 0; i < sizeof(format); i++) {
136 		char tmp = (format >> (8 * i)) & 0xff;
137 
138 		if (tmp == 'A')
139 			return true;
140 	}
141 
142 	return false;
143 }
144 
145 static u32 heo_downscaling_xcoef[] = {
146 	0x11343311,
147 	0x000000f7,
148 	0x1635300c,
149 	0x000000f9,
150 	0x1b362c08,
151 	0x000000fb,
152 	0x1f372804,
153 	0x000000fe,
154 	0x24382400,
155 	0x00000000,
156 	0x28371ffe,
157 	0x00000004,
158 	0x2c361bfb,
159 	0x00000008,
160 	0x303516f9,
161 	0x0000000c,
162 };
163 
164 static u32 heo_downscaling_ycoef[] = {
165 	0x00123737,
166 	0x00173732,
167 	0x001b382d,
168 	0x001f3928,
169 	0x00243824,
170 	0x0028391f,
171 	0x002d381b,
172 	0x00323717,
173 };
174 
175 static u32 heo_upscaling_xcoef[] = {
176 	0xf74949f7,
177 	0x00000000,
178 	0xf55f33fb,
179 	0x000000fe,
180 	0xf5701efe,
181 	0x000000ff,
182 	0xf87c0dff,
183 	0x00000000,
184 	0x00800000,
185 	0x00000000,
186 	0x0d7cf800,
187 	0x000000ff,
188 	0x1e70f5ff,
189 	0x000000fe,
190 	0x335ff5fe,
191 	0x000000fb,
192 };
193 
194 static u32 heo_upscaling_ycoef[] = {
195 	0x00004040,
196 	0x00075920,
197 	0x00056f0c,
198 	0x00027b03,
199 	0x00008000,
200 	0x00037b02,
201 	0x000c6f05,
202 	0x00205907,
203 };
204 
205 static void
206 atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
207 				struct atmel_hlcdc_plane_update_req *req)
208 {
209 	const struct atmel_hlcdc_layer_cfg_layout *layout =
210 						&plane->layer.desc->layout;
211 
212 	if (layout->size)
213 		atmel_hlcdc_layer_update_cfg(&plane->layer,
214 					     layout->size,
215 					     0xffffffff,
216 					     (req->crtc_w - 1) |
217 					     ((req->crtc_h - 1) << 16));
218 
219 	if (layout->memsize)
220 		atmel_hlcdc_layer_update_cfg(&plane->layer,
221 					     layout->memsize,
222 					     0xffffffff,
223 					     (req->src_w - 1) |
224 					     ((req->src_h - 1) << 16));
225 
226 	if (layout->pos)
227 		atmel_hlcdc_layer_update_cfg(&plane->layer,
228 					     layout->pos,
229 					     0xffffffff,
230 					     req->crtc_x |
231 					     (req->crtc_y  << 16));
232 
233 	/* TODO: rework the rescaling part */
234 	if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
235 		u32 factor_reg = 0;
236 
237 		if (req->crtc_w != req->src_w) {
238 			int i;
239 			u32 factor;
240 			u32 *coeff_tab = heo_upscaling_xcoef;
241 			u32 max_memsize;
242 
243 			if (req->crtc_w < req->src_w)
244 				coeff_tab = heo_downscaling_xcoef;
245 			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
246 				atmel_hlcdc_layer_update_cfg(&plane->layer,
247 							     17 + i,
248 							     0xffffffff,
249 							     coeff_tab[i]);
250 			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
251 				 req->crtc_w;
252 			factor++;
253 			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
254 				      2048;
255 			if (max_memsize > req->src_w)
256 				factor--;
257 			factor_reg |= factor | 0x80000000;
258 		}
259 
260 		if (req->crtc_h != req->src_h) {
261 			int i;
262 			u32 factor;
263 			u32 *coeff_tab = heo_upscaling_ycoef;
264 			u32 max_memsize;
265 
266 			if (req->crtc_w < req->src_w)
267 				coeff_tab = heo_downscaling_ycoef;
268 			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
269 				atmel_hlcdc_layer_update_cfg(&plane->layer,
270 							     33 + i,
271 							     0xffffffff,
272 							     coeff_tab[i]);
273 			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
274 				 req->crtc_w;
275 			factor++;
276 			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
277 				      2048;
278 			if (max_memsize > req->src_w)
279 				factor--;
280 			factor_reg |= (factor << 16) | 0x80000000;
281 		}
282 
283 		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
284 					     factor_reg);
285 	}
286 }
287 
288 static void
289 atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
290 				struct atmel_hlcdc_plane_update_req *req)
291 {
292 	const struct atmel_hlcdc_layer_cfg_layout *layout =
293 						&plane->layer.desc->layout;
294 	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
295 
296 	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
297 		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
298 		       ATMEL_HLCDC_LAYER_ITER;
299 
300 		if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
301 			cfg |= ATMEL_HLCDC_LAYER_LAEN;
302 		else
303 			cfg |= ATMEL_HLCDC_LAYER_GAEN;
304 	}
305 
306 	atmel_hlcdc_layer_update_cfg(&plane->layer,
307 				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
308 				     ATMEL_HLCDC_LAYER_DMA_BLEN_MASK,
309 				     ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16);
310 
311 	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
312 				     ATMEL_HLCDC_LAYER_ITER2BL |
313 				     ATMEL_HLCDC_LAYER_ITER |
314 				     ATMEL_HLCDC_LAYER_GAEN |
315 				     ATMEL_HLCDC_LAYER_LAEN |
316 				     ATMEL_HLCDC_LAYER_OVR |
317 				     ATMEL_HLCDC_LAYER_DMA, cfg);
318 }
319 
320 static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
321 				struct atmel_hlcdc_plane_update_req *req)
322 {
323 	u32 cfg;
324 	int ret;
325 
326 	ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &cfg);
327 	if (ret)
328 		return;
329 
330 	if ((req->fb->pixel_format == DRM_FORMAT_YUV422 ||
331 	     req->fb->pixel_format == DRM_FORMAT_NV61) &&
332 	    (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
333 		cfg |= ATMEL_HLCDC_YUV422ROT;
334 
335 	atmel_hlcdc_layer_update_cfg(&plane->layer,
336 				     ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
337 				     0xffffffff,
338 				     cfg);
339 
340 	/*
341 	 * Rotation optimization is not working on RGB888 (rotation is still
342 	 * working but without any optimization).
343 	 */
344 	if (req->fb->pixel_format == DRM_FORMAT_RGB888)
345 		cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
346 	else
347 		cfg = 0;
348 
349 	atmel_hlcdc_layer_update_cfg(&plane->layer,
350 				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
351 				     ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
352 }
353 
354 static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
355 				struct atmel_hlcdc_plane_update_req *req)
356 {
357 	struct atmel_hlcdc_layer *layer = &plane->layer;
358 	const struct atmel_hlcdc_layer_cfg_layout *layout =
359 							&layer->desc->layout;
360 	int i;
361 
362 	atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
363 
364 	for (i = 0; i < req->nplanes; i++) {
365 		if (layout->xstride[i]) {
366 			atmel_hlcdc_layer_update_cfg(&plane->layer,
367 						layout->xstride[i],
368 						0xffffffff,
369 						req->xstride[i]);
370 		}
371 
372 		if (layout->pstride[i]) {
373 			atmel_hlcdc_layer_update_cfg(&plane->layer,
374 						layout->pstride[i],
375 						0xffffffff,
376 						req->pstride[i]);
377 		}
378 	}
379 }
380 
381 static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
382 				struct atmel_hlcdc_plane_update_req *req,
383 				const struct drm_display_mode *mode)
384 {
385 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
386 	const struct atmel_hlcdc_layer_cfg_layout *layout =
387 						&plane->layer.desc->layout;
388 
389 	if (!layout->size &&
390 	    (mode->hdisplay != req->crtc_w ||
391 	     mode->vdisplay != req->crtc_h))
392 		return -EINVAL;
393 
394 	if (plane->layer.desc->max_height &&
395 	    req->crtc_h > plane->layer.desc->max_height)
396 		return -EINVAL;
397 
398 	if (plane->layer.desc->max_width &&
399 	    req->crtc_w > plane->layer.desc->max_width)
400 		return -EINVAL;
401 
402 	if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
403 	    (!layout->memsize ||
404 	     atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
405 		return -EINVAL;
406 
407 	if (req->crtc_x < 0 || req->crtc_y < 0)
408 		return -EINVAL;
409 
410 	if (req->crtc_w + req->crtc_x > mode->hdisplay ||
411 	    req->crtc_h + req->crtc_y > mode->vdisplay)
412 		return -EINVAL;
413 
414 	return 0;
415 }
416 
417 int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
418 				struct atmel_hlcdc_plane_update_req *req,
419 				const struct drm_display_mode *mode)
420 {
421 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
422 	unsigned int patched_crtc_w;
423 	unsigned int patched_crtc_h;
424 	unsigned int patched_src_w;
425 	unsigned int patched_src_h;
426 	unsigned int tmp;
427 	int x_offset = 0;
428 	int y_offset = 0;
429 	int hsub = 1;
430 	int vsub = 1;
431 	int i;
432 
433 	if ((req->src_x | req->src_y | req->src_w | req->src_h) &
434 	    SUBPIXEL_MASK)
435 		return -EINVAL;
436 
437 	req->src_x >>= 16;
438 	req->src_y >>= 16;
439 	req->src_w >>= 16;
440 	req->src_h >>= 16;
441 
442 	req->nplanes = drm_format_num_planes(req->fb->pixel_format);
443 	if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
444 		return -EINVAL;
445 
446 	/*
447 	 * Swap width and size in case of 90 or 270 degrees rotation
448 	 */
449 	if (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
450 		tmp = req->crtc_w;
451 		req->crtc_w = req->crtc_h;
452 		req->crtc_h = tmp;
453 		tmp = req->src_w;
454 		req->src_w = req->src_h;
455 		req->src_h = tmp;
456 	}
457 
458 	if (req->crtc_x + req->crtc_w > mode->hdisplay)
459 		patched_crtc_w = mode->hdisplay - req->crtc_x;
460 	else
461 		patched_crtc_w = req->crtc_w;
462 
463 	if (req->crtc_x < 0) {
464 		patched_crtc_w += req->crtc_x;
465 		x_offset = -req->crtc_x;
466 		req->crtc_x = 0;
467 	}
468 
469 	if (req->crtc_y + req->crtc_h > mode->vdisplay)
470 		patched_crtc_h = mode->vdisplay - req->crtc_y;
471 	else
472 		patched_crtc_h = req->crtc_h;
473 
474 	if (req->crtc_y < 0) {
475 		patched_crtc_h += req->crtc_y;
476 		y_offset = -req->crtc_y;
477 		req->crtc_y = 0;
478 	}
479 
480 	patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
481 					  req->crtc_w);
482 	patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
483 					  req->crtc_h);
484 
485 	hsub = drm_format_horz_chroma_subsampling(req->fb->pixel_format);
486 	vsub = drm_format_vert_chroma_subsampling(req->fb->pixel_format);
487 
488 	for (i = 0; i < req->nplanes; i++) {
489 		unsigned int offset = 0;
490 		int xdiv = i ? hsub : 1;
491 		int ydiv = i ? vsub : 1;
492 
493 		req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
494 		if (!req->bpp[i])
495 			return -EINVAL;
496 
497 		switch (plane->rotation & 0xf) {
498 		case BIT(DRM_ROTATE_90):
499 			offset = ((y_offset + req->src_y + patched_src_w - 1) /
500 				  ydiv) * req->fb->pitches[i];
501 			offset += ((x_offset + req->src_x) / xdiv) *
502 				  req->bpp[i];
503 			req->xstride[i] = ((patched_src_w - 1) / ydiv) *
504 					  req->fb->pitches[i];
505 			req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
506 			break;
507 		case BIT(DRM_ROTATE_180):
508 			offset = ((y_offset + req->src_y + patched_src_h - 1) /
509 				  ydiv) * req->fb->pitches[i];
510 			offset += ((x_offset + req->src_x + patched_src_w - 1) /
511 				   xdiv) * req->bpp[i];
512 			req->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
513 					   req->bpp[i]) - req->fb->pitches[i];
514 			req->pstride[i] = -2 * req->bpp[i];
515 			break;
516 		case BIT(DRM_ROTATE_270):
517 			offset = ((y_offset + req->src_y) / ydiv) *
518 				 req->fb->pitches[i];
519 			offset += ((x_offset + req->src_x + patched_src_h - 1) /
520 				   xdiv) * req->bpp[i];
521 			req->xstride[i] = -(((patched_src_w - 1) / ydiv) *
522 					    req->fb->pitches[i]) -
523 					  (2 * req->bpp[i]);
524 			req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
525 			break;
526 		case BIT(DRM_ROTATE_0):
527 		default:
528 			offset = ((y_offset + req->src_y) / ydiv) *
529 				 req->fb->pitches[i];
530 			offset += ((x_offset + req->src_x) / xdiv) *
531 				  req->bpp[i];
532 			req->xstride[i] = req->fb->pitches[i] -
533 					  ((patched_src_w / xdiv) *
534 					   req->bpp[i]);
535 			req->pstride[i] = 0;
536 			break;
537 		}
538 
539 		req->offsets[i] = offset + req->fb->offsets[i];
540 	}
541 
542 	req->src_w = patched_src_w;
543 	req->src_h = patched_src_h;
544 	req->crtc_w = patched_crtc_w;
545 	req->crtc_h = patched_crtc_h;
546 
547 	return atmel_hlcdc_plane_check_update_req(p, req, mode);
548 }
549 
550 int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
551 				struct atmel_hlcdc_plane_update_req *req)
552 {
553 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
554 	int ret;
555 
556 	ret = atmel_hlcdc_layer_update_start(&plane->layer);
557 	if (ret)
558 		return ret;
559 
560 	atmel_hlcdc_plane_update_pos_and_size(plane, req);
561 	atmel_hlcdc_plane_update_general_settings(plane, req);
562 	atmel_hlcdc_plane_update_format(plane, req);
563 	atmel_hlcdc_plane_update_buffers(plane, req);
564 
565 	atmel_hlcdc_layer_update_commit(&plane->layer);
566 
567 	return 0;
568 }
569 
570 int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p,
571 				       struct drm_crtc *crtc,
572 				       struct drm_framebuffer *fb,
573 				       int crtc_x, int crtc_y,
574 				       unsigned int crtc_w,
575 				       unsigned int crtc_h,
576 				       uint32_t src_x, uint32_t src_y,
577 				       uint32_t src_w, uint32_t src_h,
578 				       const struct drm_display_mode *mode)
579 {
580 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
581 	struct atmel_hlcdc_plane_update_req req;
582 	int ret = 0;
583 
584 	memset(&req, 0, sizeof(req));
585 	req.crtc_x = crtc_x;
586 	req.crtc_y = crtc_y;
587 	req.crtc_w = crtc_w;
588 	req.crtc_h = crtc_h;
589 	req.src_x = src_x;
590 	req.src_y = src_y;
591 	req.src_w = src_w;
592 	req.src_h = src_h;
593 	req.fb = fb;
594 
595 	ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req, mode);
596 	if (ret)
597 		return ret;
598 
599 	if (!req.crtc_h || !req.crtc_w)
600 		return atmel_hlcdc_layer_disable(&plane->layer);
601 
602 	return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
603 }
604 
605 static int atmel_hlcdc_plane_update(struct drm_plane *p,
606 				    struct drm_crtc *crtc,
607 				    struct drm_framebuffer *fb,
608 				    int crtc_x, int crtc_y,
609 				    unsigned int crtc_w, unsigned int crtc_h,
610 				    uint32_t src_x, uint32_t src_y,
611 				    uint32_t src_w, uint32_t src_h)
612 {
613 	return atmel_hlcdc_plane_update_with_mode(p, crtc, fb, crtc_x, crtc_y,
614 						  crtc_w, crtc_h, src_x, src_y,
615 						  src_w, src_h, &crtc->hwmode);
616 }
617 
618 static int atmel_hlcdc_plane_disable(struct drm_plane *p)
619 {
620 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
621 
622 	return atmel_hlcdc_layer_disable(&plane->layer);
623 }
624 
625 static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
626 {
627 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
628 
629 	if (plane->base.fb)
630 		drm_framebuffer_unreference(plane->base.fb);
631 
632 	atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
633 
634 	drm_plane_cleanup(p);
635 	devm_kfree(p->dev->dev, plane);
636 }
637 
638 static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
639 				       u8 alpha)
640 {
641 	atmel_hlcdc_layer_update_start(&plane->layer);
642 	atmel_hlcdc_layer_update_cfg(&plane->layer,
643 				     plane->layer.desc->layout.general_config,
644 				     ATMEL_HLCDC_LAYER_GA_MASK,
645 				     alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
646 	atmel_hlcdc_layer_update_commit(&plane->layer);
647 
648 	return 0;
649 }
650 
651 static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
652 					  unsigned int rotation)
653 {
654 	plane->rotation = rotation;
655 
656 	return 0;
657 }
658 
659 static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
660 					  struct drm_property *property,
661 					  uint64_t value)
662 {
663 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
664 	struct atmel_hlcdc_plane_properties *props = plane->properties;
665 
666 	if (property == props->alpha)
667 		atmel_hlcdc_plane_set_alpha(plane, value);
668 	else if (property == props->rotation)
669 		atmel_hlcdc_plane_set_rotation(plane, value);
670 	else
671 		return -EINVAL;
672 
673 	return 0;
674 }
675 
676 static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
677 				const struct atmel_hlcdc_layer_desc *desc,
678 				struct atmel_hlcdc_plane_properties *props)
679 {
680 	struct regmap *regmap = plane->layer.hlcdc->regmap;
681 
682 	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
683 	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
684 		drm_object_attach_property(&plane->base.base,
685 					   props->alpha, 255);
686 
687 		/* Set default alpha value */
688 		regmap_update_bits(regmap,
689 				desc->regs_offset +
690 				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
691 				ATMEL_HLCDC_LAYER_GA_MASK,
692 				ATMEL_HLCDC_LAYER_GA_MASK);
693 	}
694 
695 	if (desc->layout.xstride && desc->layout.pstride)
696 		drm_object_attach_property(&plane->base.base,
697 					   props->rotation,
698 					   BIT(DRM_ROTATE_0));
699 
700 	if (desc->layout.csc) {
701 		/*
702 		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
703 		 * userspace modify these factors (using a BLOB property ?).
704 		 */
705 		regmap_write(regmap,
706 			     desc->regs_offset +
707 			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
708 			     0x4c900091);
709 		regmap_write(regmap,
710 			     desc->regs_offset +
711 			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
712 			     0x7a5f5090);
713 		regmap_write(regmap,
714 			     desc->regs_offset +
715 			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
716 			     0x40040890);
717 	}
718 }
719 
720 static struct drm_plane_funcs layer_plane_funcs = {
721 	.update_plane = atmel_hlcdc_plane_update,
722 	.disable_plane = atmel_hlcdc_plane_disable,
723 	.set_property = atmel_hlcdc_plane_set_property,
724 	.destroy = atmel_hlcdc_plane_destroy,
725 };
726 
727 static struct atmel_hlcdc_plane *
728 atmel_hlcdc_plane_create(struct drm_device *dev,
729 			 const struct atmel_hlcdc_layer_desc *desc,
730 			 struct atmel_hlcdc_plane_properties *props)
731 {
732 	struct atmel_hlcdc_plane *plane;
733 	enum drm_plane_type type;
734 	int ret;
735 
736 	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
737 	if (!plane)
738 		return ERR_PTR(-ENOMEM);
739 
740 	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
741 	if (ret)
742 		return ERR_PTR(ret);
743 
744 	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
745 		type = DRM_PLANE_TYPE_PRIMARY;
746 	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
747 		type = DRM_PLANE_TYPE_CURSOR;
748 	else
749 		type = DRM_PLANE_TYPE_OVERLAY;
750 
751 	ret = drm_universal_plane_init(dev, &plane->base, 0,
752 				       &layer_plane_funcs,
753 				       desc->formats->formats,
754 				       desc->formats->nformats, type);
755 	if (ret)
756 		return ERR_PTR(ret);
757 
758 	/* Set default property values*/
759 	atmel_hlcdc_plane_init_properties(plane, desc, props);
760 
761 	return plane;
762 }
763 
764 static struct atmel_hlcdc_plane_properties *
765 atmel_hlcdc_plane_create_properties(struct drm_device *dev)
766 {
767 	struct atmel_hlcdc_plane_properties *props;
768 
769 	props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
770 	if (!props)
771 		return ERR_PTR(-ENOMEM);
772 
773 	props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
774 	if (!props->alpha)
775 		return ERR_PTR(-ENOMEM);
776 
777 	props->rotation = drm_mode_create_rotation_property(dev,
778 						BIT(DRM_ROTATE_0) |
779 						BIT(DRM_ROTATE_90) |
780 						BIT(DRM_ROTATE_180) |
781 						BIT(DRM_ROTATE_270));
782 	if (!props->rotation)
783 		return ERR_PTR(-ENOMEM);
784 
785 	return props;
786 }
787 
788 struct atmel_hlcdc_planes *
789 atmel_hlcdc_create_planes(struct drm_device *dev)
790 {
791 	struct atmel_hlcdc_dc *dc = dev->dev_private;
792 	struct atmel_hlcdc_plane_properties *props;
793 	struct atmel_hlcdc_planes *planes;
794 	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
795 	int nlayers = dc->desc->nlayers;
796 	int i;
797 
798 	planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
799 	if (!planes)
800 		return ERR_PTR(-ENOMEM);
801 
802 	for (i = 0; i < nlayers; i++) {
803 		if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
804 			planes->noverlays++;
805 	}
806 
807 	if (planes->noverlays) {
808 		planes->overlays = devm_kzalloc(dev->dev,
809 						planes->noverlays *
810 						sizeof(*planes->overlays),
811 						GFP_KERNEL);
812 		if (!planes->overlays)
813 			return ERR_PTR(-ENOMEM);
814 	}
815 
816 	props = atmel_hlcdc_plane_create_properties(dev);
817 	if (IS_ERR(props))
818 		return ERR_CAST(props);
819 
820 	planes->noverlays = 0;
821 	for (i = 0; i < nlayers; i++) {
822 		struct atmel_hlcdc_plane *plane;
823 
824 		if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
825 			continue;
826 
827 		plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
828 		if (IS_ERR(plane))
829 			return ERR_CAST(plane);
830 
831 		plane->properties = props;
832 
833 		switch (descs[i].type) {
834 		case ATMEL_HLCDC_BASE_LAYER:
835 			if (planes->primary)
836 				return ERR_PTR(-EINVAL);
837 			planes->primary = plane;
838 			break;
839 
840 		case ATMEL_HLCDC_OVERLAY_LAYER:
841 			planes->overlays[planes->noverlays++] = plane;
842 			break;
843 
844 		case ATMEL_HLCDC_CURSOR_LAYER:
845 			if (planes->cursor)
846 				return ERR_PTR(-EINVAL);
847 			planes->cursor = plane;
848 			break;
849 
850 		default:
851 			break;
852 		}
853 	}
854 
855 	return planes;
856 }
857