drm: rcar-du: Add VSP1 support to the planes allocator
[deliverable/linux.git] / drivers / gpu / drm / rcar-du / rcar_du_plane.c
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_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>
21
22 #include "rcar_du_drv.h"
23 #include "rcar_du_kms.h"
24 #include "rcar_du_plane.h"
25 #include "rcar_du_regs.h"
26
27 #define RCAR_DU_COLORKEY_NONE (0 << 24)
28 #define RCAR_DU_COLORKEY_SOURCE (1 << 24)
29 #define RCAR_DU_COLORKEY_MASK (1 << 24)
30
31 static void rcar_du_plane_write(struct rcar_du_group *rgrp,
32 unsigned int index, u32 reg, u32 data)
33 {
34 rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
35 data);
36 }
37
38 static void rcar_du_plane_setup_scanout(struct rcar_du_plane *plane)
39 {
40 struct rcar_du_plane_state *state =
41 to_rcar_plane_state(plane->plane.state);
42 struct drm_framebuffer *fb = plane->plane.state->fb;
43 struct rcar_du_group *rgrp = plane->group;
44 unsigned int src_x = state->state.src_x >> 16;
45 unsigned int src_y = state->state.src_y >> 16;
46 unsigned int index = state->hwindex;
47 struct drm_gem_cma_object *gem;
48 unsigned int pitch;
49 bool interlaced;
50 unsigned int i;
51 u32 dma[2];
52
53 interlaced = state->state.crtc->state->adjusted_mode.flags
54 & DRM_MODE_FLAG_INTERLACE;
55
56 /* Memory pitch (expressed in pixels). Must be doubled for interlaced
57 * operation with 32bpp formats.
58 */
59 if (state->format->planes == 2)
60 pitch = fb->pitches[0];
61 else
62 pitch = fb->pitches[0] * 8 / state->format->bpp;
63
64 for (i = 0; i < state->format->planes; ++i) {
65 gem = drm_fb_cma_get_gem_obj(fb, i);
66 dma[i] = gem->paddr + fb->offsets[i];
67 }
68
69 rcar_du_plane_write(rgrp, index, PnMWR,
70 (interlaced && state->format->bpp == 32) ?
71 pitch * 2 : pitch);
72
73 /* The Y position is expressed in raster line units and must be doubled
74 * for 32bpp formats, according to the R8A7790 datasheet. No mention of
75 * doubling the Y position is found in the R8A7779 datasheet, but the
76 * rule seems to apply there as well.
77 *
78 * Despite not being documented, doubling seem not to be needed when
79 * operating in interlaced mode.
80 *
81 * Similarly, for the second plane, NV12 and NV21 formats seem to
82 * require a halved Y position value, in both progressive and interlaced
83 * modes.
84 */
85 rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
86 rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
87 (!interlaced && state->format->bpp == 32 ? 2 : 1));
88
89 rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
90
91 if (state->format->planes == 2) {
92 index = (index + 1) % 8;
93
94 rcar_du_plane_write(rgrp, index, PnMWR, pitch);
95
96 rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
97 rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
98 (state->format->bpp == 16 ? 2 : 1) / 2);
99
100 rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
101 }
102 }
103
104 static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
105 unsigned int index)
106 {
107 struct rcar_du_plane_state *state =
108 to_rcar_plane_state(plane->plane.state);
109 struct rcar_du_group *rgrp = plane->group;
110 u32 colorkey;
111 u32 pnmr;
112
113 /* The PnALPHAR register controls alpha-blending in 16bpp formats
114 * (ARGB1555 and XRGB1555).
115 *
116 * For ARGB, set the alpha value to 0, and enable alpha-blending when
117 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
118 *
119 * For XRGB, set the alpha value to the plane-wide alpha value and
120 * enable alpha-blending regardless of the X bit value.
121 */
122 if (state->format->fourcc != DRM_FORMAT_XRGB1555)
123 rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
124 else
125 rcar_du_plane_write(rgrp, index, PnALPHAR,
126 PnALPHAR_ABIT_X | state->alpha);
127
128 pnmr = PnMR_BM_MD | state->format->pnmr;
129
130 /* Disable color keying when requested. YUV formats have the
131 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
132 * automatically.
133 */
134 if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
135 pnmr |= PnMR_SPIM_TP_OFF;
136
137 /* For packed YUV formats we need to select the U/V order. */
138 if (state->format->fourcc == DRM_FORMAT_YUYV)
139 pnmr |= PnMR_YCDF_YUYV;
140
141 rcar_du_plane_write(rgrp, index, PnMR, pnmr);
142
143 switch (state->format->fourcc) {
144 case DRM_FORMAT_RGB565:
145 colorkey = ((state->colorkey & 0xf80000) >> 8)
146 | ((state->colorkey & 0x00fc00) >> 5)
147 | ((state->colorkey & 0x0000f8) >> 3);
148 rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
149 break;
150
151 case DRM_FORMAT_ARGB1555:
152 case DRM_FORMAT_XRGB1555:
153 colorkey = ((state->colorkey & 0xf80000) >> 9)
154 | ((state->colorkey & 0x00f800) >> 6)
155 | ((state->colorkey & 0x0000f8) >> 3);
156 rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
157 break;
158
159 case DRM_FORMAT_XRGB8888:
160 case DRM_FORMAT_ARGB8888:
161 rcar_du_plane_write(rgrp, index, PnTC3R,
162 PnTC3R_CODE | (state->colorkey & 0xffffff));
163 break;
164 }
165 }
166
167 static void rcar_du_plane_setup_format(struct rcar_du_plane *plane,
168 unsigned int index)
169 {
170 struct rcar_du_plane_state *state =
171 to_rcar_plane_state(plane->plane.state);
172 struct rcar_du_group *rgrp = plane->group;
173 u32 ddcr2 = PnDDCR2_CODE;
174 u32 ddcr4;
175
176 /* Data format
177 *
178 * The data format is selected by the DDDF field in PnMR and the EDF
179 * field in DDCR4.
180 */
181
182 rcar_du_plane_setup_mode(plane, index);
183
184 if (state->format->planes == 2) {
185 if (state->hwindex != index) {
186 if (state->format->fourcc == DRM_FORMAT_NV12 ||
187 state->format->fourcc == DRM_FORMAT_NV21)
188 ddcr2 |= PnDDCR2_Y420;
189
190 if (state->format->fourcc == DRM_FORMAT_NV21)
191 ddcr2 |= PnDDCR2_NV21;
192
193 ddcr2 |= PnDDCR2_DIVU;
194 } else {
195 ddcr2 |= PnDDCR2_DIVY;
196 }
197 }
198
199 rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
200
201 ddcr4 = state->format->edf | PnDDCR4_CODE;
202
203 rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
204
205 /* Destination position and size */
206 rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w);
207 rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h);
208 rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x);
209 rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y);
210
211 /* Wrap-around and blinking, disabled */
212 rcar_du_plane_write(rgrp, index, PnWASPR, 0);
213 rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
214 rcar_du_plane_write(rgrp, index, PnBTR, 0);
215 rcar_du_plane_write(rgrp, index, PnMLR, 0);
216 }
217
218 void rcar_du_plane_setup(struct rcar_du_plane *plane)
219 {
220 struct rcar_du_plane_state *state =
221 to_rcar_plane_state(plane->plane.state);
222
223 rcar_du_plane_setup_format(plane, state->hwindex);
224 if (state->format->planes == 2)
225 rcar_du_plane_setup_format(plane, (state->hwindex + 1) % 8);
226
227 rcar_du_plane_setup_scanout(plane);
228 }
229
230 static int rcar_du_plane_atomic_check(struct drm_plane *plane,
231 struct drm_plane_state *state)
232 {
233 struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
234 struct rcar_du_plane *rplane = to_rcar_plane(plane);
235 struct rcar_du_device *rcdu = rplane->group->dev;
236
237 if (!state->fb || !state->crtc) {
238 rstate->format = NULL;
239 return 0;
240 }
241
242 if (state->src_w >> 16 != state->crtc_w ||
243 state->src_h >> 16 != state->crtc_h) {
244 dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
245 return -EINVAL;
246 }
247
248 rstate->format = rcar_du_format_info(state->fb->pixel_format);
249 if (rstate->format == NULL) {
250 dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
251 state->fb->pixel_format);
252 return -EINVAL;
253 }
254
255 return 0;
256 }
257
258 static void rcar_du_plane_atomic_update(struct drm_plane *plane,
259 struct drm_plane_state *old_state)
260 {
261 struct rcar_du_plane *rplane = to_rcar_plane(plane);
262
263 if (plane->state->crtc)
264 rcar_du_plane_setup(rplane);
265 }
266
267 static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
268 .atomic_check = rcar_du_plane_atomic_check,
269 .atomic_update = rcar_du_plane_atomic_update,
270 };
271
272 static struct drm_plane_state *
273 rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
274 {
275 struct rcar_du_plane_state *state;
276 struct rcar_du_plane_state *copy;
277
278 if (WARN_ON(!plane->state))
279 return NULL;
280
281 state = to_rcar_plane_state(plane->state);
282 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
283 if (copy == NULL)
284 return NULL;
285
286 __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
287
288 return &copy->state;
289 }
290
291 static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
292 struct drm_plane_state *state)
293 {
294 __drm_atomic_helper_plane_destroy_state(plane, state);
295 kfree(to_rcar_plane_state(state));
296 }
297
298 static void rcar_du_plane_reset(struct drm_plane *plane)
299 {
300 struct rcar_du_plane_state *state;
301
302 if (plane->state) {
303 rcar_du_plane_atomic_destroy_state(plane, plane->state);
304 plane->state = NULL;
305 }
306
307 state = kzalloc(sizeof(*state), GFP_KERNEL);
308 if (state == NULL)
309 return;
310
311 state->hwindex = -1;
312 state->source = RCAR_DU_PLANE_MEMORY;
313 state->alpha = 255;
314 state->colorkey = RCAR_DU_COLORKEY_NONE;
315 state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
316
317 plane->state = &state->state;
318 plane->state->plane = plane;
319 }
320
321 static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
322 struct drm_plane_state *state,
323 struct drm_property *property,
324 uint64_t val)
325 {
326 struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
327 struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
328
329 if (property == rcdu->props.alpha)
330 rstate->alpha = val;
331 else if (property == rcdu->props.colorkey)
332 rstate->colorkey = val;
333 else if (property == rcdu->props.zpos)
334 rstate->zpos = val;
335 else
336 return -EINVAL;
337
338 return 0;
339 }
340
341 static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
342 const struct drm_plane_state *state, struct drm_property *property,
343 uint64_t *val)
344 {
345 const struct rcar_du_plane_state *rstate =
346 container_of(state, const struct rcar_du_plane_state, state);
347 struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
348
349 if (property == rcdu->props.alpha)
350 *val = rstate->alpha;
351 else if (property == rcdu->props.colorkey)
352 *val = rstate->colorkey;
353 else if (property == rcdu->props.zpos)
354 *val = rstate->zpos;
355 else
356 return -EINVAL;
357
358 return 0;
359 }
360
361 static const struct drm_plane_funcs rcar_du_plane_funcs = {
362 .update_plane = drm_atomic_helper_update_plane,
363 .disable_plane = drm_atomic_helper_disable_plane,
364 .reset = rcar_du_plane_reset,
365 .set_property = drm_atomic_helper_plane_set_property,
366 .destroy = drm_plane_cleanup,
367 .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
368 .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
369 .atomic_set_property = rcar_du_plane_atomic_set_property,
370 .atomic_get_property = rcar_du_plane_atomic_get_property,
371 };
372
373 static const uint32_t formats[] = {
374 DRM_FORMAT_RGB565,
375 DRM_FORMAT_ARGB1555,
376 DRM_FORMAT_XRGB1555,
377 DRM_FORMAT_XRGB8888,
378 DRM_FORMAT_ARGB8888,
379 DRM_FORMAT_UYVY,
380 DRM_FORMAT_YUYV,
381 DRM_FORMAT_NV12,
382 DRM_FORMAT_NV21,
383 DRM_FORMAT_NV16,
384 };
385
386 int rcar_du_planes_init(struct rcar_du_group *rgrp)
387 {
388 struct rcar_du_device *rcdu = rgrp->dev;
389 unsigned int crtcs;
390 unsigned int i;
391 int ret;
392
393 /* Create one primary plane per CRTC in this group and seven overlay
394 * planes.
395 */
396 rgrp->num_planes = rgrp->num_crtcs + 7;
397
398 crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
399
400 for (i = 0; i < rgrp->num_planes; ++i) {
401 enum drm_plane_type type = i < rgrp->num_crtcs
402 ? DRM_PLANE_TYPE_PRIMARY
403 : DRM_PLANE_TYPE_OVERLAY;
404 struct rcar_du_plane *plane = &rgrp->planes[i];
405
406 plane->group = rgrp;
407
408 ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
409 &rcar_du_plane_funcs, formats,
410 ARRAY_SIZE(formats), type,
411 NULL);
412 if (ret < 0)
413 return ret;
414
415 drm_plane_helper_add(&plane->plane,
416 &rcar_du_plane_helper_funcs);
417
418 if (type == DRM_PLANE_TYPE_PRIMARY)
419 continue;
420
421 drm_object_attach_property(&plane->plane.base,
422 rcdu->props.alpha, 255);
423 drm_object_attach_property(&plane->plane.base,
424 rcdu->props.colorkey,
425 RCAR_DU_COLORKEY_NONE);
426 drm_object_attach_property(&plane->plane.base,
427 rcdu->props.zpos, 1);
428 }
429
430 return 0;
431 }
This page took 0.053448 seconds and 5 git commands to generate.