From: Laurent Pinchart Date: Mon, 7 Sep 2015 14:14:58 +0000 (+0300) Subject: drm: rcar-du: Expose the VSP1 compositor through KMS planes X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=6d62ef3ac30be756244b458fa5bd8befb35a48d4;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git drm: rcar-du: Expose the VSP1 compositor through KMS planes 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 --- diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 96dcd4a78951..daec18e2ffee 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -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. diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 05de1c4097af..827711e28226 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -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 diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index e2560aa26ef4..a862cf5568cd 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -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; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index dbc3a3ef9aa6..6f08b7e7db06 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -21,6 +21,7 @@ #include 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) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index e10b4f0d8f83..a81eb3281e7f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -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; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index f5c00c0cd026..43a53ad0841c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -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. */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 64450ce13526..280f5ca49927 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -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, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 6a6460dffae4..b18b7b25dbfa 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -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 index 000000000000..7c7d6f265a09 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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, ©->state); + + return ©->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 index 000000000000..df3bf3805c69 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h @@ -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 +#include + +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__ */