2 * rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor
4 * Copyright (C) 2015 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 <linux/bitops.h>
23 #include <linux/dma-mapping.h>
24 #include <linux/of_platform.h>
25 #include <linux/scatterlist.h>
26 #include <linux/videodev2.h>
28 #include <media/vsp1.h>
30 #include "rcar_du_drv.h"
31 #include "rcar_du_kms.h"
32 #include "rcar_du_vsp.h"
34 static void rcar_du_vsp_complete(void *private, bool completed)
36 struct rcar_du_crtc *crtc = private;
38 if (crtc->vblank_enable)
39 drm_crtc_handle_vblank(&crtc->crtc);
42 rcar_du_crtc_finish_page_flip(crtc);
45 void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
47 const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
48 struct rcar_du_device *rcdu = crtc->group->dev;
49 struct vsp1_du_lif_config cfg = {
50 .width = mode->hdisplay,
51 .height = mode->vdisplay,
52 .callback = rcar_du_vsp_complete,
53 .callback_data = crtc,
55 struct rcar_du_plane_state state = {
60 .crtc_w = mode->hdisplay,
61 .crtc_h = mode->vdisplay,
64 .src_w = mode->hdisplay << 16,
65 .src_h = mode->vdisplay << 16,
68 .format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
69 .source = RCAR_DU_PLANE_VSPD1,
74 if (rcdu->info->gen >= 3)
75 state.hwindex = (crtc->index % 2) ? 2 : 0;
77 state.hwindex = crtc->index % 2;
79 __rcar_du_plane_setup(crtc->group, &state);
82 * Ensure that the plane source configuration takes effect by requesting
83 * a restart of the group. See rcar_du_plane_atomic_update() for a more
84 * detailed explanation.
86 * TODO: Check whether this is still needed on Gen3.
88 crtc->group->need_restart = true;
90 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
93 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
95 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
98 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
100 vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
103 void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
105 vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe);
108 /* Keep the two tables in sync. */
109 static const u32 formats_kms[] = {
138 static const u32 formats_v4l2[] = {
140 V4L2_PIX_FMT_ARGB444,
141 V4L2_PIX_FMT_XRGB444,
142 V4L2_PIX_FMT_ARGB555,
143 V4L2_PIX_FMT_XRGB555,
159 V4L2_PIX_FMT_YUV420M,
160 V4L2_PIX_FMT_YVU420M,
161 V4L2_PIX_FMT_YUV422M,
162 V4L2_PIX_FMT_YVU422M,
163 V4L2_PIX_FMT_YUV444M,
164 V4L2_PIX_FMT_YVU444M,
167 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
169 struct rcar_du_vsp_plane_state *state =
170 to_rcar_vsp_plane_state(plane->plane.state);
171 struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
172 struct drm_framebuffer *fb = plane->plane.state->fb;
173 struct vsp1_du_atomic_config cfg = {
175 .pitch = fb->pitches[0],
176 .alpha = state->alpha,
177 .zpos = state->state.zpos,
181 cfg.src.left = state->state.src_x >> 16;
182 cfg.src.top = state->state.src_y >> 16;
183 cfg.src.width = state->state.src_w >> 16;
184 cfg.src.height = state->state.src_h >> 16;
186 cfg.dst.left = state->state.crtc_x;
187 cfg.dst.top = state->state.crtc_y;
188 cfg.dst.width = state->state.crtc_w;
189 cfg.dst.height = state->state.crtc_h;
191 for (i = 0; i < state->format->planes; ++i)
192 cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
195 for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
196 if (formats_kms[i] == state->format->fourcc) {
197 cfg.pixelformat = formats_v4l2[i];
202 vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
206 static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
207 struct drm_plane_state *state)
209 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
210 struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
211 struct rcar_du_device *rcdu = vsp->dev;
218 for (i = 0; i < rstate->format->planes; ++i) {
219 struct drm_gem_cma_object *gem =
220 drm_fb_cma_get_gem_obj(state->fb, i);
221 struct sg_table *sgt = &rstate->sg_tables[i];
223 ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
228 ret = vsp1_du_map_sg(vsp->vsp, sgt);
240 struct sg_table *sgt = &rstate->sg_tables[i];
242 vsp1_du_unmap_sg(vsp->vsp, sgt);
249 static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
250 struct drm_plane_state *state)
252 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
253 struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
259 for (i = 0; i < rstate->format->planes; ++i) {
260 struct sg_table *sgt = &rstate->sg_tables[i];
262 vsp1_du_unmap_sg(vsp->vsp, sgt);
267 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
268 struct drm_plane_state *state)
270 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
271 struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
272 struct rcar_du_device *rcdu = rplane->vsp->dev;
274 if (!state->fb || !state->crtc) {
275 rstate->format = NULL;
279 if (state->src_w >> 16 != state->crtc_w ||
280 state->src_h >> 16 != state->crtc_h) {
281 dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
285 rstate->format = rcar_du_format_info(state->fb->format->format);
286 if (rstate->format == NULL) {
287 dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
288 state->fb->format->format);
295 static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
296 struct drm_plane_state *old_state)
298 struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
299 struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
301 if (plane->state->crtc)
302 rcar_du_vsp_plane_setup(rplane);
304 vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
305 rplane->index, NULL);
308 static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
309 .prepare_fb = rcar_du_vsp_plane_prepare_fb,
310 .cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
311 .atomic_check = rcar_du_vsp_plane_atomic_check,
312 .atomic_update = rcar_du_vsp_plane_atomic_update,
315 static struct drm_plane_state *
316 rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
318 struct rcar_du_vsp_plane_state *state;
319 struct rcar_du_vsp_plane_state *copy;
321 if (WARN_ON(!plane->state))
324 state = to_rcar_vsp_plane_state(plane->state);
325 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
329 __drm_atomic_helper_plane_duplicate_state(plane, ©->state);
334 static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
335 struct drm_plane_state *state)
337 __drm_atomic_helper_plane_destroy_state(state);
338 kfree(to_rcar_vsp_plane_state(state));
341 static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
343 struct rcar_du_vsp_plane_state *state;
346 rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
350 state = kzalloc(sizeof(*state), GFP_KERNEL);
355 state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
357 plane->state = &state->state;
358 plane->state->plane = plane;
361 static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
362 struct drm_plane_state *state, struct drm_property *property,
365 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
366 struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
368 if (property == rcdu->props.alpha)
376 static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
377 const struct drm_plane_state *state, struct drm_property *property,
380 const struct rcar_du_vsp_plane_state *rstate =
381 container_of(state, const struct rcar_du_vsp_plane_state, state);
382 struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
384 if (property == rcdu->props.alpha)
385 *val = rstate->alpha;
392 static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
393 .update_plane = drm_atomic_helper_update_plane,
394 .disable_plane = drm_atomic_helper_disable_plane,
395 .reset = rcar_du_vsp_plane_reset,
396 .destroy = drm_plane_cleanup,
397 .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
398 .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
399 .atomic_set_property = rcar_du_vsp_plane_atomic_set_property,
400 .atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
403 int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
406 struct rcar_du_device *rcdu = vsp->dev;
407 struct platform_device *pdev;
408 unsigned int num_crtcs = hweight32(crtcs);
412 /* Find the VSP device and initialize it. */
413 pdev = of_find_device_by_node(np);
417 vsp->vsp = &pdev->dev;
419 ret = vsp1_du_init(vsp->vsp);
424 * The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
427 vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
429 vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
430 sizeof(*vsp->planes), GFP_KERNEL);
434 for (i = 0; i < vsp->num_planes; ++i) {
435 enum drm_plane_type type = i < num_crtcs
436 ? DRM_PLANE_TYPE_PRIMARY
437 : DRM_PLANE_TYPE_OVERLAY;
438 struct rcar_du_vsp_plane *plane = &vsp->planes[i];
443 ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
444 &rcar_du_vsp_plane_funcs,
446 ARRAY_SIZE(formats_kms),
451 drm_plane_helper_add(&plane->plane,
452 &rcar_du_vsp_plane_helper_funcs);
454 if (type == DRM_PLANE_TYPE_PRIMARY)
457 drm_object_attach_property(&plane->plane.base,
458 rcdu->props.alpha, 255);
459 drm_plane_create_zpos_property(&plane->plane, 1, 1,
460 vsp->num_planes - 1);