diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 1be15d2321eb..ad6f6f19bbf8 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -93,6 +93,14 @@ config ROCKCHIP_RGB and serial RGB format to panel or connect to a conversion chip. say Y to enable its driver. +config DRM_ROCKCHIP_VVOP + tristate "Rockchip virtual VOP drm driver" + depends on DRM_ROCKCHIP + help + Say y here if you want use some module of rockchip drm, but + don't need a real vop driver(et: you just want rockchip drm + gem driver to allocate memory). + source "drivers/gpu/drm/rockchip/ebc-dev/Kconfig" source "drivers/gpu/drm/rockchip/rk618/Kconfig" source "drivers/gpu/drm/rockchip/rk628/Kconfig" diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index fc90b481e10f..83c7360fc058 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -18,6 +18,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o rockchipdrm-$(CONFIG_ROCKCHIP_DRM_TVE) += rockchip_drm_tve.o rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o +rockchipdrm-$(CONFIG_DRM_ROCKCHIP_VVOP) += rockchip_drm_vvop.o obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 0dfe33ae057f..611231eff992 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -58,7 +58,11 @@ * **********************************************************************/ +#if IS_ENABLED(CONFIG_DRM_ROCKCHIP_VVOP) +static bool is_support_iommu = false; +#else static bool is_support_iommu = true; +#endif static struct drm_driver rockchip_drm_driver; struct rockchip_drm_mode_set { @@ -2095,8 +2099,10 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev) int ret; ret = rockchip_drm_platform_of_probe(dev); +#if !IS_ENABLED(CONFIG_DRM_ROCKCHIP_VVOP) if (ret) return ret; +#endif match = rockchip_drm_match_add(dev); if (IS_ERR(match)) @@ -2157,6 +2163,9 @@ static int __init rockchip_drm_init(void) int ret; num_rockchip_sub_drivers = 0; +#if IS_ENABLED(CONFIG_DRM_ROCKCHIP_VVOP) + ADD_ROCKCHIP_SUB_DRIVER(vvop_platform_driver, CONFIG_DRM_ROCKCHIP_VVOP); +#else ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP); ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_DRM_ROCKCHIP); ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, @@ -2172,7 +2181,7 @@ static int __init rockchip_drm_init(void) ADD_ROCKCHIP_SUB_DRIVER(rockchip_tve_driver, CONFIG_ROCKCHIP_DRM_TVE); ADD_ROCKCHIP_SUB_DRIVER(rockchip_rgb_driver, CONFIG_ROCKCHIP_RGB); - +#endif ret = platform_register_drivers(rockchip_sub_drivers, num_rockchip_sub_drivers); if (ret) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 156d56ffe9ad..9e7648e0bd01 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -251,5 +251,6 @@ extern struct platform_driver rockchip_lvds_driver; extern struct platform_driver rockchip_tve_driver; extern struct platform_driver vop_platform_driver; extern struct platform_driver vop2_platform_driver; +extern struct platform_driver vvop_platform_driver; extern struct platform_driver rockchip_rgb_driver; #endif /* _ROCKCHIP_DRM_DRV_H_ */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vvop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vvop.c new file mode 100644 index 000000000000..9c74221d7a05 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vvop.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Virtual vop driver based on vkms + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "virtual-vop" + +#define XRES_MIN 32 +#define YRES_MIN 32 + +#define XRES_DEF 1024 +#define YRES_DEF 768 + +#define XRES_MAX 8192 +#define YRES_MAX 8192 + + +struct vvop { + struct device *dev; + struct drm_device *drm_dev; + struct platform_device *pdev; + struct drm_crtc crtc; + struct drm_plane *plane; + struct drm_encoder encoder; + struct drm_connector connector; + struct hrtimer vblank_hrtimer; + ktime_t period_ns; + struct drm_pending_vblank_event *event; + +}; + +static const u32 vvop_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +#define drm_crtc_to_vvop(crtc) \ + container_of(crtc, struct vvop, crtc) + + +static const struct drm_plane_funcs vvop_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static void vvop_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + +static const struct drm_plane_helper_funcs vvop_plane_helper_funcs = { + .atomic_update = vvop_plane_atomic_update, +}; + +static struct drm_plane *vvop_plane_init(struct vvop *vvop) +{ + struct drm_device *dev = vvop->drm_dev; + struct drm_plane *plane; + const u32 *formats; + int ret, nformats; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) + return ERR_PTR(-ENOMEM); + + formats = vvop_formats; + nformats = ARRAY_SIZE(vvop_formats); + + ret = drm_universal_plane_init(dev, plane, 0, + &vvop_plane_funcs, + formats, nformats, + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + kfree(plane); + return ERR_PTR(ret); + } + + drm_plane_helper_add(plane, &vvop_plane_helper_funcs); + + return plane; +} + +static enum hrtimer_restart vvop_vblank_simulate(struct hrtimer *timer) +{ + struct vvop *vvop = container_of(timer, struct vvop, vblank_hrtimer); + struct drm_crtc *crtc = &vvop->crtc; + bool ret; + + ret = drm_crtc_handle_vblank(crtc); + if (!ret) + DRM_ERROR("vvop failure on handling vblank"); + + hrtimer_forward_now(&vvop->vblank_hrtimer, vvop->period_ns); + + return HRTIMER_RESTART; +} + +static int vvop_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct vvop *vvop = drm_crtc_to_vvop(crtc); + + drm_calc_timestamping_constants(crtc, &crtc->mode); + + hrtimer_init(&vvop->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vvop->vblank_hrtimer.function = &vvop_vblank_simulate; + vvop->period_ns = ktime_set(-1, vblank->framedur_ns); + hrtimer_start(&vvop->vblank_hrtimer, vvop->period_ns, HRTIMER_MODE_REL); + + return 0; +} + +static void vvop_disable_vblank(struct drm_crtc *crtc) +{ + struct vvop *vvop = drm_crtc_to_vvop(crtc); + + hrtimer_cancel(&vvop->vblank_hrtimer); +} + +static void vvop_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs vvop_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vvop_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_encoder_funcs vvop_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int vvop_conn_get_modes(struct drm_connector *connector) +{ + int count; + + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); + + return count; +} + +static const struct drm_connector_helper_funcs vvop_conn_helper_funcs = { + .get_modes = vvop_conn_get_modes, +}; + +static const struct drm_crtc_funcs vvop_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = drm_crtc_cleanup, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = vvop_enable_vblank, + .disable_vblank = vvop_disable_vblank, +}; + +static void vvop_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + drm_crtc_vblank_on(crtc); +} + +static void vvop_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + drm_crtc_vblank_off(crtc); +} + +static void vvop_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + unsigned long flags; + + if (crtc->state->event) { + spin_lock_irqsave(&crtc->dev->event_lock, flags); + + if (drm_crtc_vblank_get(crtc) != 0) + drm_crtc_send_vblank_event(crtc, crtc->state->event); + else + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + crtc->state->event = NULL; + } +} + +static const struct drm_crtc_helper_funcs vvop_crtc_helper_funcs = { + .atomic_flush = vvop_crtc_atomic_flush, + .atomic_enable = vvop_crtc_atomic_enable, + .atomic_disable = vvop_crtc_atomic_disable, +}; + +static int vvop_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, struct drm_plane *cursor) +{ + int ret; + + ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, + &vvop_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to init CRTC\n"); + return ret; + } + + drm_crtc_helper_add(crtc, &vvop_crtc_helper_funcs); + + return ret; +} + +static int vvop_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm_dev = data; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_plane *primary; + struct drm_crtc *crtc; + struct vvop *vvop; + int ret; + + vvop = devm_kzalloc(dev, sizeof(*vvop), GFP_KERNEL); + if (!vvop) + return -ENOMEM; + + vvop->dev = dev; + vvop->drm_dev = drm_dev; + connector = &vvop->connector; + encoder = &vvop->encoder; + crtc = &vvop->crtc; + + dev_set_drvdata(dev, vvop); + + primary = vvop_plane_init(vvop); + if (IS_ERR(primary)) + return PTR_ERR(primary); + vvop->plane = primary; + + ret = vvop_crtc_init(drm_dev, crtc, primary, NULL); + if (ret) + goto err_crtc; + + ret = drm_connector_init(drm_dev, connector, &vvop_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to init connector\n"); + goto err_connector; + } + + drm_connector_helper_add(connector, &vvop_conn_helper_funcs); + + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_connector_register; + } + + ret = drm_encoder_init(drm_dev, encoder, &vvop_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to init encoder\n"); + goto err_encoder; + } + encoder->possible_crtcs = 1; + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) { + DRM_ERROR("Failed to attach connector to encoder\n"); + goto err_attach; + } + + return 0; + +err_attach: + drm_encoder_cleanup(encoder); + +err_encoder: + drm_connector_unregister(connector); + +err_connector_register: + drm_connector_cleanup(connector); + +err_connector: + drm_crtc_cleanup(crtc); + +err_crtc: + drm_plane_cleanup(primary); + + return ret; +} + +static void vvop_unbind(struct device *dev, struct device *master, void *data) +{ + struct vvop *vvop = dev_get_drvdata(dev); + + drm_plane_cleanup(vvop->plane); + drm_connector_cleanup(&vvop->connector); + drm_crtc_cleanup(&vvop->crtc); +} + +const struct component_ops vvop_component_ops = { + .bind = vvop_bind, + .unbind = vvop_unbind, +}; + +static int vvop_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + DRM_DEV_INFO(dev, "virtual vop probe\n"); + + return component_add(dev, &vvop_component_ops); +} + +static int vvop_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vvop_component_ops); + + return 0; +} + + +struct platform_driver vvop_platform_driver = { + .probe = vvop_probe, + .remove = vvop_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init vvop_init(void) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + DRM_ERROR("failed to register platform device %s\n", DRIVER_NAME); + return PTR_ERR(pdev); + } + + return 0; +} + +static void __exit vvop_exit(void) +{ +} + +rootfs_initcall(vvop_init); +module_exit(vvop_exit); + +MODULE_AUTHOR("Andy Yan "); +MODULE_LICENSE("GPL");