drm: rcar-du: Expose the VSP1 compositor through KMS planes
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Mon, 7 Sep 2015 14:14:58 +0000 (17:14 +0300)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Tue, 23 Feb 2016 07:32:44 +0000 (09:32 +0200)
On R-Car Gen3 SoCs the DU lost its ability to access memory directly and
needs to work in conjunction with the VSP to do so. This commit handles
the VSP internally to hide it from the user.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
drivers/gpu/drm/rcar-du/Kconfig
drivers/gpu/drm/rcar-du/Makefile
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
drivers/gpu/drm/rcar-du/rcar_du_drv.h
drivers/gpu/drm/rcar-du/rcar_du_kms.c
drivers/gpu/drm/rcar-du/rcar_du_plane.c
drivers/gpu/drm/rcar-du/rcar_du_plane.h
drivers/gpu/drm/rcar-du/rcar_du_vsp.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_vsp.h [new file with mode: 0644]

index 96dcd4a78951c51b3471a7c4dc22bded4cd8b39d..daec18e2ffee01ddb15faf821d92209770aeaf0b 100644 (file)
@@ -25,3 +25,10 @@ config DRM_RCAR_LVDS
        help
          Enable support for the R-Car Display Unit embedded LVDS encoders
          (currently only on R8A7790 and R8A7791).
+
+config DRM_RCAR_VSP
+       bool "R-Car DU VSP Compositor Support"
+       depends on DRM_RCAR_DU
+       depends on VIDEO_RENESAS_VSP1
+       help
+         Enable support to expose the R-Car VSP Compositor as KMS planes.
index 05de1c4097af92192bbb28ee5c7a704775a9b1db..827711e282269ea2656e6b16d8fafee43e20c3a0 100644 (file)
@@ -11,4 +11,6 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI)   += rcar_du_hdmicon.o \
                                           rcar_du_hdmienc.o
 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)    += rcar_du_lvdsenc.o
 
+rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)     += rcar_du_vsp.o
+
 obj-$(CONFIG_DRM_RCAR_DU)              += rcar-du-drm.o
index e2560aa26ef4fc27e43c6f2f3768ee69fb9d8c94..a862cf5568cdafdbe8a3f21522d84cbf401750d7 100644 (file)
@@ -28,6 +28,7 @@
 #include "rcar_du_kms.h"
 #include "rcar_du_plane.h"
 #include "rcar_du_regs.h"
+#include "rcar_du_vsp.h"
 
 static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
 {
@@ -207,6 +208,7 @@ plane_format(struct rcar_du_plane *plane)
 static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
 {
        struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
        unsigned int num_planes = 0;
        unsigned int dptsr_planes;
        unsigned int hwplanes = 0;
@@ -250,6 +252,12 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
                }
        }
 
+       /* If VSP+DU integration is enabled the plane assignment is fixed. */
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+               dspr = (rcrtc->index % 2) + 1;
+               hwplanes = 1 << (rcrtc->index % 2);
+       }
+
        /* Update the planes to display timing and dot clock generator
         * associations.
         *
@@ -369,6 +377,10 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
 
        rcar_du_group_start_stop(rcrtc->group, true);
 
+       /* Enable the VSP compositor. */
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_enable(rcrtc);
+
        /* Turn vertical blanking interrupt reporting back on. */
        drm_crtc_vblank_on(crtc);
 
@@ -402,6 +414,10 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
        rcar_du_crtc_wait_page_flip(rcrtc);
        drm_crtc_vblank_off(crtc);
 
+       /* Disable the VSP compositor. */
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_disable(rcrtc);
+
        /* Select switch sync mode. This stops display operation and configures
         * the HSYNC and VSYNC signals as inputs.
         */
@@ -414,6 +430,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
 
 void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
 {
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_disable(rcrtc);
+
        rcar_du_crtc_stop(rcrtc);
        rcar_du_crtc_put(rcrtc);
 }
@@ -429,13 +448,17 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
        rcar_du_crtc_start(rcrtc);
 
        /* Commit the planes state. */
-       for (i = 0; i < rcrtc->group->num_planes; ++i) {
-               struct rcar_du_plane *plane = &rcrtc->group->planes[i];
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+               rcar_du_vsp_enable(rcrtc);
+       } else {
+               for (i = 0; i < rcrtc->group->num_planes; ++i) {
+                       struct rcar_du_plane *plane = &rcrtc->group->planes[i];
 
-               if (plane->plane.state->crtc != &rcrtc->crtc)
-                       continue;
+                       if (plane->plane.state->crtc != &rcrtc->crtc)
+                               continue;
 
-               rcar_du_plane_setup(plane);
+                       rcar_du_plane_setup(plane);
+               }
        }
 
        rcar_du_crtc_update_planes(rcrtc);
@@ -486,6 +509,9 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc,
                rcrtc->event = event;
                spin_unlock_irqrestore(&dev->event_lock, flags);
        }
+
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_atomic_begin(rcrtc);
 }
 
 static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc,
@@ -494,6 +520,9 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc,
        struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 
        rcar_du_crtc_update_planes(rcrtc);
+
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_atomic_flush(rcrtc);
 }
 
 static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
@@ -549,6 +578,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
        struct platform_device *pdev = to_platform_device(rcdu->dev);
        struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
        struct drm_crtc *crtc = &rcrtc->crtc;
+       struct drm_plane *primary;
        unsigned int irqflags;
        struct clk *clk;
        char clk_name[9];
@@ -585,8 +615,12 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
        rcrtc->mmio_offset = mmio_offsets[index];
        rcrtc->index = index;
 
-       ret = drm_crtc_init_with_planes(rcdu->ddev, crtc,
-                                       &rgrp->planes[index % 2].plane,
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
+               primary = &rcrtc->vsp->planes[0].plane;
+       else
+               primary = &rgrp->planes[index % 2].plane;
+
+       ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, primary,
                                        NULL, &crtc_funcs, NULL);
        if (ret < 0)
                return ret;
index dbc3a3ef9aa6c89f79dd3ee6472dc7c07c78a6e7..6f08b7e7db06708cfa910fd7655d3d52ee3f78b7 100644 (file)
@@ -21,6 +21,7 @@
 #include <drm/drm_crtc.h>
 
 struct rcar_du_group;
+struct rcar_du_vsp;
 
 /**
  * struct rcar_du_crtc - the CRTC, representing a DU superposition processor
@@ -50,6 +51,7 @@ struct rcar_du_crtc {
        unsigned int outputs;
 
        struct rcar_du_group *group;
+       struct rcar_du_vsp *vsp;
 };
 
 #define to_rcar_crtc(c)        container_of(c, struct rcar_du_crtc, crtc)
index e10b4f0d8f83e6da01cbc2fecf8fe45678d3d980..a81eb3281e7f2748d7794577f4dde2e5b89cd74b 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "rcar_du_crtc.h"
 #include "rcar_du_group.h"
+#include "rcar_du_vsp.h"
 
 struct clk;
 struct device;
@@ -29,6 +30,7 @@ struct rcar_du_lvdsenc;
 
 #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0)        /* Per-CRTC IRQ and clock */
 #define RCAR_DU_FEATURE_EXT_CTRL_REGS  (1 << 1)        /* Has extended control registers */
+#define RCAR_DU_FEATURE_VSP1_SOURCE    (1 << 2)        /* Has inputs from VSP1 */
 
 #define RCAR_DU_QUIRK_ALIGN_128B       (1 << 0)        /* Align pitches to 128 bytes */
 #define RCAR_DU_QUIRK_LVDS_LANES       (1 << 1)        /* LVDS lanes 1 and 3 inverted */
@@ -68,6 +70,7 @@ struct rcar_du_device_info {
 #define RCAR_DU_MAX_CRTCS              3
 #define RCAR_DU_MAX_GROUPS             DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
 #define RCAR_DU_MAX_LVDS               2
+#define RCAR_DU_MAX_VSPS               4
 
 struct rcar_du_device {
        struct device *dev;
@@ -82,6 +85,7 @@ struct rcar_du_device {
        unsigned int num_crtcs;
 
        struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
+       struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
 
        struct {
                struct drm_property *alpha;
index f5c00c0cd0267ce697edc037113adb3de8bef536..43a53ad0841cb2089e0d3ceee365a69874eec208 100644 (file)
@@ -28,6 +28,7 @@
 #include "rcar_du_kms.h"
 #include "rcar_du_lvdsenc.h"
 #include "rcar_du_regs.h"
+#include "rcar_du_vsp.h"
 
 /* -----------------------------------------------------------------------------
  * Format helpers
@@ -195,12 +196,16 @@ static void rcar_du_output_poll_changed(struct drm_device *dev)
 static int rcar_du_atomic_check(struct drm_device *dev,
                                struct drm_atomic_state *state)
 {
+       struct rcar_du_device *rcdu = dev->dev_private;
        int ret;
 
        ret = drm_atomic_helper_check(dev, state);
        if (ret < 0)
                return ret;
 
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
+               return 0;
+
        return rcar_du_atomic_check_planes(dev, state);
 }
 
@@ -544,9 +549,26 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
                 */
                rgrp->dptsr_planes = rgrp->num_crtcs > 1 ? 0xf0 : 0;
 
-               ret = rcar_du_planes_init(rgrp);
-               if (ret < 0)
-                       return ret;
+               if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+                       ret = rcar_du_planes_init(rgrp);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       /* Initialize the compositors. */
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+               for (i = 0; i < rcdu->num_crtcs; ++i) {
+                       struct rcar_du_vsp *vsp = &rcdu->vsps[i];
+
+                       vsp->index = i;
+                       vsp->dev = rcdu;
+                       rcdu->crtcs[i].vsp = vsp;
+
+                       ret = rcar_du_vsp_init(vsp);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        /* Create the CRTCs. */
index 64450ce13526fc3f1fc094723131f621287a78a6..280f5ca4992741ab3261edce87aa7ce3b0d60971 100644 (file)
@@ -505,12 +505,9 @@ static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
        rcar_du_plane_write(rgrp, index, PnMLR, 0);
 }
 
-void rcar_du_plane_setup(struct rcar_du_plane *plane)
+void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
+                          const struct rcar_du_plane_state *state)
 {
-       struct rcar_du_plane_state *state =
-               to_rcar_plane_state(plane->plane.state);
-       struct rcar_du_group *rgrp = plane->group;
-
        rcar_du_plane_setup_format(rgrp, state->hwindex, state);
        if (state->format->planes == 2)
                rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
index 6a6460dffae4af90849b8ae67ecb47d3a14fc9e3..b18b7b25dbface31873a26a89cd41e6df2b5cf8a 100644 (file)
@@ -76,6 +76,15 @@ int rcar_du_atomic_check_planes(struct drm_device *dev,
 
 int rcar_du_planes_init(struct rcar_du_group *rgrp);
 
-void rcar_du_plane_setup(struct rcar_du_plane *plane);
+void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
+                          const struct rcar_du_plane_state *state);
+
+static inline void rcar_du_plane_setup(struct rcar_du_plane *plane)
+{
+       struct rcar_du_plane_state *state =
+               to_rcar_plane_state(plane->plane.state);
+
+       return __rcar_du_plane_setup(plane->group, state);
+}
 
 #endif /* __RCAR_DU_PLANE_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
new file mode 100644 (file)
index 0000000..7c7d6f2
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * rcar_du_vsp.h  --  R-Car Display Unit VSP-Based Compositor
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/of_platform.h>
+#include <linux/videodev2.h>
+
+#include <media/vsp1.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_vsp.h"
+
+void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
+{
+       const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
+       struct rcar_du_plane_state state = {
+               .state = {
+                       .crtc = &crtc->crtc,
+                       .crtc_x = 0,
+                       .crtc_y = 0,
+                       .crtc_w = mode->hdisplay,
+                       .crtc_h = mode->vdisplay,
+                       .src_x = 0,
+                       .src_y = 0,
+                       .src_w = mode->hdisplay << 16,
+                       .src_h = mode->vdisplay << 16,
+               },
+               .format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
+               .hwindex = crtc->index % 2,
+               .source = RCAR_DU_PLANE_VSPD1,
+               .alpha = 255,
+               .colorkey = 0,
+               .zpos = 0,
+       };
+
+       __rcar_du_plane_setup(crtc->group, &state);
+
+       /* Ensure that the plane source configuration takes effect by requesting
+        * a restart of the group. See rcar_du_plane_atomic_update() for a more
+        * detailed explanation.
+        *
+        * TODO: Check whether this is still needed on Gen3.
+        */
+       crtc->group->need_restart = true;
+
+       vsp1_du_setup_lif(crtc->vsp->vsp, mode->hdisplay, mode->vdisplay);
+}
+
+void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
+{
+       vsp1_du_setup_lif(crtc->vsp->vsp, 0, 0);
+}
+
+void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
+{
+       vsp1_du_atomic_begin(crtc->vsp->vsp);
+}
+
+void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
+{
+       vsp1_du_atomic_flush(crtc->vsp->vsp);
+}
+
+/* Keep the two tables in sync. */
+static const u32 formats_kms[] = {
+       DRM_FORMAT_RGB332,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_BGR888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_BGRA8888,
+       DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV61,
+};
+
+static const u32 formats_v4l2[] = {
+       V4L2_PIX_FMT_RGB332,
+       V4L2_PIX_FMT_ARGB444,
+       V4L2_PIX_FMT_XRGB444,
+       V4L2_PIX_FMT_ARGB555,
+       V4L2_PIX_FMT_XRGB555,
+       V4L2_PIX_FMT_RGB565,
+       V4L2_PIX_FMT_RGB24,
+       V4L2_PIX_FMT_BGR24,
+       V4L2_PIX_FMT_ARGB32,
+       V4L2_PIX_FMT_XRGB32,
+       V4L2_PIX_FMT_ABGR32,
+       V4L2_PIX_FMT_XBGR32,
+       V4L2_PIX_FMT_UYVY,
+       V4L2_PIX_FMT_VYUY,
+       V4L2_PIX_FMT_YUYV,
+       V4L2_PIX_FMT_YVYU,
+       V4L2_PIX_FMT_NV12M,
+       V4L2_PIX_FMT_NV21M,
+       V4L2_PIX_FMT_NV16M,
+       V4L2_PIX_FMT_NV61M,
+};
+
+static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
+{
+       struct rcar_du_vsp_plane_state *state =
+               to_rcar_vsp_plane_state(plane->plane.state);
+       struct drm_framebuffer *fb = plane->plane.state->fb;
+       struct drm_gem_cma_object *gem;
+       struct v4l2_rect src;
+       struct v4l2_rect dst;
+       dma_addr_t paddr[2] = { 0, };
+       u32 pixelformat = 0;
+       unsigned int i;
+
+       src.left = state->state.src_x >> 16;
+       src.top = state->state.src_y >> 16;
+       src.width = state->state.src_w >> 16;
+       src.height = state->state.src_h >> 16;
+
+       dst.left = state->state.crtc_x;
+       dst.top = state->state.crtc_y;
+       dst.width = state->state.crtc_w;
+       dst.height = state->state.crtc_h;
+
+       gem = drm_fb_cma_get_gem_obj(fb, 0);
+       paddr[0] = gem->paddr + fb->offsets[0];
+
+       if (state->format->planes == 2) {
+               gem = drm_fb_cma_get_gem_obj(fb, 1);
+               paddr[1] = gem->paddr + fb->offsets[1];
+       }
+
+       for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
+               if (formats_kms[i] == state->format->fourcc) {
+                       pixelformat = formats_v4l2[i];
+                       break;
+               }
+       }
+
+       WARN_ON(!pixelformat);
+
+       vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat,
+                             fb->pitches[0], paddr, &src, &dst);
+}
+
+static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
+                                         struct drm_plane_state *state)
+{
+       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
+       struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
+       struct rcar_du_device *rcdu = rplane->vsp->dev;
+
+       if (!state->fb || !state->crtc) {
+               rstate->format = NULL;
+               return 0;
+       }
+
+       if (state->src_w >> 16 != state->crtc_w ||
+           state->src_h >> 16 != state->crtc_h) {
+               dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
+               return -EINVAL;
+       }
+
+       rstate->format = rcar_du_format_info(state->fb->pixel_format);
+       if (rstate->format == NULL) {
+               dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
+                       state->fb->pixel_format);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
+                                       struct drm_plane_state *old_state)
+{
+       struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
+
+       if (plane->state->crtc)
+               rcar_du_vsp_plane_setup(rplane);
+       else
+               vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0,
+                                     NULL, NULL);
+}
+
+static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
+       .atomic_check = rcar_du_vsp_plane_atomic_check,
+       .atomic_update = rcar_du_vsp_plane_atomic_update,
+};
+
+static struct drm_plane_state *
+rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+       struct rcar_du_vsp_plane_state *state;
+       struct rcar_du_vsp_plane_state *copy;
+
+       if (WARN_ON(!plane->state))
+               return NULL;
+
+       state = to_rcar_vsp_plane_state(plane->state);
+       copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+       if (copy == NULL)
+               return NULL;
+
+       __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
+
+       return &copy->state;
+}
+
+static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
+                                                  struct drm_plane_state *state)
+{
+       __drm_atomic_helper_plane_destroy_state(plane, state);
+       kfree(to_rcar_vsp_plane_state(state));
+}
+
+static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
+{
+       struct rcar_du_vsp_plane_state *state;
+
+       if (plane->state) {
+               rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
+               plane->state = NULL;
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return;
+
+       state->alpha = 255;
+
+       plane->state = &state->state;
+       plane->state->plane = plane;
+}
+
+static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
+       struct drm_plane_state *state, struct drm_property *property,
+       uint64_t val)
+{
+       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
+       struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
+
+       if (property == rcdu->props.alpha)
+               rstate->alpha = val;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
+       const struct drm_plane_state *state, struct drm_property *property,
+       uint64_t *val)
+{
+       const struct rcar_du_vsp_plane_state *rstate =
+               container_of(state, const struct rcar_du_vsp_plane_state, state);
+       struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
+
+       if (property == rcdu->props.alpha)
+               *val = rstate->alpha;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .reset = rcar_du_vsp_plane_reset,
+       .set_property = drm_atomic_helper_plane_set_property,
+       .destroy = drm_plane_cleanup,
+       .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
+       .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
+       .atomic_set_property = rcar_du_vsp_plane_atomic_set_property,
+       .atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
+};
+
+int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
+{
+       struct rcar_du_device *rcdu = vsp->dev;
+       struct platform_device *pdev;
+       struct device_node *np;
+       unsigned int i;
+       int ret;
+
+       /* Find the VSP device and initialize it. */
+       np = of_parse_phandle(rcdu->dev->of_node, "vsps", vsp->index);
+       if (!np) {
+               dev_err(rcdu->dev, "vsps node not found\n");
+               return -ENXIO;
+       }
+
+       pdev = of_find_device_by_node(np);
+       of_node_put(np);
+       if (!pdev)
+               return -ENXIO;
+
+       vsp->vsp = &pdev->dev;
+
+       ret = vsp1_du_init(vsp->vsp);
+       if (ret < 0)
+               return ret;
+
+        /* The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
+         * 4 RPFs. Hardcode the number of planes to 4 as Gen3 isn't supported
+         * yet.
+         */
+       vsp->num_planes = 4;
+
+       vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
+                                  sizeof(*vsp->planes), GFP_KERNEL);
+       if (!vsp->planes)
+               return -ENOMEM;
+
+       for (i = 0; i < vsp->num_planes; ++i) {
+               enum drm_plane_type type = i ? DRM_PLANE_TYPE_OVERLAY
+                                        : DRM_PLANE_TYPE_PRIMARY;
+               struct rcar_du_vsp_plane *plane = &vsp->planes[i];
+
+               plane->vsp = vsp;
+               plane->index = i;
+
+               ret = drm_universal_plane_init(rcdu->ddev, &plane->plane,
+                                              1 << vsp->index,
+                                              &rcar_du_vsp_plane_funcs,
+                                              formats_kms,
+                                              ARRAY_SIZE(formats_kms), type,
+                                              NULL);
+               if (ret < 0)
+                       return ret;
+
+               drm_plane_helper_add(&plane->plane,
+                                    &rcar_du_vsp_plane_helper_funcs);
+
+               if (type == DRM_PLANE_TYPE_PRIMARY)
+                       continue;
+
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->props.alpha, 255);
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
new file mode 100644 (file)
index 0000000..df3bf38
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * rcar_du_vsp.h  --  R-Car Display Unit VSP-Based Compositor
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_VSP_H__
+#define __RCAR_DU_VSP_H__
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct rcar_du_format_info;
+struct rcar_du_vsp;
+
+struct rcar_du_vsp_plane {
+       struct drm_plane plane;
+       struct rcar_du_vsp *vsp;
+       unsigned int index;
+};
+
+struct rcar_du_vsp {
+       unsigned int index;
+       struct device *vsp;
+       struct rcar_du_device *dev;
+       struct rcar_du_vsp_plane *planes;
+       unsigned int num_planes;
+};
+
+static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
+{
+       return container_of(p, struct rcar_du_vsp_plane, plane);
+}
+
+/**
+ * struct rcar_du_vsp_plane_state - Driver-specific plane state
+ * @state: base DRM plane state
+ * @format: information about the pixel format used by the plane
+ * @alpha: value of the plane alpha property
+ */
+struct rcar_du_vsp_plane_state {
+       struct drm_plane_state state;
+
+       const struct rcar_du_format_info *format;
+
+       unsigned int alpha;
+};
+
+static inline struct rcar_du_vsp_plane_state *
+to_rcar_vsp_plane_state(struct drm_plane_state *state)
+{
+       return container_of(state, struct rcar_du_vsp_plane_state, state);
+}
+
+#ifdef CONFIG_DRM_RCAR_VSP
+int rcar_du_vsp_init(struct rcar_du_vsp *vsp);
+void rcar_du_vsp_enable(struct rcar_du_crtc *crtc);
+void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
+void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
+void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
+#else
+static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp) { return 0; };
+static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { };
+static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
+static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
+static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { };
+#endif
+
+#endif /* __RCAR_DU_VSP_H__ */