Skip to content
Permalink
Browse files
rcar-du: Add support virtual DRM device
In order to use vDRM, it is necessary that the vDRM device is registered
to du decice in the device tree.
The "vdrms" key is added in du node and the vDRM device node is specified.
For example:
----------
& du {
    ...
    vdrms = <&vdrm0>;
};
----------

Signed-off-by: Tomohito Esaki <etom@igel.co.jp>
  • Loading branch information
tesaki authored and intel-lab-lkp committed Jun 21, 2021
1 parent 236425f commit cc44235a16ab2596f4eae5c4e9011e884ce89691
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 0 deletions.
@@ -50,3 +50,7 @@ config DRM_RCAR_WRITEBACK
bool
default y if ARM64
depends on DRM_RCAR_DU

config DRM_RCAR_DU_VDRM
tristate "Virtual DRM for R-Car DU"
depends on DRM_RCAR_DU && DRM_VDRM
@@ -14,6 +14,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
rcar_du_of_lvds_r8a7796.dtb.o
rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
rcar-du-drm-$(CONFIG_DRM_RCAR_DU_VDRM) += rcar_du_vdrm.o

obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
@@ -32,6 +32,11 @@
#include "rcar_du_vsp.h"
#include "rcar_lvds.h"

#include "rcar_du_vdrm.h"
#ifdef CONFIG_DRM_RCAR_DU_VDRM
#include "../vdrm/vdrm_api.h"
#endif

static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
{
struct rcar_du_device *rcdu = rcrtc->dev;
@@ -1293,5 +1298,42 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,

rcar_du_crtc_crc_init(rcrtc);

INIT_LIST_HEAD(&rcrtc->vdrm_displays);
ret = rcar_du_vdrm_crtc_init(rcrtc, swindex);
if (ret < 0) {
dev_err(rcdu->dev,
"failed to initialize crtc %u for vDRM\n", swindex);
return ret;
}

return 0;
}

int rcar_du_crtc_add_vdrm_display(struct rcar_du_crtc *rcrtc,
struct vdrm_display *vdisplay)
{
struct rcar_du_vdrm_display *disp;

disp = kzalloc(sizeof(*disp), GFP_KERNEL);
if (!disp)
return -ENOMEM;

disp->display = vdisplay;
INIT_LIST_HEAD(&disp->head);
list_add_tail(&disp->head, &rcrtc->vdrm_displays);

return 0;
}

void rcar_du_crtc_remove_vdrm_displays(struct rcar_du_crtc *rcrtc)
{
struct rcar_du_vdrm_display *disp, *tmp;

if (!rcrtc->dev)
return;

list_for_each_entry_safe(disp, tmp, &rcrtc->vdrm_displays, head) {
list_del(&disp->head);
kfree(disp);
}
}
@@ -21,6 +21,12 @@

struct rcar_du_group;
struct rcar_du_vsp;
struct vdrm_display;

struct rcar_du_vdrm_display {
struct vdrm_display *display;
struct list_head head;
};

/**
* struct rcar_du_crtc - the CRTC, representing a DU superposition processor
@@ -43,6 +49,7 @@ struct rcar_du_vsp;
* @vsp: VSP feeding video to this CRTC
* @vsp_pipe: index of the VSP pipeline feeding video to this CRTC
* @writeback: the writeback connector
* @vdrm_displays: display list for virtual DRM
*/
struct rcar_du_crtc {
struct drm_crtc crtc;
@@ -73,6 +80,8 @@ struct rcar_du_crtc {
unsigned int sources_count;

struct drm_writeback_connector writeback;

struct list_head vdrm_displays;
};

#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
@@ -111,4 +120,8 @@ void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc);

void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set);

int rcar_du_crtc_add_vdrm_display(struct rcar_du_crtc *rcrtc,
struct vdrm_display *vdisplay);
void rcar_du_crtc_remove_vdrm_displays(struct rcar_du_crtc *rcrtc);

#endif /* __RCAR_DU_CRTC_H__ */
@@ -29,6 +29,7 @@
#include "rcar_du_kms.h"
#include "rcar_du_of.h"
#include "rcar_du_regs.h"
#include "rcar_du_vdrm.h"

/* -----------------------------------------------------------------------------
* Device Information
@@ -552,6 +553,8 @@ static int rcar_du_remove(struct platform_device *pdev)
struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
struct drm_device *ddev = &rcdu->ddev;

rcar_du_vdrms_fini(rcdu);

drm_dev_unregister(ddev);

drm_kms_helper_poll_fini(ddev);
@@ -584,6 +587,11 @@ static int rcar_du_probe(struct platform_device *pdev)
if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio);

/* Initialize the vDRM device */
ret = rcar_du_vdrms_init(rcdu);
if (ret < 0)
return ret;

/* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
if (ret < 0) {
@@ -607,6 +615,11 @@ static int rcar_du_probe(struct platform_device *pdev)

drm_fbdev_generic_setup(&rcdu->ddev, 32);

/* Register the vDRM device */
ret = rcar_du_vdrms_register(rcdu);
if (ret)
DRM_WARN("Setup virtual device failed.\n");

return 0;

error:
@@ -97,6 +97,9 @@ struct rcar_du_device {
unsigned int dpad0_source;
unsigned int dpad1_source;
unsigned int vspd1_sink;

struct vdrm_device **vdrms;
int num_vdrms;
};

static inline struct rcar_du_device *to_rcar_du_device(struct drm_device *dev)
@@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* rcar_du_vdrm.c -- R-Car Display Unit Virtual DRMs
*
* Copyright (C) 2021 Renesas Electronics Corporation
*/

#include <linux/of_device.h>

#include <drm/drm_print.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <media/vsp1.h>

#include "rcar_du_vdrm.h"
#include "rcar_du_kms.h"
#include "rcar_du_crtc.h"
#include "rcar_du_vsp.h"

static int rcar_du_vdrm_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
/*
* TODO:
* This is Warkarround.
* In the future, this function will be removed.
* The vdrm will be modified to directly call the dumb_create
* callback of the du driver.
*/
unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
unsigned int align;

/*
* The R8A7779 DU requires a 16 pixels pitch alignment as documented.
*/
align = 16 * args->bpp / 8;

args->pitch = roundup(min_pitch, align);

return drm_gem_cma_dumb_create_internal(file, dev, args);
}

static void rcar_du_vdrm_crtc_flush(struct drm_crtc *crtc)
{
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);

rcar_du_vsp_atomic_flush(rcrtc);
}

static struct vdrm_funcs vdrm_funcs = {
.dumb_create = rcar_du_vdrm_dumb_create,
.crtc_flush = rcar_du_vdrm_crtc_flush,
};

void rcar_du_vdrm_crtc_complete(struct rcar_du_crtc *crtc, unsigned int status)
{
struct rcar_du_vdrm_display *disp;

list_for_each_entry(disp, &crtc->vdrm_displays, head) {
vdrm_drv_handle_vblank(disp->display);
if (status & VSP1_DU_STATUS_COMPLETE)
vdrm_drv_finish_page_flip(disp->display);
}
}

int rcar_du_vdrm_count(struct rcar_du_device *rcdu)
{
const struct device_node *np = rcdu->dev->of_node;
int num;

num = of_property_count_u32_elems(np, "vdrms");
if (num < 0)
return 0;

return num;
}

int rcar_du_vdrms_init(struct rcar_du_device *rcdu)
{
struct vdrm_device *vdrm;
int num_vdrms;
int i, ret;

num_vdrms = rcar_du_vdrm_count(rcdu);
if (num_vdrms == 0)
return 0;

rcdu->vdrms = kcalloc(num_vdrms, sizeof(vdrm), GFP_KERNEL);
if (!rcdu->vdrms)
return -1;

DRM_INFO("VDRM: num vdrm = %d\n", num_vdrms);

for (i = 0; i < num_vdrms; i++) {
struct of_phandle_args args;
const struct device_node *np = rcdu->dev->of_node;

ret = of_parse_phandle_with_fixed_args(np, "vdrms", 0, i,
&args);
if (ret < 0) {
DRM_WARN("VDRM: failed get vdrm%d.\n", i);
goto err;
}

vdrm = vdrm_drv_init(&rcdu->ddev, args.np, 0, NULL,
&vdrm_funcs);
of_node_put(args.np);
if (IS_ERR(vdrm)) {
ret = PTR_ERR(vdrm);
goto err;
}

rcdu->vdrms[i] = vdrm;
rcdu->num_vdrms++;
}

return 0;

err:
rcar_du_vdrms_fini(rcdu);
rcdu->num_vdrms = 0;
return ret;
}

int rcar_du_vdrm_plane_init(struct vdrm_device *vdrm,
struct rcar_du_vsp_plane *plane,
const struct drm_plane_funcs *funcs,
const struct drm_plane_helper_funcs *helper_funcs,
const u32 *formats, unsigned int num_formats,
int max_zpos)
{
return vdrm_drv_plane_init(vdrm, &plane->plane, funcs,
helper_funcs, formats, num_formats,
max_zpos);
}

int rcar_du_vdrm_crtc_init(struct rcar_du_crtc *crtc, int index)
{
struct rcar_du_device *rcdu;
int i;

rcdu = crtc->dev;
for (i = 0; i < rcdu->num_vdrms; i++) {
struct vdrm_display *vdisplay;
int plane_index = crtc->vsp->num_planes + i;
struct drm_plane *plane =
&crtc->vsp->planes[plane_index].plane;

vdisplay = vdrm_drv_display_init(rcdu->vdrms[i], &crtc->crtc,
plane);
if (IS_ERR(vdisplay))
return PTR_ERR(vdisplay);

rcar_du_crtc_add_vdrm_display(crtc, vdisplay);
}

return 0;
}

int rcar_du_vdrms_register(struct rcar_du_device *rcdu)
{
int i, ret;

for (i = 0; i < rcdu->num_vdrms; i++) {
ret = vdrm_drv_register(rcdu->vdrms[i]);
if (ret)
return ret;
}

return 0;
}

void rcar_du_vdrms_fini(struct rcar_du_device *rcdu)
{
int i;

for (i = 0; i < rcdu->num_vdrms; i++) {
if (rcdu->vdrms[i])
vdrm_drv_fini(rcdu->vdrms[i]);
}

for (i = 0; i < RCAR_DU_MAX_CRTCS; i++)
rcar_du_crtc_remove_vdrm_displays(&rcdu->crtcs[i]);

kfree(rcdu->vdrms);
}

0 comments on commit cc44235

Please sign in to comment.