2 * rcar_du_plane.c -- R-Car Display Unit Planes
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
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.
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_crtc.h>
17 #include <drm/drm_crtc_helper.h>
18 #include <drm/drm_fb_cma_helper.h>
19 #include <drm/drm_gem_cma_helper.h>
20 #include <drm/drm_plane_helper.h>
22 #include "rcar_du_drv.h"
23 #include "rcar_du_kms.h"
24 #include "rcar_du_plane.h"
25 #include "rcar_du_regs.h"
27 #define RCAR_DU_COLORKEY_NONE (0 << 24)
28 #define RCAR_DU_COLORKEY_SOURCE (1 << 24)
29 #define RCAR_DU_COLORKEY_MASK (1 << 24)
31 static u32
rcar_du_plane_read(struct rcar_du_group
*rgrp
,
32 unsigned int index
, u32 reg
)
34 return rcar_du_read(rgrp
->dev
,
35 rgrp
->mmio_offset
+ index
* PLANE_OFF
+ reg
);
38 static void rcar_du_plane_write(struct rcar_du_group
*rgrp
,
39 unsigned int index
, u32 reg
, u32 data
)
41 rcar_du_write(rgrp
->dev
, rgrp
->mmio_offset
+ index
* PLANE_OFF
+ reg
,
45 static void rcar_du_plane_setup_fb(struct rcar_du_plane
*plane
)
47 struct rcar_du_plane_state
*state
=
48 to_rcar_plane_state(plane
->plane
.state
);
49 struct drm_framebuffer
*fb
= plane
->plane
.state
->fb
;
50 struct rcar_du_group
*rgrp
= plane
->group
;
51 unsigned int src_x
= state
->state
.src_x
>> 16;
52 unsigned int src_y
= state
->state
.src_y
>> 16;
53 unsigned int index
= state
->hwindex
;
54 struct drm_gem_cma_object
*gem
;
58 interlaced
= state
->state
.crtc
->state
->adjusted_mode
.flags
59 & DRM_MODE_FLAG_INTERLACE
;
61 /* Memory pitch (expressed in pixels). Must be doubled for interlaced
62 * operation with 32bpp formats.
64 if (state
->format
->planes
== 2)
67 mwr
= fb
->pitches
[0] * 8 / state
->format
->bpp
;
69 if (interlaced
&& state
->format
->bpp
== 32)
72 rcar_du_plane_write(rgrp
, index
, PnMWR
, mwr
);
74 /* The Y position is expressed in raster line units and must be doubled
75 * for 32bpp formats, according to the R8A7790 datasheet. No mention of
76 * doubling the Y position is found in the R8A7779 datasheet, but the
77 * rule seems to apply there as well.
79 * Despite not being documented, doubling seem not to be needed when
80 * operating in interlaced mode.
82 * Similarly, for the second plane, NV12 and NV21 formats seem to
83 * require a halved Y position value, in both progressive and interlaced
86 rcar_du_plane_write(rgrp
, index
, PnSPXR
, src_x
);
87 rcar_du_plane_write(rgrp
, index
, PnSPYR
, src_y
*
88 (!interlaced
&& state
->format
->bpp
== 32 ? 2 : 1));
90 gem
= drm_fb_cma_get_gem_obj(fb
, 0);
91 rcar_du_plane_write(rgrp
, index
, PnDSA0R
, gem
->paddr
+ fb
->offsets
[0]);
93 if (state
->format
->planes
== 2) {
94 index
= (index
+ 1) % 8;
96 rcar_du_plane_write(rgrp
, index
, PnMWR
, fb
->pitches
[0]);
98 rcar_du_plane_write(rgrp
, index
, PnSPXR
, src_x
);
99 rcar_du_plane_write(rgrp
, index
, PnSPYR
, src_y
*
100 (state
->format
->bpp
== 16 ? 2 : 1) / 2);
102 gem
= drm_fb_cma_get_gem_obj(fb
, 1);
103 rcar_du_plane_write(rgrp
, index
, PnDSA0R
,
104 gem
->paddr
+ fb
->offsets
[1]);
108 static void rcar_du_plane_setup_mode(struct rcar_du_plane
*plane
,
111 struct rcar_du_plane_state
*state
=
112 to_rcar_plane_state(plane
->plane
.state
);
113 struct rcar_du_group
*rgrp
= plane
->group
;
117 /* The PnALPHAR register controls alpha-blending in 16bpp formats
118 * (ARGB1555 and XRGB1555).
120 * For ARGB, set the alpha value to 0, and enable alpha-blending when
121 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
123 * For XRGB, set the alpha value to the plane-wide alpha value and
124 * enable alpha-blending regardless of the X bit value.
126 if (state
->format
->fourcc
!= DRM_FORMAT_XRGB1555
)
127 rcar_du_plane_write(rgrp
, index
, PnALPHAR
, PnALPHAR_ABIT_0
);
129 rcar_du_plane_write(rgrp
, index
, PnALPHAR
,
130 PnALPHAR_ABIT_X
| state
->alpha
);
132 pnmr
= PnMR_BM_MD
| state
->format
->pnmr
;
134 /* Disable color keying when requested. YUV formats have the
135 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
138 if ((state
->colorkey
& RCAR_DU_COLORKEY_MASK
) == RCAR_DU_COLORKEY_NONE
)
139 pnmr
|= PnMR_SPIM_TP_OFF
;
141 /* For packed YUV formats we need to select the U/V order. */
142 if (state
->format
->fourcc
== DRM_FORMAT_YUYV
)
143 pnmr
|= PnMR_YCDF_YUYV
;
145 rcar_du_plane_write(rgrp
, index
, PnMR
, pnmr
);
147 switch (state
->format
->fourcc
) {
148 case DRM_FORMAT_RGB565
:
149 colorkey
= ((state
->colorkey
& 0xf80000) >> 8)
150 | ((state
->colorkey
& 0x00fc00) >> 5)
151 | ((state
->colorkey
& 0x0000f8) >> 3);
152 rcar_du_plane_write(rgrp
, index
, PnTC2R
, colorkey
);
155 case DRM_FORMAT_ARGB1555
:
156 case DRM_FORMAT_XRGB1555
:
157 colorkey
= ((state
->colorkey
& 0xf80000) >> 9)
158 | ((state
->colorkey
& 0x00f800) >> 6)
159 | ((state
->colorkey
& 0x0000f8) >> 3);
160 rcar_du_plane_write(rgrp
, index
, PnTC2R
, colorkey
);
163 case DRM_FORMAT_XRGB8888
:
164 case DRM_FORMAT_ARGB8888
:
165 rcar_du_plane_write(rgrp
, index
, PnTC3R
,
166 PnTC3R_CODE
| (state
->colorkey
& 0xffffff));
171 static void __rcar_du_plane_setup(struct rcar_du_plane
*plane
,
174 struct rcar_du_plane_state
*state
=
175 to_rcar_plane_state(plane
->plane
.state
);
176 struct rcar_du_group
*rgrp
= plane
->group
;
177 u32 ddcr2
= PnDDCR2_CODE
;
182 * The data format is selected by the DDDF field in PnMR and the EDF
185 ddcr4
= rcar_du_plane_read(rgrp
, index
, PnDDCR4
);
186 ddcr4
&= ~PnDDCR4_EDF_MASK
;
187 ddcr4
|= state
->format
->edf
| PnDDCR4_CODE
;
189 rcar_du_plane_setup_mode(plane
, index
);
191 if (state
->format
->planes
== 2) {
192 if (state
->hwindex
!= index
) {
193 if (state
->format
->fourcc
== DRM_FORMAT_NV12
||
194 state
->format
->fourcc
== DRM_FORMAT_NV21
)
195 ddcr2
|= PnDDCR2_Y420
;
197 if (state
->format
->fourcc
== DRM_FORMAT_NV21
)
198 ddcr2
|= PnDDCR2_NV21
;
200 ddcr2
|= PnDDCR2_DIVU
;
202 ddcr2
|= PnDDCR2_DIVY
;
206 rcar_du_plane_write(rgrp
, index
, PnDDCR2
, ddcr2
);
207 rcar_du_plane_write(rgrp
, index
, PnDDCR4
, ddcr4
);
209 /* Destination position and size */
210 rcar_du_plane_write(rgrp
, index
, PnDSXR
, plane
->plane
.state
->crtc_w
);
211 rcar_du_plane_write(rgrp
, index
, PnDSYR
, plane
->plane
.state
->crtc_h
);
212 rcar_du_plane_write(rgrp
, index
, PnDPXR
, plane
->plane
.state
->crtc_x
);
213 rcar_du_plane_write(rgrp
, index
, PnDPYR
, plane
->plane
.state
->crtc_y
);
215 /* Wrap-around and blinking, disabled */
216 rcar_du_plane_write(rgrp
, index
, PnWASPR
, 0);
217 rcar_du_plane_write(rgrp
, index
, PnWAMWR
, 4095);
218 rcar_du_plane_write(rgrp
, index
, PnBTR
, 0);
219 rcar_du_plane_write(rgrp
, index
, PnMLR
, 0);
222 void rcar_du_plane_setup(struct rcar_du_plane
*plane
)
224 struct rcar_du_plane_state
*state
=
225 to_rcar_plane_state(plane
->plane
.state
);
227 __rcar_du_plane_setup(plane
, state
->hwindex
);
228 if (state
->format
->planes
== 2)
229 __rcar_du_plane_setup(plane
, (state
->hwindex
+ 1) % 8);
231 rcar_du_plane_setup_fb(plane
);
234 static int rcar_du_plane_atomic_check(struct drm_plane
*plane
,
235 struct drm_plane_state
*state
)
237 struct rcar_du_plane_state
*rstate
= to_rcar_plane_state(state
);
238 struct rcar_du_plane
*rplane
= to_rcar_plane(plane
);
239 struct rcar_du_device
*rcdu
= rplane
->group
->dev
;
241 if (!state
->fb
|| !state
->crtc
) {
242 rstate
->format
= NULL
;
246 if (state
->src_w
>> 16 != state
->crtc_w
||
247 state
->src_h
>> 16 != state
->crtc_h
) {
248 dev_dbg(rcdu
->dev
, "%s: scaling not supported\n", __func__
);
252 rstate
->format
= rcar_du_format_info(state
->fb
->pixel_format
);
253 if (rstate
->format
== NULL
) {
254 dev_dbg(rcdu
->dev
, "%s: unsupported format %08x\n", __func__
,
255 state
->fb
->pixel_format
);
262 static void rcar_du_plane_atomic_update(struct drm_plane
*plane
,
263 struct drm_plane_state
*old_state
)
265 struct rcar_du_plane
*rplane
= to_rcar_plane(plane
);
267 if (plane
->state
->crtc
)
268 rcar_du_plane_setup(rplane
);
271 static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs
= {
272 .atomic_check
= rcar_du_plane_atomic_check
,
273 .atomic_update
= rcar_du_plane_atomic_update
,
276 static void rcar_du_plane_reset(struct drm_plane
*plane
)
278 struct rcar_du_plane_state
*state
;
280 if (plane
->state
&& plane
->state
->fb
)
281 drm_framebuffer_unreference(plane
->state
->fb
);
286 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
292 state
->colorkey
= RCAR_DU_COLORKEY_NONE
;
293 state
->zpos
= plane
->type
== DRM_PLANE_TYPE_PRIMARY
? 0 : 1;
295 plane
->state
= &state
->state
;
296 plane
->state
->plane
= plane
;
299 static struct drm_plane_state
*
300 rcar_du_plane_atomic_duplicate_state(struct drm_plane
*plane
)
302 struct rcar_du_plane_state
*state
;
303 struct rcar_du_plane_state
*copy
;
305 if (WARN_ON(!plane
->state
))
308 state
= to_rcar_plane_state(plane
->state
);
309 copy
= kmemdup(state
, sizeof(*state
), GFP_KERNEL
);
313 __drm_atomic_helper_plane_duplicate_state(plane
, ©
->state
);
318 static void rcar_du_plane_atomic_destroy_state(struct drm_plane
*plane
,
319 struct drm_plane_state
*state
)
321 __drm_atomic_helper_plane_destroy_state(plane
, state
);
322 kfree(to_rcar_plane_state(state
));
325 static int rcar_du_plane_atomic_set_property(struct drm_plane
*plane
,
326 struct drm_plane_state
*state
,
327 struct drm_property
*property
,
330 struct rcar_du_plane_state
*rstate
= to_rcar_plane_state(state
);
331 struct rcar_du_device
*rcdu
= to_rcar_plane(plane
)->group
->dev
;
333 if (property
== rcdu
->props
.alpha
)
335 else if (property
== rcdu
->props
.colorkey
)
336 rstate
->colorkey
= val
;
337 else if (property
== rcdu
->props
.zpos
)
345 static int rcar_du_plane_atomic_get_property(struct drm_plane
*plane
,
346 const struct drm_plane_state
*state
, struct drm_property
*property
,
349 const struct rcar_du_plane_state
*rstate
=
350 container_of(state
, const struct rcar_du_plane_state
, state
);
351 struct rcar_du_device
*rcdu
= to_rcar_plane(plane
)->group
->dev
;
353 if (property
== rcdu
->props
.alpha
)
354 *val
= rstate
->alpha
;
355 else if (property
== rcdu
->props
.colorkey
)
356 *val
= rstate
->colorkey
;
357 else if (property
== rcdu
->props
.zpos
)
365 static const struct drm_plane_funcs rcar_du_plane_funcs
= {
366 .update_plane
= drm_atomic_helper_update_plane
,
367 .disable_plane
= drm_atomic_helper_disable_plane
,
368 .reset
= rcar_du_plane_reset
,
369 .set_property
= drm_atomic_helper_plane_set_property
,
370 .destroy
= drm_plane_cleanup
,
371 .atomic_duplicate_state
= rcar_du_plane_atomic_duplicate_state
,
372 .atomic_destroy_state
= rcar_du_plane_atomic_destroy_state
,
373 .atomic_set_property
= rcar_du_plane_atomic_set_property
,
374 .atomic_get_property
= rcar_du_plane_atomic_get_property
,
377 static const uint32_t formats
[] = {
390 int rcar_du_planes_init(struct rcar_du_group
*rgrp
)
392 struct rcar_du_device
*rcdu
= rgrp
->dev
;
397 /* Create one primary plane per CRTC in this group and seven overlay
400 rgrp
->num_planes
= rgrp
->num_crtcs
+ 7;
402 crtcs
= ((1 << rcdu
->num_crtcs
) - 1) & (3 << (2 * rgrp
->index
));
404 for (i
= 0; i
< rgrp
->num_planes
; ++i
) {
405 enum drm_plane_type type
= i
< rgrp
->num_crtcs
406 ? DRM_PLANE_TYPE_PRIMARY
407 : DRM_PLANE_TYPE_OVERLAY
;
408 struct rcar_du_plane
*plane
= &rgrp
->planes
[i
];
412 ret
= drm_universal_plane_init(rcdu
->ddev
, &plane
->plane
, crtcs
,
413 &rcar_du_plane_funcs
, formats
,
414 ARRAY_SIZE(formats
), type
);
418 drm_plane_helper_add(&plane
->plane
,
419 &rcar_du_plane_helper_funcs
);
421 if (type
== DRM_PLANE_TYPE_PRIMARY
)
424 drm_object_attach_property(&plane
->plane
.base
,
425 rcdu
->props
.alpha
, 255);
426 drm_object_attach_property(&plane
->plane
.base
,
427 rcdu
->props
.colorkey
,
428 RCAR_DU_COLORKEY_NONE
);
429 drm_object_attach_property(&plane
->plane
.base
,
430 rcdu
->props
.zpos
, 1);