• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * rcar_du_plane.c  --  R-Car Display Unit Planes
3  *
4  * Copyright (C) 2013-2014 Renesas Electronics Corporation
5  *
6  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13 
14 #include <drm/drmP.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_crtc_helper.h>
17 #include <drm/drm_fb_cma_helper.h>
18 #include <drm/drm_gem_cma_helper.h>
19 
20 #include "rcar_du_drv.h"
21 #include "rcar_du_kms.h"
22 #include "rcar_du_plane.h"
23 #include "rcar_du_regs.h"
24 
25 #define RCAR_DU_COLORKEY_NONE		(0 << 24)
26 #define RCAR_DU_COLORKEY_SOURCE		(1 << 24)
27 #define RCAR_DU_COLORKEY_MASK		(1 << 24)
28 
29 struct rcar_du_kms_plane {
30 	struct drm_plane plane;
31 	struct rcar_du_plane *hwplane;
32 };
33 
to_rcar_plane(struct drm_plane * plane)34 static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
35 {
36 	return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
37 }
38 
rcar_du_plane_read(struct rcar_du_group * rgrp,unsigned int index,u32 reg)39 static u32 rcar_du_plane_read(struct rcar_du_group *rgrp,
40 			      unsigned int index, u32 reg)
41 {
42 	return rcar_du_read(rgrp->dev,
43 			    rgrp->mmio_offset + index * PLANE_OFF + reg);
44 }
45 
rcar_du_plane_write(struct rcar_du_group * rgrp,unsigned int index,u32 reg,u32 data)46 static void rcar_du_plane_write(struct rcar_du_group *rgrp,
47 				unsigned int index, u32 reg, u32 data)
48 {
49 	rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
50 		      data);
51 }
52 
rcar_du_plane_reserve(struct rcar_du_plane * plane,const struct rcar_du_format_info * format)53 int rcar_du_plane_reserve(struct rcar_du_plane *plane,
54 			  const struct rcar_du_format_info *format)
55 {
56 	struct rcar_du_group *rgrp = plane->group;
57 	unsigned int i;
58 	int ret = -EBUSY;
59 
60 	mutex_lock(&rgrp->planes.lock);
61 
62 	for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) {
63 		if (!(rgrp->planes.free & (1 << i)))
64 			continue;
65 
66 		if (format->planes == 1 ||
67 		    rgrp->planes.free & (1 << ((i + 1) % 8)))
68 			break;
69 	}
70 
71 	if (i == ARRAY_SIZE(rgrp->planes.planes))
72 		goto done;
73 
74 	rgrp->planes.free &= ~(1 << i);
75 	if (format->planes == 2)
76 		rgrp->planes.free &= ~(1 << ((i + 1) % 8));
77 
78 	plane->hwindex = i;
79 
80 	ret = 0;
81 
82 done:
83 	mutex_unlock(&rgrp->planes.lock);
84 	return ret;
85 }
86 
rcar_du_plane_release(struct rcar_du_plane * plane)87 void rcar_du_plane_release(struct rcar_du_plane *plane)
88 {
89 	struct rcar_du_group *rgrp = plane->group;
90 
91 	if (plane->hwindex == -1)
92 		return;
93 
94 	mutex_lock(&rgrp->planes.lock);
95 	rgrp->planes.free |= 1 << plane->hwindex;
96 	if (plane->format->planes == 2)
97 		rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8);
98 	mutex_unlock(&rgrp->planes.lock);
99 
100 	plane->hwindex = -1;
101 }
102 
rcar_du_plane_update_base(struct rcar_du_plane * plane)103 void rcar_du_plane_update_base(struct rcar_du_plane *plane)
104 {
105 	struct rcar_du_group *rgrp = plane->group;
106 	unsigned int index = plane->hwindex;
107 	u32 mwr;
108 
109 	/* Memory pitch (expressed in pixels) */
110 	if (plane->format->planes == 2)
111 		mwr = plane->pitch;
112 	else
113 		mwr = plane->pitch * 8 / plane->format->bpp;
114 
115 	rcar_du_plane_write(rgrp, index, PnMWR, mwr);
116 
117 	/* The Y position is expressed in raster line units and must be doubled
118 	 * for 32bpp formats, according to the R8A7790 datasheet. No mention of
119 	 * doubling the Y position is found in the R8A7779 datasheet, but the
120 	 * rule seems to apply there as well.
121 	 *
122 	 * Similarly, for the second plane, NV12 and NV21 formats seem to
123 	 * require a halved Y position value.
124 	 */
125 	rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x);
126 	rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y *
127 			    (plane->format->bpp == 32 ? 2 : 1));
128 	rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]);
129 
130 	if (plane->format->planes == 2) {
131 		index = (index + 1) % 8;
132 
133 		rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x);
134 		rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y *
135 				    (plane->format->bpp == 16 ? 2 : 1) / 2);
136 		rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]);
137 	}
138 }
139 
rcar_du_plane_compute_base(struct rcar_du_plane * plane,struct drm_framebuffer * fb)140 void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
141 				struct drm_framebuffer *fb)
142 {
143 	struct drm_gem_cma_object *gem;
144 
145 	plane->pitch = fb->pitches[0];
146 
147 	gem = drm_fb_cma_get_gem_obj(fb, 0);
148 	plane->dma[0] = gem->paddr + fb->offsets[0];
149 
150 	if (plane->format->planes == 2) {
151 		gem = drm_fb_cma_get_gem_obj(fb, 1);
152 		plane->dma[1] = gem->paddr + fb->offsets[1];
153 	}
154 }
155 
rcar_du_plane_setup_mode(struct rcar_du_plane * plane,unsigned int index)156 static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
157 				     unsigned int index)
158 {
159 	struct rcar_du_group *rgrp = plane->group;
160 	u32 colorkey;
161 	u32 pnmr;
162 
163 	/* The PnALPHAR register controls alpha-blending in 16bpp formats
164 	 * (ARGB1555 and XRGB1555).
165 	 *
166 	 * For ARGB, set the alpha value to 0, and enable alpha-blending when
167 	 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
168 	 *
169 	 * For XRGB, set the alpha value to the plane-wide alpha value and
170 	 * enable alpha-blending regardless of the X bit value.
171 	 */
172 	if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
173 		rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
174 	else
175 		rcar_du_plane_write(rgrp, index, PnALPHAR,
176 				    PnALPHAR_ABIT_X | plane->alpha);
177 
178 	pnmr = PnMR_BM_MD | plane->format->pnmr;
179 
180 	/* Disable color keying when requested. YUV formats have the
181 	 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
182 	 * automatically.
183 	 */
184 	if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
185 		pnmr |= PnMR_SPIM_TP_OFF;
186 
187 	/* For packed YUV formats we need to select the U/V order. */
188 	if (plane->format->fourcc == DRM_FORMAT_YUYV)
189 		pnmr |= PnMR_YCDF_YUYV;
190 
191 	rcar_du_plane_write(rgrp, index, PnMR, pnmr);
192 
193 	switch (plane->format->fourcc) {
194 	case DRM_FORMAT_RGB565:
195 		colorkey = ((plane->colorkey & 0xf80000) >> 8)
196 			 | ((plane->colorkey & 0x00fc00) >> 5)
197 			 | ((plane->colorkey & 0x0000f8) >> 3);
198 		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
199 		break;
200 
201 	case DRM_FORMAT_ARGB1555:
202 	case DRM_FORMAT_XRGB1555:
203 		colorkey = ((plane->colorkey & 0xf80000) >> 9)
204 			 | ((plane->colorkey & 0x00f800) >> 6)
205 			 | ((plane->colorkey & 0x0000f8) >> 3);
206 		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
207 		break;
208 
209 	case DRM_FORMAT_XRGB8888:
210 	case DRM_FORMAT_ARGB8888:
211 		rcar_du_plane_write(rgrp, index, PnTC3R,
212 				    PnTC3R_CODE | (plane->colorkey & 0xffffff));
213 		break;
214 	}
215 }
216 
__rcar_du_plane_setup(struct rcar_du_plane * plane,unsigned int index)217 static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
218 				  unsigned int index)
219 {
220 	struct rcar_du_group *rgrp = plane->group;
221 	u32 ddcr2 = PnDDCR2_CODE;
222 	u32 ddcr4;
223 
224 	/* Data format
225 	 *
226 	 * The data format is selected by the DDDF field in PnMR and the EDF
227 	 * field in DDCR4.
228 	 */
229 	ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4);
230 	ddcr4 &= ~PnDDCR4_EDF_MASK;
231 	ddcr4 |= plane->format->edf | PnDDCR4_CODE;
232 
233 	rcar_du_plane_setup_mode(plane, index);
234 
235 	if (plane->format->planes == 2) {
236 		if (plane->hwindex != index) {
237 			if (plane->format->fourcc == DRM_FORMAT_NV12 ||
238 			    plane->format->fourcc == DRM_FORMAT_NV21)
239 				ddcr2 |= PnDDCR2_Y420;
240 
241 			if (plane->format->fourcc == DRM_FORMAT_NV21)
242 				ddcr2 |= PnDDCR2_NV21;
243 
244 			ddcr2 |= PnDDCR2_DIVU;
245 		} else {
246 			ddcr2 |= PnDDCR2_DIVY;
247 		}
248 	}
249 
250 	rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
251 	rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
252 
253 	/* Destination position and size */
254 	rcar_du_plane_write(rgrp, index, PnDSXR, plane->width);
255 	rcar_du_plane_write(rgrp, index, PnDSYR, plane->height);
256 	rcar_du_plane_write(rgrp, index, PnDPXR, plane->dst_x);
257 	rcar_du_plane_write(rgrp, index, PnDPYR, plane->dst_y);
258 
259 	/* Wrap-around and blinking, disabled */
260 	rcar_du_plane_write(rgrp, index, PnWASPR, 0);
261 	rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
262 	rcar_du_plane_write(rgrp, index, PnBTR, 0);
263 	rcar_du_plane_write(rgrp, index, PnMLR, 0);
264 }
265 
rcar_du_plane_setup(struct rcar_du_plane * plane)266 void rcar_du_plane_setup(struct rcar_du_plane *plane)
267 {
268 	__rcar_du_plane_setup(plane, plane->hwindex);
269 	if (plane->format->planes == 2)
270 		__rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
271 
272 	rcar_du_plane_update_base(plane);
273 }
274 
275 static int
rcar_du_plane_update(struct drm_plane * plane,struct drm_crtc * crtc,struct drm_framebuffer * fb,int crtc_x,int crtc_y,unsigned int crtc_w,unsigned int crtc_h,uint32_t src_x,uint32_t src_y,uint32_t src_w,uint32_t src_h)276 rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
277 		       struct drm_framebuffer *fb, int crtc_x, int crtc_y,
278 		       unsigned int crtc_w, unsigned int crtc_h,
279 		       uint32_t src_x, uint32_t src_y,
280 		       uint32_t src_w, uint32_t src_h)
281 {
282 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
283 	struct rcar_du_device *rcdu = rplane->group->dev;
284 	const struct rcar_du_format_info *format;
285 	unsigned int nplanes;
286 	int ret;
287 
288 	format = rcar_du_format_info(fb->pixel_format);
289 	if (format == NULL) {
290 		dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
291 			fb->pixel_format);
292 		return -EINVAL;
293 	}
294 
295 	if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
296 		dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
297 		return -EINVAL;
298 	}
299 
300 	nplanes = rplane->format ? rplane->format->planes : 0;
301 
302 	/* Reallocate hardware planes if the number of required planes has
303 	 * changed.
304 	 */
305 	if (format->planes != nplanes) {
306 		rcar_du_plane_release(rplane);
307 		ret = rcar_du_plane_reserve(rplane, format);
308 		if (ret < 0)
309 			return ret;
310 	}
311 
312 	rplane->crtc = crtc;
313 	rplane->format = format;
314 
315 	rplane->src_x = src_x >> 16;
316 	rplane->src_y = src_y >> 16;
317 	rplane->dst_x = crtc_x;
318 	rplane->dst_y = crtc_y;
319 	rplane->width = crtc_w;
320 	rplane->height = crtc_h;
321 
322 	rcar_du_plane_compute_base(rplane, fb);
323 	rcar_du_plane_setup(rplane);
324 
325 	mutex_lock(&rplane->group->planes.lock);
326 	rplane->enabled = true;
327 	rcar_du_crtc_update_planes(rplane->crtc);
328 	mutex_unlock(&rplane->group->planes.lock);
329 
330 	return 0;
331 }
332 
rcar_du_plane_disable(struct drm_plane * plane)333 static int rcar_du_plane_disable(struct drm_plane *plane)
334 {
335 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
336 
337 	if (!rplane->enabled)
338 		return 0;
339 
340 	mutex_lock(&rplane->group->planes.lock);
341 	rplane->enabled = false;
342 	rcar_du_crtc_update_planes(rplane->crtc);
343 	mutex_unlock(&rplane->group->planes.lock);
344 
345 	rcar_du_plane_release(rplane);
346 
347 	rplane->crtc = NULL;
348 	rplane->format = NULL;
349 
350 	return 0;
351 }
352 
353 /* Both the .set_property and the .update_plane operations are called with the
354  * mode_config lock held. There is this no need to explicitly protect access to
355  * the alpha and colorkey fields and the mode register.
356  */
rcar_du_plane_set_alpha(struct rcar_du_plane * plane,u32 alpha)357 static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
358 {
359 	if (plane->alpha == alpha)
360 		return;
361 
362 	plane->alpha = alpha;
363 	if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
364 		return;
365 
366 	rcar_du_plane_setup_mode(plane, plane->hwindex);
367 }
368 
rcar_du_plane_set_colorkey(struct rcar_du_plane * plane,u32 colorkey)369 static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
370 				       u32 colorkey)
371 {
372 	if (plane->colorkey == colorkey)
373 		return;
374 
375 	plane->colorkey = colorkey;
376 	if (!plane->enabled)
377 		return;
378 
379 	rcar_du_plane_setup_mode(plane, plane->hwindex);
380 }
381 
rcar_du_plane_set_zpos(struct rcar_du_plane * plane,unsigned int zpos)382 static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
383 				   unsigned int zpos)
384 {
385 	mutex_lock(&plane->group->planes.lock);
386 	if (plane->zpos == zpos)
387 		goto done;
388 
389 	plane->zpos = zpos;
390 	if (!plane->enabled)
391 		goto done;
392 
393 	rcar_du_crtc_update_planes(plane->crtc);
394 
395 done:
396 	mutex_unlock(&plane->group->planes.lock);
397 }
398 
rcar_du_plane_set_property(struct drm_plane * plane,struct drm_property * property,uint64_t value)399 static int rcar_du_plane_set_property(struct drm_plane *plane,
400 				      struct drm_property *property,
401 				      uint64_t value)
402 {
403 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
404 	struct rcar_du_group *rgrp = rplane->group;
405 
406 	if (property == rgrp->planes.alpha)
407 		rcar_du_plane_set_alpha(rplane, value);
408 	else if (property == rgrp->planes.colorkey)
409 		rcar_du_plane_set_colorkey(rplane, value);
410 	else if (property == rgrp->planes.zpos)
411 		rcar_du_plane_set_zpos(rplane, value);
412 	else
413 		return -EINVAL;
414 
415 	return 0;
416 }
417 
418 static const struct drm_plane_funcs rcar_du_plane_funcs = {
419 	.update_plane = rcar_du_plane_update,
420 	.disable_plane = rcar_du_plane_disable,
421 	.set_property = rcar_du_plane_set_property,
422 	.destroy = drm_plane_cleanup,
423 };
424 
425 static const uint32_t formats[] = {
426 	DRM_FORMAT_RGB565,
427 	DRM_FORMAT_ARGB1555,
428 	DRM_FORMAT_XRGB1555,
429 	DRM_FORMAT_XRGB8888,
430 	DRM_FORMAT_ARGB8888,
431 	DRM_FORMAT_UYVY,
432 	DRM_FORMAT_YUYV,
433 	DRM_FORMAT_NV12,
434 	DRM_FORMAT_NV21,
435 	DRM_FORMAT_NV16,
436 };
437 
rcar_du_planes_init(struct rcar_du_group * rgrp)438 int rcar_du_planes_init(struct rcar_du_group *rgrp)
439 {
440 	struct rcar_du_planes *planes = &rgrp->planes;
441 	struct rcar_du_device *rcdu = rgrp->dev;
442 	unsigned int i;
443 
444 	mutex_init(&planes->lock);
445 	planes->free = 0xff;
446 
447 	planes->alpha =
448 		drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
449 	if (planes->alpha == NULL)
450 		return -ENOMEM;
451 
452 	/* The color key is expressed as an RGB888 triplet stored in a 32-bit
453 	 * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
454 	 * or enable source color keying (1).
455 	 */
456 	planes->colorkey =
457 		drm_property_create_range(rcdu->ddev, 0, "colorkey",
458 					  0, 0x01ffffff);
459 	if (planes->colorkey == NULL)
460 		return -ENOMEM;
461 
462 	planes->zpos =
463 		drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
464 	if (planes->zpos == NULL)
465 		return -ENOMEM;
466 
467 	for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) {
468 		struct rcar_du_plane *plane = &planes->planes[i];
469 
470 		plane->group = rgrp;
471 		plane->hwindex = -1;
472 		plane->alpha = 255;
473 		plane->colorkey = RCAR_DU_COLORKEY_NONE;
474 		plane->zpos = 0;
475 	}
476 
477 	return 0;
478 }
479 
rcar_du_planes_register(struct rcar_du_group * rgrp)480 int rcar_du_planes_register(struct rcar_du_group *rgrp)
481 {
482 	struct rcar_du_planes *planes = &rgrp->planes;
483 	struct rcar_du_device *rcdu = rgrp->dev;
484 	unsigned int crtcs;
485 	unsigned int i;
486 	int ret;
487 
488 	crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
489 
490 	for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
491 		struct rcar_du_kms_plane *plane;
492 
493 		plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
494 		if (plane == NULL)
495 			return -ENOMEM;
496 
497 		plane->hwplane = &planes->planes[i + 2];
498 		plane->hwplane->zpos = 1;
499 
500 		ret = drm_plane_init(rcdu->ddev, &plane->plane, crtcs,
501 				     &rcar_du_plane_funcs, formats,
502 				     ARRAY_SIZE(formats), false);
503 		if (ret < 0)
504 			return ret;
505 
506 		drm_object_attach_property(&plane->plane.base,
507 					   planes->alpha, 255);
508 		drm_object_attach_property(&plane->plane.base,
509 					   planes->colorkey,
510 					   RCAR_DU_COLORKEY_NONE);
511 		drm_object_attach_property(&plane->plane.base,
512 					   planes->zpos, 1);
513 	}
514 
515 	return 0;
516 }
517