Hi, Sam Thank you for looking at the details. I will fix it according to your comment. Best regards Tomohito Esaki On 2021/06/22 0:55, Sam Ravnborg wrote: > Hi Tomohito > > On Mon, Jun 21, 2021 at 03:44:00PM +0900, Tomohito Esaki wrote: >> Virtual DRM splits the resources of an overlay plane into multiple >> virtual devices to allow each plane to be accessed by each process. >> >> This makes it possible to overlay images output from multiple processes >> on a display. For example, one process displays the camera image without >> compositor while another process overlays the compositor's drawing of >> the UI. >> >> The virtual DRM creates standalone virtual device and make DRM planes >> from a master device (e.g. card0) accessible via one or more virtual >> devices. However, these plane are no longer accessible from the original >> device. >> Each virtual device (and plane) can be accessed via a separate >> device file. >> >> Signed-off-by: Tomohito Esaki <etom@xxxxxxxxxx> >> --- >> drivers/gpu/drm/Kconfig | 7 + >> drivers/gpu/drm/Makefile | 1 + >> drivers/gpu/drm/vdrm/vdrm_api.h | 68 +++ >> drivers/gpu/drm/vdrm/vdrm_drv.c | 859 ++++++++++++++++++++++++++++++++ >> drivers/gpu/drm/vdrm/vdrm_drv.h | 80 +++ > > Plase consider making the header files self-contained. > So there are no hdden dependencies between the two. > > Use forward declarations rahter than including header files is possible. > > A few trivial comments in the following. I did not try to follow all the > functionality of the driver and I expect others to comment on the idea. > > Sam > >> 5 files changed, 1015 insertions(+) >> create mode 100644 drivers/gpu/drm/vdrm/vdrm_api.h >> create mode 100644 drivers/gpu/drm/vdrm/vdrm_drv.c >> create mode 100644 drivers/gpu/drm/vdrm/vdrm_drv.h >> >> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig >> index 3c16bd1afd87..ba7f4eeab385 100644 >> --- a/drivers/gpu/drm/Kconfig >> +++ b/drivers/gpu/drm/Kconfig >> @@ -294,6 +294,13 @@ config DRM_VKMS >> >> If M is selected the module will be called vkms. >> >> +config DRM_VDRM >> + tristate "Virtual DRM" >> + depends on DRM >> + help >> + Virtual DRM splits the resources of an overlay plane into multiple >> + virtual devices to allow each plane to be accessed by each process. > Could you look into pulling a bit more info here. You made a very nice > intro to the patch, consider using it in the help text too. > > >> + >> source "drivers/gpu/drm/exynos/Kconfig" >> >> source "drivers/gpu/drm/rockchip/Kconfig" >> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile >> index 5279db4392df..55dbf85e2579 100644 >> --- a/drivers/gpu/drm/Makefile >> +++ b/drivers/gpu/drm/Makefile >> @@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ >> obj-$(CONFIG_DRM_VIA) +=via/ >> obj-$(CONFIG_DRM_VGEM) += vgem/ >> obj-$(CONFIG_DRM_VKMS) += vkms/ >> +obj-$(CONFIG_DRM_VDRM) += vdrm/ > Alphabetic order (mostly) so before vgem/ > >> obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ >> obj-$(CONFIG_DRM_EXYNOS) +=exynos/ >> obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ >> diff --git a/drivers/gpu/drm/vdrm/vdrm_api.h b/drivers/gpu/drm/vdrm/vdrm_api.h >> new file mode 100644 >> index 000000000000..dd4d7e774800 >> --- /dev/null >> +++ b/drivers/gpu/drm/vdrm/vdrm_api.h >> @@ -0,0 +1,68 @@ >> +/* SPDX-License-Identifier: GPL-2.0+ */ >> +/* >> + * vdrm_api.h -- Virtual DRM API >> + * >> + * Copyright (C) 2021 Renesas Electronics Corporation >> + */ >> + >> +#ifndef __VDRM_API__ >> +#define __VDRM_API__ >> + >> +#include <linux/of_device.h> >> +#include <drm/drm_crtc.h> >> + >> +/** >> + * struct vdrm_property_info - Information about the properties passed from >> + * the DRM driver to vDRM >> + * @prop: Parent property to pass to vDRM >> + * @default_val: Default value for the property passed to vDRM >> + */ >> +struct vdrm_property_info { >> + struct drm_property *prop; >> + uint64_t default_val; >> +}; > It would be nice that all structs used inline comments - and then you > are consistent too. > >> + >> +/** >> + * struct vdrm_funcs - Callbacks to parent DRM driver >> + */ >> +struct vdrm_funcs { >> + /** >> + * @dumb_create: >> + * >> + * Called by &drm_driver.dumb_create. Please read the documentation >> + * for the &drm_driver.dumb_create hook for more details. >> + */ >> + int (*dumb_create)(struct drm_file *file, struct drm_device *dev, >> + struct drm_mode_create_dumb *args); >> + >> + /** >> + * @crtc_flush: >> + * >> + * Called by &drm_crtc_helper_funcs.atomic_flush. Please read the >> + * documentation for the &drm_crtc_helper_funcs.atomic_flush hook for >> + * more details. >> + */ >> + void (*crtc_flush)(struct drm_crtc *crtc); >> +}; >> + >> +struct vdrm_device; >> +struct vdrm_display; >> + >> +void vdrm_drv_handle_vblank(struct vdrm_display *vdisplay); >> +void vdrm_drv_finish_page_flip(struct vdrm_display *vdisplay); >> +struct vdrm_device *vdrm_drv_init(struct drm_device *dev, >> + struct device_node *np, int num_props, >> + struct vdrm_property_info *props, >> + const struct vdrm_funcs *funcs); >> +int vdrm_drv_plane_init(struct vdrm_device *vdrm, struct drm_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); >> +struct vdrm_display *vdrm_drv_display_init(struct vdrm_device *vdrm, >> + struct drm_crtc *crtc, >> + struct drm_plane *plane); >> +int vdrm_drv_register(struct vdrm_device *vdrm); >> +void vdrm_drv_fini(struct vdrm_device *vdrm); >> + >> +#endif /* __VDRM_API__ */ >> diff --git a/drivers/gpu/drm/vdrm/vdrm_drv.c b/drivers/gpu/drm/vdrm/vdrm_drv.c >> new file mode 100644 >> index 000000000000..835bdecfc8e6 >> --- /dev/null >> +++ b/drivers/gpu/drm/vdrm/vdrm_drv.c >> @@ -0,0 +1,859 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> +/* >> + * vdrm_drv.c -- Virtual DRM driver >> + * >> + * Copyright (C) 2021 Renesas Electronics Corporation >> + * >> + * This driver is based on drivers/gpu/drm/drm_simple_kms_helper.c. >> + */ >> + >> +#include <linux/of_device.h> >> +#include <drm/drm_atomic.h> >> +#include <drm/drm_atomic_helper.h> >> +#include <drm/drm_atomic_uapi.h> >> +#include <drm/drm_fb_cma_helper.h> >> +#include <drm/drm_fb_helper.h> >> +#include <drm/drm_drv.h> >> +#include <drm/drm_gem_cma_helper.h> >> +#include <drm/drm_plane_helper.h> >> +#include <drm/drm_probe_helper.h> >> +#include <drm/drm_vblank.h> >> +#include <drm/drm_gem_framebuffer_helper.h> >> +#include <video/videomode.h> >> + >> +#include "vdrm_drv.h" >> + >> +static inline struct vdrm_display * >> +to_vdrm_display(struct drm_connector *connector) >> +{ >> + return container_of(connector, struct vdrm_display, connector); >> +} >> + >> +static inline struct vdrm_display * >> +crtc_to_vdrm_display(struct drm_crtc *crtc) >> +{ >> + return container_of(crtc, struct vdrm_display, crtc); >> +} >> + >> +static int vdrm_dumb_create(struct drm_file *file, struct drm_device *dev, >> + struct drm_mode_create_dumb *args) >> +{ >> + struct vdrm_device *vdrm = to_vdrm_device(dev); >> + >> + return vdrm->funcs->dumb_create(file, dev, args); >> +} >> + >> +struct vdrm_framebuffer { >> + struct drm_framebuffer fb; >> + struct drm_framebuffer *parent_fb; >> +}; >> + >> +static inline struct vdrm_framebuffer * >> +to_vdrm_framebuffer(struct drm_framebuffer *fb) >> +{ >> + return container_of(fb, struct vdrm_framebuffer, fb); >> +} >> + >> +static void vdrm_fb_destroy(struct drm_framebuffer *fb) >> +{ >> + struct vdrm_framebuffer *vfb = to_vdrm_framebuffer(fb); >> + >> + vfb->parent_fb->funcs->destroy(vfb->parent_fb); >> + drm_framebuffer_cleanup(fb); >> + kfree(vfb); >> +} >> + >> +static const struct drm_framebuffer_funcs vdrm_fb_funcs = { >> + .destroy = vdrm_fb_destroy, >> +}; >> + >> +static int vdrm_fb_init(struct drm_device *dev, struct vdrm_framebuffer *vfb) >> +{ >> + vfb->fb = *vfb->parent_fb; >> + vfb->fb.dev = dev; >> + >> + return drm_framebuffer_init(dev, &vfb->fb, &vdrm_fb_funcs); >> +} >> + >> +static struct drm_framebuffer * >> +vdrm_fb_create(struct drm_device *dev, struct drm_file *file_priv, >> + const struct drm_mode_fb_cmd2 *mode_cmd) >> +{ >> + struct vdrm_device *vdrm = to_vdrm_device(dev); >> + const struct drm_mode_config_funcs *mode_config_funcs = >> + vdrm->parent->mode_config.funcs; >> + struct vdrm_framebuffer *vfb; >> + struct drm_framebuffer *fb; >> + int ret; >> + >> + vfb = kzalloc(sizeof(*vfb), GFP_KERNEL); >> + if (!vfb) >> + return ERR_PTR(-ENOMEM); >> + >> + fb = mode_config_funcs->fb_create(vdrm->parent, file_priv, mode_cmd); >> + if (IS_ERR(fb)) { >> + kfree(vfb); >> + return fb; >> + } >> + >> + vfb->parent_fb = fb; >> + ret = vdrm_fb_init(dev, vfb); >> + if (ret) { >> + fb->funcs->destroy(fb); >> + kfree(vfb); >> + return ERR_PTR(ret); >> + } >> + >> + return &vfb->fb; >> +} >> + >> +static const struct drm_mode_config_funcs vdrm_mode_config_funcs = { >> + .fb_create = vdrm_fb_create, >> + .atomic_check = drm_atomic_helper_check, >> + .atomic_commit = drm_atomic_helper_commit, >> +}; >> + >> +static struct drm_display_mode *vdrm_create_mode(struct vdrm_display *disp) >> +{ >> + struct drm_display_mode *mode; >> + struct videomode videomode; >> + >> + mode = drm_mode_create(&disp->dev->ddev); >> + if (!mode) >> + return NULL; >> + >> + memset(&videomode, 0, sizeof(videomode)); >> + videomode.hactive = disp->plane_info.width; >> + videomode.vactive = disp->plane_info.height; >> + videomode.pixelclock = >> + disp->parent_crtc->state->adjusted_mode.crtc_clock * 1000; >> + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; >> + drm_display_mode_from_videomode(&videomode, mode); >> + > If you create a drm_display_mode here then you can loose the videomode > dependency. And I do not need to tell you that a Kconfig dependency is missing > to use videomode. Do not forget to drop the include. > > >> + return mode; >> +} >> + >> +static int vdrm_connector_get_mode(struct drm_connector *connector) >> +{ >> + struct vdrm_display *disp = to_vdrm_display(connector); >> + struct drm_display_mode *mode = vdrm_create_mode(disp); >> + >> + if (!mode) >> + return 0; >> + >> + drm_mode_probed_add(connector, mode); >> + return 1; >> +} >> + >> +static const struct drm_connector_helper_funcs vdrm_conn_helper_funcs = { >> + .get_modes = vdrm_connector_get_mode, >> +}; >> + >> +/* >> + * TODO: >> + * At the time this callback is called, the parent CRTC must be connected. >> + * Since this callback will not be called when detect() callback of the >> + * parent connector is called, vDRM driver desn't support hotplug. >> + * In the future, it is necessary that hotplug is supported. >> + */ >> +static enum drm_connector_status >> +vdrm_connector_detect(struct drm_connector *connector, bool force) >> +{ >> + struct vdrm_display *disp = to_vdrm_display(connector); >> + struct vdrm_device *vdrm = to_vdrm_device(connector->dev); >> + struct drm_connector *conn; >> + struct drm_connector_list_iter conn_iter; >> + >> + drm_connector_list_iter_begin(vdrm->parent, &conn_iter); >> + drm_for_each_connector_iter(conn, &conn_iter) { >> + if (!conn->state) >> + continue; >> + >> + if (conn->state->crtc == disp->parent_crtc) { >> + drm_connector_list_iter_end(&conn_iter); >> + return connector_status_connected; >> + } >> + } >> + drm_connector_list_iter_end(&conn_iter); >> + return connector_status_disconnected; >> +} >> + >> +static const struct drm_connector_funcs vdrm_conn_funcs = { >> + .reset = drm_atomic_helper_connector_reset, >> + .detect = vdrm_connector_detect, >> + .fill_modes = drm_helper_probe_single_connector_modes, >> + .destroy = drm_connector_cleanup, >> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, >> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, >> +}; >> + >> +static void vdrm_drv_finish_page_flip_internal(struct vdrm_display *disp) >> +{ >> + struct drm_device *dev = &disp->dev->ddev; >> + struct drm_pending_vblank_event *event; >> + unsigned long flags; >> + >> + spin_lock_irqsave(&dev->event_lock, flags); >> + event = disp->event; >> + disp->event = NULL; >> + spin_unlock_irqrestore(&dev->event_lock, flags); >> + >> + if (event == NULL) >> + return; >> + >> + spin_lock_irqsave(&dev->event_lock, flags); >> + drm_crtc_send_vblank_event(&disp->crtc, event); >> + spin_unlock_irqrestore(&dev->event_lock, flags); >> + >> + if (disp->vblank_count) { >> + drm_crtc_vblank_put(&disp->crtc); >> + disp->vblank_count--; >> + } >> +} >> + >> +static void vdrm_plane_update(struct drm_plane *plane, >> + struct drm_atomic_state *state) >> +{ >> + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); >> + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); >> + struct drm_crtc *vcrtc_old_state = old_state->crtc; >> + struct drm_crtc *vcrtc_plane_state = new_state->crtc; >> + struct drm_crtc *crtc; >> + struct vdrm_display *vdisplay; >> + >> + crtc = (old_state->crtc ? old_state->crtc : new_state->crtc); >> + if (WARN_ON(!crtc)) >> + return; >> + >> + vdisplay = crtc_to_vdrm_display(crtc); >> + >> + old_state->crtc = vdisplay->parent_crtc; >> + new_state->crtc = vdisplay->parent_crtc; >> + >> + new_state->dst.x1 += vdisplay->plane_info.x; >> + new_state->dst.y1 += vdisplay->plane_info.y; >> + vdisplay->parent_plane_helper_funcs->atomic_update(plane, state); >> + >> + old_state->crtc = vcrtc_old_state; >> + new_state->crtc = vcrtc_plane_state; >> +} >> + >> +static struct vdrm_display * >> +vdrm_plane_find_display(struct vdrm_device *vdrm, struct drm_plane *plane) >> +{ >> + struct vdrm_display *disp; >> + >> + list_for_each_entry(disp, &vdrm->disps, head) { >> + if (disp->plane == plane) >> + return disp; >> + } >> + >> + return NULL; >> +} >> + >> +static void vdrm_plane_reset(struct drm_plane *plane) >> +{ >> + struct vdrm_device *vdrm = to_vdrm_device(plane->dev); >> + struct vdrm_display *disp; >> + >> + disp = vdrm_plane_find_display(vdrm, plane); >> + if (WARN_ON(!disp)) >> + return; >> + >> + disp->parent_plane_funcs->reset(plane); >> + plane->state->zpos = disp->plane_info.z; >> +} >> + >> +static struct drm_property * >> +vdrm_find_parent_property(struct vdrm_device *vdrm, struct drm_property *prop) >> +{ >> + int i; >> + >> + for (i = 0; i < vdrm->num_props; i++) { >> + if (vdrm->props[i].prop == prop) >> + return vdrm->props[i].parent_prop; >> + } >> + >> + return NULL; >> +} >> + >> +static int vdrm_plane_set_property(struct drm_plane *plane, >> + struct drm_plane_state *state, >> + struct drm_property *property, >> + uint64_t val) >> +{ >> + struct vdrm_device *vdrm = to_vdrm_device(plane->dev); >> + struct vdrm_display *disp; >> + struct drm_property *parent_prop; >> + >> + disp = vdrm_plane_find_display(vdrm, plane); >> + if (WARN_ON(!disp)) >> + return -EINVAL; >> + >> + parent_prop = vdrm_find_parent_property(vdrm, property); >> + if (parent_prop && disp->parent_plane_funcs->atomic_set_property) >> + return disp->parent_plane_funcs->atomic_set_property(plane, >> + state, >> + parent_prop, >> + val); >> + >> + if (vdrm->plane_props.offset_x == property) { >> + if (val > disp->parent_crtc->mode.hdisplay) >> + return -EINVAL; >> + disp->plane_info.x = val; >> + } else if (vdrm->plane_props.offset_y == property) { >> + if (val > disp->parent_crtc->mode.vdisplay) >> + return -EINVAL; >> + disp->plane_info.y = val; >> + } else if (vdrm->plane_props.width == property) { >> + if (val > disp->parent_crtc->mode.hdisplay) >> + return -EINVAL; >> + disp->plane_info.width = val; >> + } else if (vdrm->plane_props.height == property) { >> + if (val > disp->parent_crtc->mode.vdisplay) >> + return -EINVAL; >> + disp->plane_info.height = val; >> + } else { >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int vdrm_plane_get_property(struct drm_plane *plane, >> + const struct drm_plane_state *state, >> + struct drm_property *property, >> + uint64_t *val) >> +{ >> + struct vdrm_device *vdrm = to_vdrm_device(plane->dev); >> + struct vdrm_display *disp; >> + struct drm_property *parent_prop; >> + >> + disp = vdrm_plane_find_display(vdrm, plane); >> + if (WARN_ON(!disp)) >> + return -EINVAL; >> + >> + parent_prop = vdrm_find_parent_property(vdrm, property); >> + if (parent_prop && disp->parent_plane_funcs->atomic_get_property) >> + return disp->parent_plane_funcs->atomic_get_property(plane, >> + state, >> + parent_prop, >> + val); >> + >> + if (vdrm->plane_props.offset_x == property) >> + *val = disp->plane_info.x; >> + else if (vdrm->plane_props.offset_y == property) >> + *val = disp->plane_info.y; >> + else if (vdrm->plane_props.width == property) >> + *val = disp->plane_info.width; >> + else if (vdrm->plane_props.height == property) >> + *val = disp->plane_info.height; >> + else >> + return -EINVAL; >> + >> + return 0; >> +} >> + >> +static int vdrm_crtc_check(struct drm_crtc *crtc, >> + struct drm_atomic_state *state) >> +{ >> + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, >> + crtc); >> + bool has_primary = crtc_state->plane_mask & >> + drm_plane_mask(crtc->primary); >> + >> + /* We always want to have an active plane with an active CRTC */ >> + if (has_primary != crtc_state->enable) >> + return -EINVAL; >> + >> + return drm_atomic_add_affected_planes(state, crtc); >> +} >> + >> +static void vdrm_crtc_flush(struct drm_crtc *crtc, >> + struct drm_atomic_state *state) >> +{ >> + struct vdrm_display *disp = crtc_to_vdrm_display(crtc); >> + struct vdrm_device *vdrm = disp->dev; >> + >> + if (crtc->state->event) { >> + struct drm_device *dev = crtc->dev; >> + unsigned long flags; >> + >> + if (disp->crtc_enabled) { >> + WARN_ON(drm_crtc_vblank_get(crtc) != 0); >> + disp->vblank_count++; >> + } >> + >> + spin_lock_irqsave(&dev->event_lock, flags); >> + disp->event = crtc->state->event; >> + crtc->state->event = NULL; >> + spin_unlock_irqrestore(&dev->event_lock, flags); >> + } >> + >> + if (vdrm->funcs->crtc_flush) >> + vdrm->funcs->crtc_flush(disp->parent_crtc); >> +} >> + >> +static void vdrm_crtc_enable(struct drm_crtc *crtc, >> + struct drm_atomic_state *state) >> +{ >> + struct vdrm_display *disp = crtc_to_vdrm_display(crtc); >> + >> + drm_crtc_vblank_on(crtc); >> + disp->crtc_enabled = true; >> +} >> + >> +static void vdrm_crtc_disable(struct drm_crtc *crtc, >> + struct drm_atomic_state *state) >> +{ >> + struct vdrm_display *disp = crtc_to_vdrm_display(crtc); >> + unsigned long flags; >> + bool pending; >> + >> + disp->crtc_enabled = false; >> + drm_crtc_vblank_off(crtc); >> + >> + spin_lock_irqsave(&crtc->dev->event_lock, flags); >> + pending = disp->event != NULL; >> + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); >> + >> + if (!wait_event_timeout(disp->flip_wait, !pending, >> + msecs_to_jiffies(50))) { >> + DRM_WARN("VDRM: page flip timeout\n"); >> + vdrm_drv_finish_page_flip_internal(disp); >> + } >> + >> + spin_lock_irq(&crtc->dev->event_lock); >> + if (crtc->state->event) { >> + drm_crtc_send_vblank_event(crtc, crtc->state->event); >> + crtc->state->event = NULL; >> + } >> + spin_unlock_irq(&crtc->dev->event_lock); >> +} >> + >> +static const struct drm_crtc_helper_funcs vdrm_crtc_helper_funcs = { >> + .atomic_check = vdrm_crtc_check, >> + .atomic_flush = vdrm_crtc_flush, >> + .atomic_enable = vdrm_crtc_enable, >> + .atomic_disable = vdrm_crtc_disable, >> +}; >> + >> +static int vdrm_crtc_enable_vblank(struct drm_crtc *crtc) >> +{ >> + struct vdrm_display *disp; >> + >> + disp = crtc_to_vdrm_display(crtc); >> + disp->vblank_enabled = true; >> + >> + return 0; >> +} >> + >> +static void vdrm_crtc_disable_vblank(struct drm_crtc *crtc) >> +{ >> + struct vdrm_display *disp; >> + >> + disp = crtc_to_vdrm_display(crtc); >> + disp->vblank_enabled = false; >> +} >> + >> +static const struct drm_crtc_funcs vdrm_crtc_funcs = { >> + .reset = drm_atomic_helper_crtc_reset, >> + .destroy = drm_crtc_cleanup, >> + .set_config = drm_atomic_helper_set_config, >> + .page_flip = drm_atomic_helper_page_flip, >> + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, >> + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, >> + .enable_vblank = vdrm_crtc_enable_vblank, >> + .disable_vblank = vdrm_crtc_disable_vblank, >> +}; >> +static const struct drm_encoder_funcs vdrm_encoder_funcs = { >> + .destroy = drm_encoder_cleanup, >> +}; >> + >> +static int vdrm_properties_init(struct vdrm_device *vdrm, int num_props, >> + struct vdrm_property_info *props) >> +{ >> + int i; >> + unsigned int w = vdrm->ddev.mode_config.max_width; >> + unsigned int h = vdrm->ddev.mode_config.max_height; >> + >> + vdrm->plane_props.offset_x = >> + drm_property_create_range(&vdrm->ddev, 0, "vdrm_offset_x", 0, w); >> + if (!vdrm->plane_props.offset_x) >> + return -1; >> + vdrm->plane_props.offset_y = >> + drm_property_create_range(&vdrm->ddev, 0, "vdrm_offset_y", 0, h); >> + if (!vdrm->plane_props.offset_y) >> + return -1; >> + vdrm->plane_props.width = >> + drm_property_create_range(&vdrm->ddev, 0, "vdrm_width", 1, w); >> + if (!vdrm->plane_props.width) >> + return -1; >> + vdrm->plane_props.height = >> + drm_property_create_range(&vdrm->ddev, 0, "vdrm_height", 1, h); >> + if (!vdrm->plane_props.height) >> + return -1; >> + >> + if (num_props == 0) >> + return 0; >> + >> + vdrm->props = devm_kzalloc(vdrm->parent->dev, >> + sizeof(*vdrm->props) * num_props, >> + GFP_KERNEL); >> + if (!vdrm->props) >> + return -ENOMEM; >> + >> + for (i = 0; i < num_props; i++) { >> + struct drm_property *p = props[i].prop; >> + >> + vdrm->props[i].prop = >> + drm_property_create_range(&vdrm->ddev, p->flags, >> + p->name, p->values[0], >> + p->values[1]); >> + if (!vdrm->props[i].prop) >> + goto err; >> + >> + vdrm->props[i].parent_prop = p; >> + vdrm->props[i].default_val = props[i].default_val; >> + } >> + vdrm->num_props = num_props; >> + >> + return 0; >> + >> +err: >> + for (i--; i >= 0; i--) >> + drm_property_destroy(&vdrm->ddev, vdrm->props[i].prop); >> + devm_kfree(vdrm->parent->dev, vdrm->props); >> + return -1; >> +} >> + >> +static int vdrm_of_get_plane(struct device_node *np, >> + int *x, int *y, int *width, int *height, int *z) >> +{ >> + struct device_node *child; >> + int ret; >> + >> + child = of_get_next_child(np, NULL); >> + if (!child) >> + return -ENODEV; >> + >> + ret = of_property_read_u32(child, "x", x); >> + ret |= of_property_read_u32(child, "y", y); >> + ret |= of_property_read_u32(child, "width", width); >> + ret |= of_property_read_u32(child, "height", height); >> + ret |= of_property_read_u32(child, "zpos", z); >> + >> + of_node_put(child); >> + return ret; >> +} >> + >> +static void vdrm_dump(struct vdrm_device *vdrm) >> +{ >> + struct vdrm_display *disp; >> + >> + DRM_INFO("Virtual DRM Info:\n"); >> + list_for_each_entry(disp, &vdrm->disps, head) { >> + DRM_INFO("\tCONNECTOR: %d\n", >> + disp->connector.base.id); >> + DRM_INFO("\tCRTC: %d\n", >> + disp->crtc.base.id); >> + DRM_INFO("\tENCODER: %d\n", >> + disp->encoder.base.id); >> + DRM_INFO("\tPLANE: %d\n", >> + disp->plane->base.id); >> + DRM_INFO("\tParent CRTC: %d\n", >> + disp->parent_crtc->base.id); >> + } >> +} >> + >> +/** >> + * vdrm_drv_handle_vblank - handle a vblank event for vDRM >> + * @vdisplay: vDRM display object >> + */ >> +void vdrm_drv_handle_vblank(struct vdrm_display *vdisplay) >> +{ >> + if (vdisplay->vblank_enabled) >> + drm_crtc_handle_vblank(&vdisplay->crtc); >> +} >> + >> +/** >> + * vdrm_drv_finish_page_flip - handle a page flip event for vDRM >> + * @vdisplay: vDRM display object >> + */ >> +void vdrm_drv_finish_page_flip(struct vdrm_display *vdisplay) >> +{ >> + vdrm_drv_finish_page_flip_internal(vdisplay); >> +} >> + >> +DEFINE_DRM_GEM_CMA_FOPS(vdrm_fops); >> + >> +static struct drm_driver vdrm_driver = { >> + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, >> + .dumb_create = vdrm_dumb_create, >> + .fops = &vdrm_fops, >> + .name = "virt-drm", >> + .desc = "Virtual DRM driver", >> + .date = "20201104", >> + .major = 1, >> + .minor = 0, >> +}; >> + >> +/** >> + * vdrm_drv_init - Initialize vDRM device >> + * @dev: parent DRM device >> + * @np: vDRM device node in DTB >> + * @num_props: number of parent property objects >> + * @props: parent plane properties used in vDRM >> + * @funcs: callbacks for vDRM >> + * >> + * Allocates a new vDRM device, initializes mode_config of the vDRM device >> + * and allocates property objects. Not initialize plane, crtc, encoder and >> + * connector. Initialization of plane must be done in vdrm_drv_plane_init() >> + * and initialization of crtc, encoder and connector must be done by >> + * vdrm_drv_display_init(). Also, after initializing the plane, crtc, >> + * connector, and encoder, register vDRM device must be done by >> + * vdrm_drv_register(). >> + * >> + * Returns: >> + * vDRM object (&vdrm_device) on success, error code encoded into the pointer >> + * on failure. >> + */ >> +struct vdrm_device *vdrm_drv_init(struct drm_device *dev, >> + struct device_node *np, int num_props, >> + struct vdrm_property_info *props, >> + const struct vdrm_funcs *funcs) >> +{ >> + struct vdrm_device *vdrm; >> + struct vdrm_plane_info plane_info; >> + int ret; >> + >> + if (!of_device_is_compatible(np, "virt-drm")) >> + return ERR_PTR(-ENODEV); >> + >> + /* get plane information from device tree */ >> + ret = vdrm_of_get_plane(np, &plane_info.x, &plane_info.y, >> + &plane_info.width, &plane_info.height, >> + &plane_info.z); >> + if (ret < 0) { >> + DRM_WARN("VDRM: failed get plane node of %s\n", >> + of_node_full_name(np)); >> + return ERR_PTR(ret); >> + } >> + >> + vdrm_driver.prime_handle_to_fd = dev->driver->prime_handle_to_fd; >> + vdrm_driver.prime_fd_to_handle = dev->driver->prime_fd_to_handle; >> + vdrm_driver.gem_prime_import_sg_table = >> + dev->driver->gem_prime_import_sg_table; >> + vdrm_driver.gem_prime_mmap = dev->driver->gem_prime_mmap; >> + >> + vdrm = devm_drm_dev_alloc(dev->dev, &vdrm_driver, struct vdrm_device, >> + ddev); >> + if (IS_ERR(vdrm)) >> + return vdrm; >> + >> + vdrm->parent = dev; >> + vdrm->funcs = funcs; >> + vdrm->of_plane_info = plane_info; >> + >> + INIT_LIST_HEAD(&vdrm->disps); >> + >> + ret = drmm_mode_config_init(&vdrm->ddev); >> + if (ret) >> + goto failed; >> + >> + vdrm->ddev.mode_config.min_width = 0; >> + vdrm->ddev.mode_config.min_height = 0; >> + vdrm->ddev.mode_config.max_width = 8190; >> + vdrm->ddev.mode_config.max_height = 8190; >> + vdrm->ddev.mode_config.normalize_zpos = true; >> + vdrm->ddev.mode_config.funcs = &vdrm_mode_config_funcs; >> + >> + ret = vdrm_properties_init(vdrm, num_props, props); >> + if (ret < 0) >> + goto failed; >> + >> + drm_dev_set_unique(&vdrm->ddev, of_node_full_name(np)); >> + return vdrm; >> + >> +failed: >> + kfree(vdrm); >> + return ERR_PTR(ret); >> +} >> + >> +/** >> + * vdrm_drv_plane_init - Initialize the plane used by vDRM >> + * @vdrm: vDRM object >> + * @plane: plane to assign to vDRM >> + * @funcs: callbacks for the plane >> + * @helper_funcs: helper vtable to set for plane >> + * @formats: color formats >> + * @num_formats: number of color formats >> + * @max_zpos: max value for zpos property of plane >> + * >> + * Initializes a plane object of PRIMARY type by drm_universal_plane_init() >> + * and initializes @plane's properties. The property passed by vdrm_drv_init() >> + * is set to @plane. >> + * >> + * Returns: >> + * Zero on success, error code on failure. >> + */ >> +int vdrm_drv_plane_init(struct vdrm_device *vdrm, struct drm_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) >> +{ >> + struct vdrm_display *disp; >> + int i, ret; >> + >> + disp = kzalloc(sizeof(*disp), GFP_KERNEL); >> + if (!disp) >> + return -ENOMEM; >> + >> + disp->dev = vdrm; >> + disp->plane = plane; >> + disp->plane_info = vdrm->of_plane_info; >> + >> + disp->parent_plane_funcs = funcs; >> + disp->parent_plane_helper_funcs = helper_funcs; >> + disp->plane_funcs = *funcs; >> + disp->plane_helper_funcs = *helper_funcs; >> + >> + disp->plane_funcs.reset = vdrm_plane_reset; >> + disp->plane_funcs.atomic_set_property = vdrm_plane_set_property; >> + disp->plane_funcs.atomic_get_property = vdrm_plane_get_property; >> + disp->plane_helper_funcs.atomic_update = vdrm_plane_update; >> + >> + drm_plane_helper_add(disp->plane, &disp->plane_helper_funcs); >> + ret = drm_universal_plane_init(&vdrm->ddev, plane, 0, >> + &disp->plane_funcs, formats, >> + num_formats, NULL, >> + DRM_PLANE_TYPE_PRIMARY, NULL); >> + if (ret) { >> + kfree(disp); >> + return ret; >> + } >> + >> + drm_plane_create_alpha_property(plane); >> + drm_plane_create_zpos_property(plane, disp->plane_info.z, 0, max_zpos); >> + drm_object_attach_property(&plane->base, >> + vdrm->plane_props.offset_x, >> + disp->plane_info.x); >> + drm_object_attach_property(&plane->base, >> + vdrm->plane_props.offset_y, >> + disp->plane_info.y); >> + drm_object_attach_property(&plane->base, >> + vdrm->plane_props.width, >> + disp->plane_info.width); >> + drm_object_attach_property(&plane->base, >> + vdrm->plane_props.height, >> + disp->plane_info.height); >> + for (i = 0; i < vdrm->num_props; i++) { >> + drm_object_attach_property(&plane->base, >> + vdrm->props[i].prop, >> + vdrm->props[i].default_val); >> + } >> + >> + INIT_LIST_HEAD(&disp->head); >> + list_add_tail(&disp->head, &vdrm->disps); >> + vdrm->num_crtcs++; >> + return 0; >> +} >> + >> +/** >> + * vdrm_drv_display_init - Initialize the vDRM display object >> + * @vdrm: vDRM object >> + * @crtc: parent crtc to be linked with the vDRM crtc >> + * @plane: plane assigned to vDRM >> + * >> + * Initializes crtc, connector and encorder, and links @crtc to crtc of vDRM. >> + * >> + * Returns: >> + * vDRM display object on success, error code encoded into the pointer on >> + * failure. >> + */ >> +struct vdrm_display *vdrm_drv_display_init(struct vdrm_device *vdrm, >> + struct drm_crtc *crtc, >> + struct drm_plane *plane) >> +{ >> + struct vdrm_display *disp; >> + int ret; >> + >> + disp = vdrm_plane_find_display(vdrm, plane); >> + if (!disp) >> + return ERR_PTR(-EINVAL); >> + >> + drm_crtc_helper_add(&disp->crtc, &vdrm_crtc_helper_funcs); >> + ret = drm_crtc_init_with_planes(&vdrm->ddev, &disp->crtc, plane, NULL, >> + &vdrm_crtc_funcs, NULL); >> + if (ret) >> + return ERR_PTR(ret); >> + >> + drm_connector_helper_add(&disp->connector, &vdrm_conn_helper_funcs); >> + ret = drm_connector_init(&vdrm->ddev, &disp->connector, >> + &vdrm_conn_funcs, DRM_MODE_CONNECTOR_VIRTUAL); >> + if (ret) >> + return ERR_PTR(ret); >> + >> + disp->encoder.possible_crtcs = drm_crtc_mask(&disp->crtc); >> + ret = drm_encoder_init(&vdrm->ddev, &disp->encoder, &vdrm_encoder_funcs, >> + DRM_MODE_ENCODER_NONE, NULL); >> + if (ret) >> + return ERR_PTR(ret); >> + >> + ret = drm_connector_attach_encoder(&disp->connector, &disp->encoder); >> + if (ret) >> + return ERR_PTR(ret); >> + >> + init_waitqueue_head(&disp->flip_wait); >> + disp->parent_crtc = crtc; >> + >> + return disp; >> +} >> + >> +/** >> + * vdrm_drv_register - Register vDRM device >> + * @vdrm: vDRM object >> + * >> + * Returns: >> + * Zero on success, error code on failure. >> + */ >> +int vdrm_drv_register(struct vdrm_device *vdrm) >> +{ >> + int ret; >> + struct drm_device *dev = &vdrm->ddev; >> + >> + ret = drm_vblank_init(dev, vdrm->num_crtcs); >> + if (ret) >> + return ret; >> + >> + drm_mode_config_reset(dev); >> + >> + ret = drm_dev_register(dev, 0); >> + if (ret) >> + return ret; >> + >> + dev->irq_enabled = true; >> + >> + DRM_INFO("Virtual Device is initialized.\n"); >> + >> + vdrm_dump(vdrm); >> + >> + return 0; >> +} >> + >> +/** >> + * vdrm_drv_fini - release vDRM resources >> + * @vdrm: vDRM object >> + */ >> +void vdrm_drv_fini(struct vdrm_device *vdrm) >> +{ >> + struct vdrm_display *disp; >> + >> + if (vdrm->ddev.registered) >> + drm_dev_unregister(&vdrm->ddev); >> + drm_mode_config_cleanup(&vdrm->ddev); >> + >> + list_for_each_entry(disp, &vdrm->disps, head) >> + kfree(disp); >> +} >> diff --git a/drivers/gpu/drm/vdrm/vdrm_drv.h b/drivers/gpu/drm/vdrm/vdrm_drv.h >> new file mode 100644 >> index 000000000000..67b8e02efddf >> --- /dev/null >> +++ b/drivers/gpu/drm/vdrm/vdrm_drv.h >> @@ -0,0 +1,80 @@ >> +/* SPDX-License-Identifier: GPL-2.0+ */ >> +/* >> + * vdrm_drv.h -- Virtual DRM driver >> + * >> + * Copyright (C) 2021 Renesas Electronics Corporation >> + */ >> + >> +#ifndef __VDRM_DRV_H__ >> +#define __VDRM_DRV_H__ >> + >> +#include <drm/drm_device.h> >> + >> +#include "vdrm_api.h" >> + >> +struct vdrm_property { >> + struct drm_property *prop; >> + struct drm_property *parent_prop; >> + uint64_t default_val; >> +}; >> + >> +struct vdrm_plane_info { >> + int x; >> + int y; >> + unsigned int width; >> + unsigned int height; >> + unsigned int z; >> +}; >> + >> +struct vdrm_device; >> + >> +struct vdrm_display { >> + struct drm_connector connector; >> + struct drm_crtc crtc; >> + struct drm_crtc *parent_crtc; >> + struct drm_plane *plane; >> + struct drm_encoder encoder; >> + struct drm_pending_vblank_event *event; >> + struct vdrm_device *dev; >> + bool vblank_enabled; >> + wait_queue_head_t flip_wait; >> + bool crtc_enabled; >> + int vblank_count; >> + >> + struct list_head head; >> + >> + struct vdrm_plane_info plane_info; >> + >> + const struct drm_plane_funcs *parent_plane_funcs; >> + const struct drm_plane_helper_funcs *parent_plane_helper_funcs; >> + struct drm_plane_funcs plane_funcs; >> + struct drm_plane_helper_funcs plane_helper_funcs; >> +}; >> + >> +struct vdrm_device { >> + struct drm_device ddev; >> + struct drm_device *parent; >> + >> + int num_crtcs; >> + struct list_head disps; >> + >> + const struct vdrm_funcs *funcs; >> + struct vdrm_property *props; >> + int num_props; >> + >> + struct { >> + struct drm_property *offset_x; >> + struct drm_property *offset_y; >> + struct drm_property *width; >> + struct drm_property *height; >> + } plane_props; >> + >> + struct vdrm_plane_info of_plane_info; >> +}; >> + >> +static inline struct vdrm_device *to_vdrm_device(struct drm_device *dev) >> +{ >> + return container_of(dev, struct vdrm_device, ddev); >> +} >> + >> +#endif /* __VDRM_DRV_H__ */ >> -- >> 2.25.1