Message ID | 20231204123315.28456-4-keith.zhao@starfivetech.com (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | DRM driver for verisilicon | expand |
Hi Keith, On Mo, 2023-12-04 at 20:33 +0800, Keith Zhao wrote: > Implement drm device registration interface > > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com> > --- [...] > diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig > new file mode 100644 > index 000000000000..e10fa97635aa > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/Kconfig > @@ -0,0 +1,13 @@ > +# SPDX-License-Identifier: GPL-2.0 > +config DRM_VERISILICON > + tristate "DRM Support for VeriSilicon" > + depends on DRM > + select DRM_KMS_HELPER > + select DRM_GEM_DMA_HELPER > + select CMA > + select DMA_CMA > + help > + Choose this option if you have a VeriSilicon soc chipset. This seems a bit generic. Doesn't the VeriSilicon display controller IP used on JH7110 have a product name? [...] > diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c > new file mode 100644 > index 000000000000..4fb1f29ef84b > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/vs_drv.c > @@ -0,0 +1,316 @@ > +// SPDX-License-Identifier: GPL-2.0 [...] > +static void vs_drm_device_release_clocks(void *res) > +{ > + struct vs_drm_device *priv = res; > + unsigned int i; > + > + reset_control_bulk_assert(priv->nrsts, priv->rst_vout); > + > + for (i = 0; i < priv->clk_count; ++i) { > + if (priv->clks[i]) { > + clk_disable_unprepare(priv->clks[i]); > + clk_put(priv->clks[i]); > + } > + } Why not use the bulk API for clk as well? [...] > +static int vs_drm_device_init_clocks(struct vs_drm_device *priv) > +{ > + struct drm_device *dev = &priv->base; > + struct platform_device *pdev = to_platform_device(dev->dev); > + struct device_node *of_node = pdev->dev.of_node; > + struct clk *clock; > + unsigned int i; > + int ret; > + > + if (dev_get_platdata(&pdev->dev) || !of_node) > + return 0; > + > + priv->nrsts = ARRAY_SIZE(priv->rst_vout); > + for (int i = 0; i < priv->nrsts; ++i) > + priv->rst_vout[i].id = vout_resets[i]; > + ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts, > + priv->rst_vout); I would request resets and clocks in _probe(). If component_bind_all() returns -EPROBE_DEFER because of a still missing DSI panel backlight or similar, this doesn't have to be done multiple times. > + if (ret) { > + drm_err(dev, "Failed to get reset controls\n"); > + return ret; > + } > + > + priv->clk_count = of_clk_get_parent_count(of_node); > + if (!priv->clk_count) > + return 0; > + > + priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]), > + GFP_KERNEL); > + if (!priv->clks) > + return -ENOMEM; > + > + for (i = 0; i < priv->clk_count; ++i) { > + clock = of_clk_get(of_node, i); > + if (IS_ERR(clock)) { > + ret = PTR_ERR(clock); > + if (ret == -EPROBE_DEFER) > + goto err; > + drm_err(dev, "clock %u not found: %d\n", i, ret); > + continue; > + } > + ret = clk_prepare_enable(clock); > + if (ret) { > + drm_err(dev, "failed to enable clock %u: %d\n", > + i, ret); > + clk_put(clock); > + continue; > + } > + priv->clks[i] = clock; > + } > + > + ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout); > + if (ret) > + return ret; This should goto err, otherwise clocks are left enabled. > + > + return devm_add_action_or_reset(&pdev->dev, > + vs_drm_device_release_clocks, > + priv); > + > +err: > + while (i) { > + --i; > + if (priv->clks[i]) { > + clk_disable_unprepare(priv->clks[i]); > + clk_put(priv->clks[i]); > + } > + } > + return ret; > +} > + > +static int vs_drm_bind(struct device *dev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct vs_drm_device *priv; > + int ret; > + struct drm_device *drm_dev; > + > + /* Remove existing drivers that may own the framebuffer memory. */ > + ret = drm_aperture_remove_framebuffers(&vs_drm_driver); > + if (ret) > + return ret; > + > + priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base); > + if (IS_ERR(priv)) > + return PTR_ERR(priv); > + > + priv->pitch_alignment = 64; Why is this a variable instead of a constant? > + ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40)); > + if (ret) > + return ret; > + > + drm_dev = &priv->base; > + platform_set_drvdata(pdev, drm_dev); > + > + ret = vs_drm_device_init_clocks(priv); > + if (ret) > + return ret; > + > + vs_mode_config_init(drm_dev); > + > + /* Now try and bind all our sub-components */ > + ret = component_bind_all(dev, drm_dev); > + if (ret) > + return ret; > + > + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); > + if (ret) > + return ret; Missing component_unbind_all(), see below. > + > + drm_mode_config_reset(drm_dev); > + > + drm_kms_helper_poll_init(drm_dev); > + > + ret = drm_dev_register(drm_dev, 0); > + if (ret) > + return ret; Missing drm_kms_helper_poll_fini(), see below. > + > + drm_fbdev_generic_setup(drm_dev, 32); > + > + return 0; Here I'd expect an error path calling drm_kms_helper_poll_fini() and component_unbind_all() as appropriate. regards Philipp
On Mon, 4 Dec 2023 at 14:33, Keith Zhao <keith.zhao@starfivetech.com> wrote: > > Implement drm device registration interface > > Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com> > --- > MAINTAINERS | 1 + > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/verisilicon/Kconfig | 13 + > drivers/gpu/drm/verisilicon/Makefile | 6 + > drivers/gpu/drm/verisilicon/vs_drv.c | 316 +++++++++++++++++++++++ > drivers/gpu/drm/verisilicon/vs_drv.h | 42 +++ > drivers/gpu/drm/verisilicon/vs_modeset.c | 39 +++ > drivers/gpu/drm/verisilicon/vs_modeset.h | 10 + > 9 files changed, 430 insertions(+) > create mode 100644 drivers/gpu/drm/verisilicon/Kconfig > create mode 100644 drivers/gpu/drm/verisilicon/Makefile > create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c > create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h > create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c > create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 7caaadb83f3f..8dc9ebfe4605 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -6887,6 +6887,7 @@ L: dri-devel@lists.freedesktop.org > S: Maintained > T: git git://anongit.freedesktop.org/drm/drm-misc > F: Documentation/devicetree/bindings/display/starfive/ > +F: drivers/gpu/drm/verisilicon/ > > DRM DRIVER FOR TI DLPC3433 MIPI DSI TO DMD BRIDGE > M: Jagan Teki <jagan@amarulasolutions.com> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 3eee8636f847..e8d53c2e7c86 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -394,6 +394,8 @@ source "drivers/gpu/drm/solomon/Kconfig" > > source "drivers/gpu/drm/sprd/Kconfig" > > +source "drivers/gpu/drm/verisilicon/Kconfig" > + > config DRM_HYPERV > tristate "DRM Support for Hyper-V synthetic video device" > depends on DRM && PCI && MMU && HYPERV > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 8e1bde059170..29e04acded06 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/ > obj-y += solomon/ > obj-$(CONFIG_DRM_SPRD) += sprd/ > obj-$(CONFIG_DRM_LOONGSON) += loongson/ > +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/ > diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig > new file mode 100644 > index 000000000000..e10fa97635aa > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/Kconfig > @@ -0,0 +1,13 @@ > +# SPDX-License-Identifier: GPL-2.0 > +config DRM_VERISILICON > + tristate "DRM Support for VeriSilicon" > + depends on DRM > + select DRM_KMS_HELPER > + select DRM_GEM_DMA_HELPER > + select CMA > + select DMA_CMA > + help > + Choose this option if you have a VeriSilicon soc chipset. > + This driver provides VeriSilicon kernel mode > + setting and buffer management. It does not > + provide 2D or 3D acceleration. > diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile > new file mode 100644 > index 000000000000..d785a1dfaa7f > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/Makefile > @@ -0,0 +1,6 @@ > +# SPDX-License-Identifier: GPL-2.0 > + > +vs_drm-objs := vs_drv.o \ > + vs_modeset.o > + > +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o > diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c > new file mode 100644 > index 000000000000..4fb1f29ef84b > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/vs_drv.c > @@ -0,0 +1,316 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. > + */ > +#include <linux/clk.h> > +#include <linux/component.h> > +#include <linux/of_clk.h> > +#include <linux/pm_runtime.h> > + > +#include <drm/drm_aperture.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_fb_helper.h> > +#include <drm/drm_fbdev_generic.h> > +#include <drm/drm_file.h> > +#include <drm/drm_gem_dma_helper.h> > +#include <drm/drm_module.h> > +#include <drm/drm_of.h> > +#include <drm/drm_probe_helper.h> > +#include <drm/drm_vblank.h> > + > +#include "vs_drv.h" > +#include "vs_modeset.h" > + > +#define DRV_NAME "verisilicon" > +#define DRV_DESC "Verisilicon DRM driver" > +#define DRV_DATE "20230516" > +#define DRV_MAJOR 1 > +#define DRV_MINOR 0 > + > +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev, > + struct drm_mode_create_dumb *args) > +{ > + struct vs_drm_device *priv = to_vs_drm_private(dev); > + unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8); > + > + args->pitch = ALIGN(pitch, priv->pitch_alignment); > + return drm_gem_dma_dumb_create_internal(file, dev, args); > +} > + > +DEFINE_DRM_GEM_FOPS(vs_drm_fops); > + > +static struct drm_driver vs_drm_driver = { > + .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, > + > + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create), > + > + .fops = &vs_drm_fops, > + .name = DRV_NAME, > + .desc = DRV_DESC, > + .date = DRV_DATE, > + .major = DRV_MAJOR, > + .minor = DRV_MINOR, > +}; > + > +static void vs_drm_device_release_clocks(void *res) > +{ > + struct vs_drm_device *priv = res; > + unsigned int i; > + > + reset_control_bulk_assert(priv->nrsts, priv->rst_vout); > + > + for (i = 0; i < priv->clk_count; ++i) { > + if (priv->clks[i]) { > + clk_disable_unprepare(priv->clks[i]); > + clk_put(priv->clks[i]); > + } > + } > +} > + > +static const char * const vout_resets[] = { > + "axi", > + "ahb", > + "core", > +}; > + > +static int vs_drm_device_init_clocks(struct vs_drm_device *priv) > +{ > + struct drm_device *dev = &priv->base; > + struct platform_device *pdev = to_platform_device(dev->dev); > + struct device_node *of_node = pdev->dev.of_node; > + struct clk *clock; > + unsigned int i; > + int ret; > + > + if (dev_get_platdata(&pdev->dev) || !of_node) > + return 0; Drop dev_get_platdata(), you don't seem to use it. > + > + priv->nrsts = ARRAY_SIZE(priv->rst_vout); > + for (int i = 0; i < priv->nrsts; ++i) > + priv->rst_vout[i].id = vout_resets[i]; > + ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts, > + priv->rst_vout); > + if (ret) { > + drm_err(dev, "Failed to get reset controls\n"); > + return ret; > + } > + > + priv->clk_count = of_clk_get_parent_count(of_node); > + if (!priv->clk_count) > + return 0; > + > + priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]), > + GFP_KERNEL); > + if (!priv->clks) > + return -ENOMEM; > + > + for (i = 0; i < priv->clk_count; ++i) { > + clock = of_clk_get(of_node, i); > + if (IS_ERR(clock)) { > + ret = PTR_ERR(clock); > + if (ret == -EPROBE_DEFER) > + goto err; > + drm_err(dev, "clock %u not found: %d\n", i, ret); > + continue; > + } > + ret = clk_prepare_enable(clock); > + if (ret) { > + drm_err(dev, "failed to enable clock %u: %d\n", > + i, ret); > + clk_put(clock); > + continue; > + } > + priv->clks[i] = clock; > + } This can be rewritten as devm_clk_bulk_get_all() > + > + ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout); > + if (ret) > + return ret; It is a bad idea to mix get_resources kind of function with the actual resource control. Please move reset deassertion upwards. > + > + return devm_add_action_or_reset(&pdev->dev, > + vs_drm_device_release_clocks, > + priv); > + > +err: > + while (i) { > + --i; > + if (priv->clks[i]) { > + clk_disable_unprepare(priv->clks[i]); > + clk_put(priv->clks[i]); > + } > + } > + return ret; > +} > + > +static int vs_drm_bind(struct device *dev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct vs_drm_device *priv; > + int ret; > + struct drm_device *drm_dev; > + > + /* Remove existing drivers that may own the framebuffer memory. */ > + ret = drm_aperture_remove_framebuffers(&vs_drm_driver); > + if (ret) > + return ret; If anything happens during the probe, your platform is left with no display output. I think it might be better to call this when the driver has acquired all the resources and is ready to start hw init. > + > + priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base); > + if (IS_ERR(priv)) > + return PTR_ERR(priv); > + > + priv->pitch_alignment = 64; > + > + ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40)); > + if (ret) > + return ret; > + > + drm_dev = &priv->base; > + platform_set_drvdata(pdev, drm_dev); > + > + ret = vs_drm_device_init_clocks(priv); > + if (ret) > + return ret; > + > + vs_mode_config_init(drm_dev); > + > + /* Now try and bind all our sub-components */ > + ret = component_bind_all(dev, drm_dev); > + if (ret) > + return ret; > + > + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); > + if (ret) > + return ret; > + > + drm_mode_config_reset(drm_dev); > + > + drm_kms_helper_poll_init(drm_dev); > + > + ret = drm_dev_register(drm_dev, 0); > + if (ret) > + return ret; Teardown path is missing. > + > + drm_fbdev_generic_setup(drm_dev, 32); > + > + return 0; > +} > + > +static void vs_drm_unbind(struct device *dev) > +{ > + struct drm_device *drm_dev = dev_get_drvdata(dev); > + > + drm_dev_unregister(drm_dev); > + drm_kms_helper_poll_fini(drm_dev); > + component_unbind_all(drm_dev->dev, drm_dev); > +} > + > +static const struct component_master_ops vs_drm_ops = { > + .bind = vs_drm_bind, > + .unbind = vs_drm_unbind, > +}; > + > +static struct platform_driver *drm_sub_drivers[] = { > +}; > + > +static struct component_match *vs_drm_match_add(struct device *dev) > +{ > + struct component_match *match = NULL; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) { > + struct platform_driver *drv = drm_sub_drivers[i]; > + struct device *p = NULL, *d; > + > + while ((d = platform_find_device_by_driver(p, &drv->driver))) { > + put_device(p); > + > + drm_of_component_match_add(dev, &match, component_compare_of, > + d->of_node); > + p = d; > + } > + put_device(p); > + } > + > + return match ? match : ERR_PTR(-ENODEV); > +} > + > +static int vs_drm_platform_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct component_match *match; > + > + match = vs_drm_match_add(dev); > + if (IS_ERR(match)) > + return PTR_ERR(match); > + > + return component_master_add_with_match(dev, &vs_drm_ops, match); I wonder if you can use drm_of_component_probe() instead? > +} > + > +static int vs_drm_platform_remove(struct platform_device *pdev) I think this should be void vs_drm_platform_remove() and .remove_new > +{ > + component_master_del(&pdev->dev, &vs_drm_ops); > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int vs_drm_suspend(struct device *dev) > +{ > + return drm_mode_config_helper_suspend(dev_get_drvdata(dev)); > +} > + > +static int vs_drm_resume(struct device *dev) > +{ > + drm_mode_config_helper_resume(dev_get_drvdata(dev)); return drm_mode_config_helper_resume() > + > + return 0; > +} > +#endif > + > +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume); > + > +static const struct of_device_id vs_drm_dt_ids[] = { > + { .compatible = "starfive,display-subsystem", }, > + { }, > +}; > + > +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids); > + > +static struct platform_driver vs_drm_platform_driver = { > + .probe = vs_drm_platform_probe, > + .remove = vs_drm_platform_remove, > + > + .driver = { > + .name = DRV_NAME, > + .of_match_table = vs_drm_dt_ids, > + .pm = &vs_drm_pm_ops, > + }, > +}; > + > +static int __init vs_drm_init(void) > +{ > + int ret; > + > + ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers)); > + if (ret) > + return ret; > + > + ret = drm_platform_driver_register(&vs_drm_platform_driver); > + if (ret) > + platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers)); > + > + return ret; > +} > + > +static void __exit vs_drm_fini(void) > +{ > + platform_driver_unregister(&vs_drm_platform_driver); > + platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers)); > +} > + > +late_initcall_sync(vs_drm_init); > +module_exit(vs_drm_fini); > + > +MODULE_DESCRIPTION("VeriSilicon DRM Driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h > new file mode 100644 > index 000000000000..ea2189772980 > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/vs_drv.h > @@ -0,0 +1,42 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. > + */ > + > +#ifndef __VS_DRV_H__ > +#define __VS_DRV_H__ > + > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <drm/drm_drv.h> > +#include <drm/drm_gem.h> > +#include <drm/drm_managed.h> > +#include <linux/clk.h> > +#include <linux/reset.h> > + > +enum rst_vout { > + RST_VOUT_AXI = 0, > + RST_VOUT_AHB, > + RST_VOUT_CORE, > + RST_VOUT_NUM > +}; Do you need these values? They can easily get out of sync with vout_rsts. > + > +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/ > +struct vs_drm_device { > + struct drm_device base; > + unsigned int pitch_alignment; > + /* clocks */ > + unsigned int clk_count; > + struct clk **clks; > + > + struct reset_control_bulk_data rst_vout[RST_VOUT_NUM]; > + int nrsts; > +}; > + > +static inline struct vs_drm_device * > +to_vs_drm_private(const struct drm_device *dev) > +{ > + return container_of(dev, struct vs_drm_device, base); > +} > + > +#endif /* __VS_DRV_H__ */ > diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c > new file mode 100644 > index 000000000000..eaf406c1b7c7 > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c > @@ -0,0 +1,39 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. > + */ > + > +#include <linux/module.h> > + > +#include <drm/drm_damage_helper.h> > +#include <drm/drm_fb_helper.h> > +#include <drm/drm_gem_framebuffer_helper.h> > + > +#include "vs_modeset.h" > + > +static const struct drm_mode_config_funcs vs_mode_config_funcs = { > + .fb_create = drm_gem_fb_create, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = drm_atomic_helper_commit, > +}; > + > +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = { > + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, > +}; > + > +void vs_mode_config_init(struct drm_device *dev) > +{ > + int ret; > + > + ret = drmm_mode_config_init(dev); > + if (ret) > + return; > + > + dev->mode_config.min_width = 0; > + dev->mode_config.min_height = 0; > + dev->mode_config.max_width = 4096; > + dev->mode_config.max_height = 4096; > + > + dev->mode_config.funcs = &vs_mode_config_funcs; > + dev->mode_config.helper_private = &vs_mode_config_helpers; > +} > diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h > new file mode 100644 > index 000000000000..bd04f81d2ad2 > --- /dev/null > +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h > @@ -0,0 +1,10 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd. > + */ > + > +#ifndef __VS_MODESET_H__ > +#define __VS_MODESET_H__ > + > +void vs_mode_config_init(struct drm_device *dev); > +#endif /* __VS_FB_H__ */ > -- > 2.34.1 >
hi Philipp: On 2023/12/4 21:30, Philipp Zabel wrote: > Hi Keith, > > On Mo, 2023-12-04 at 20:33 +0800, Keith Zhao wrote: >> Implement drm device registration interface >> >> Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com> >> --- > [...] >> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig >> new file mode 100644 >> index 000000000000..e10fa97635aa >> --- /dev/null >> +++ b/drivers/gpu/drm/verisilicon/Kconfig >> @@ -0,0 +1,13 @@ >> +# SPDX-License-Identifier: GPL-2.0 >> +config DRM_VERISILICON >> + tristate "DRM Support for VeriSilicon" >> + depends on DRM >> + select DRM_KMS_HELPER >> + select DRM_GEM_DMA_HELPER >> + select CMA >> + select DMA_CMA >> + help >> + Choose this option if you have a VeriSilicon soc chipset. > > This seems a bit generic. Doesn't the VeriSilicon display controller IP > used on JH7110 have a product name? yes , there is a product name "dc8200", I will match it. > > [...] >> diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c >> new file mode 100644 >> index 000000000000..4fb1f29ef84b >> --- /dev/null >> +++ b/drivers/gpu/drm/verisilicon/vs_drv.c >> @@ -0,0 +1,316 @@ >> +// SPDX-License-Identifier: GPL-2.0 > [...] >> +static void vs_drm_device_release_clocks(void *res) >> +{ >> + struct vs_drm_device *priv = res; >> + unsigned int i; >> + >> + reset_control_bulk_assert(priv->nrsts, priv->rst_vout); >> + >> + for (i = 0; i < priv->clk_count; ++i) { >> + if (priv->clks[i]) { >> + clk_disable_unprepare(priv->clks[i]); >> + clk_put(priv->clks[i]); >> + } >> + } > > Why not use the bulk API for clk as well? ok , will do it next version > > [...] >> +static int vs_drm_device_init_clocks(struct vs_drm_device *priv) >> +{ >> + struct drm_device *dev = &priv->base; >> + struct platform_device *pdev = to_platform_device(dev->dev); >> + struct device_node *of_node = pdev->dev.of_node; >> + struct clk *clock; >> + unsigned int i; >> + int ret; >> + >> + if (dev_get_platdata(&pdev->dev) || !of_node) >> + return 0; >> + >> + priv->nrsts = ARRAY_SIZE(priv->rst_vout); >> + for (int i = 0; i < priv->nrsts; ++i) >> + priv->rst_vout[i].id = vout_resets[i]; >> + ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts, >> + priv->rst_vout); > > I would request resets and clocks in _probe(). > > If component_bind_all() returns -EPROBE_DEFER because of a still > missing DSI panel backlight or similar, this doesn't have to be done > multiple times. I got what you mean. component_bind_all should be done multiple times to prevent the dsi panel driver from lagging load. in my drm subsystem , there are 2 pipeline +------------------------------+ | | | | +----+ | +-------------------+ | +-------+ +------+ +------+ | +----->+ dc controller 0 +--->----->+HDMICtl| ->+ PHY +-->+PANEL0+ |AXI | | +-------------------+ | +-------+ +------+ +------+ | | | | | | | | | | | | | | | | |APB | | +-------------------+ +---------+ +------+ +-------+ | +----->+ dc controller 1 +--->---->+ dsiTx +--->+DPHY +->+ PANEL1+ | | | +-------------------+ +---------+ +------+ +-------+ +----+ | | +------------------------------+ component_bind_all will bind the hdmi encoder and dsi encoder . binding the hdmi encoder will always return ok . binging the dsi encoder has a question : I used the panel-raspberrypi-touchscreen.c as panel driver , this driver is a i2c device and it use a i2c command to read reg ID if read success , it will do drm_panel_add. if I disconnect the panel ,it will not do drm_panel_add. dsiTx will fail to find panel , The consequence is that the inputbridge cannot be created , also outputbridge cannot be created. for encoder bind , it will fail to find the input bridge of dsi. Under this premise, although returning -EPROBE_DEFER allows bind to be executed multiple times, the final result is that the entire bind fails. returning -EPROBE_DEFER can solve panel driver from lagging load , but for no panel case , it will destory all pipeline (include hdmi and dsi). I did two things: late_initcall_sync(vs_drm_init); to make sure the panel drive has been probed; dsi encoder bind always return ok to make sure hdmi pipeline ok at lease. component_bind_all do once . > >> + if (ret) { >> + drm_err(dev, "Failed to get reset controls\n"); >> + return ret; >> + } >> + >> + priv->clk_count = of_clk_get_parent_count(of_node); >> + if (!priv->clk_count) >> + return 0; >> + >> + priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]), >> + GFP_KERNEL); >> + if (!priv->clks) >> + return -ENOMEM; >> + >> + for (i = 0; i < priv->clk_count; ++i) { >> + clock = of_clk_get(of_node, i); >> + if (IS_ERR(clock)) { >> + ret = PTR_ERR(clock); >> + if (ret == -EPROBE_DEFER) >> + goto err; >> + drm_err(dev, "clock %u not found: %d\n", i, ret); >> + continue; >> + } >> + ret = clk_prepare_enable(clock); >> + if (ret) { >> + drm_err(dev, "failed to enable clock %u: %d\n", >> + i, ret); >> + clk_put(clock); >> + continue; >> + } >> + priv->clks[i] = clock; >> + } >> + >> + ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout); >> + if (ret) >> + return ret; > > This should goto err, otherwise clocks are left enabled. > >> + >> + return devm_add_action_or_reset(&pdev->dev, >> + vs_drm_device_release_clocks, >> + priv); >> + >> +err: >> + while (i) { >> + --i; >> + if (priv->clks[i]) { >> + clk_disable_unprepare(priv->clks[i]); >> + clk_put(priv->clks[i]); >> + } >> + } >> + return ret; >> +} >> + >> +static int vs_drm_bind(struct device *dev) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct vs_drm_device *priv; >> + int ret; >> + struct drm_device *drm_dev; >> + >> + /* Remove existing drivers that may own the framebuffer memory. */ >> + ret = drm_aperture_remove_framebuffers(&vs_drm_driver); >> + if (ret) >> + return ret; >> + >> + priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base); >> + if (IS_ERR(priv)) >> + return PTR_ERR(priv); >> + >> + priv->pitch_alignment = 64; > > Why is this a variable instead of a constant? the dc controllers of VeriSilicon have different series, like dc8200 , dc9200... Their pitch values may vary from series to series, like 64 , 128 > >> + ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40)); >> + if (ret) >> + return ret; >> + >> + drm_dev = &priv->base; >> + platform_set_drvdata(pdev, drm_dev); >> + >> + ret = vs_drm_device_init_clocks(priv); >> + if (ret) >> + return ret; >> + >> + vs_mode_config_init(drm_dev); >> + >> + /* Now try and bind all our sub-components */ >> + ret = component_bind_all(dev, drm_dev); >> + if (ret) >> + return ret; >> + >> + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); >> + if (ret) >> + return ret; > > Missing component_unbind_all(), see below. > >> + >> + drm_mode_config_reset(drm_dev); >> + >> + drm_kms_helper_poll_init(drm_dev); >> + >> + ret = drm_dev_register(drm_dev, 0); >> + if (ret) >> + return ret; > > Missing drm_kms_helper_poll_fini(), see below. > >> + >> + drm_fbdev_generic_setup(drm_dev, 32); >> + >> + return 0; > > Here I'd expect an error path calling drm_kms_helper_poll_fini() and > component_unbind_all() as appropriate. > ok > > regards > Philipp
Hi, On Mon, Dec 11, 2023 at 05:00:04PM +0800, Keith Zhao wrote: > >> +static int vs_drm_device_init_clocks(struct vs_drm_device *priv) > >> +{ > >> + struct drm_device *dev = &priv->base; > >> + struct platform_device *pdev = to_platform_device(dev->dev); > >> + struct device_node *of_node = pdev->dev.of_node; > >> + struct clk *clock; > >> + unsigned int i; > >> + int ret; > >> + > >> + if (dev_get_platdata(&pdev->dev) || !of_node) > >> + return 0; > >> + > >> + priv->nrsts = ARRAY_SIZE(priv->rst_vout); > >> + for (int i = 0; i < priv->nrsts; ++i) > >> + priv->rst_vout[i].id = vout_resets[i]; > >> + ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts, > >> + priv->rst_vout); > > > > I would request resets and clocks in _probe(). > > > > > If component_bind_all() returns -EPROBE_DEFER because of a still > > missing DSI panel backlight or similar, this doesn't have to be done > > multiple times. > I got what you mean. component_bind_all should be done multiple times > to prevent the dsi panel driver from lagging load. No. component_bind_all only needs to be called once. > in my drm subsystem , there are 2 pipeline > > +------------------------------+ > | | > | | > +----+ | +-------------------+ | +-------+ +------+ +------+ > | +----->+ dc controller 0 +--->----->+HDMICtl| ->+ PHY +-->+PANEL0+ > |AXI | | +-------------------+ | +-------+ +------+ +------+ > | | | | > | | | | > | | | | > | | | | > |APB | | +-------------------+ +---------+ +------+ +-------+ > | +----->+ dc controller 1 +--->---->+ dsiTx +--->+DPHY +->+ PANEL1+ > | | | +-------------------+ +---------+ +------+ +-------+ > +----+ | | > +------------------------------+ > > > component_bind_all will bind the hdmi encoder and dsi encoder . > binding the hdmi encoder will always return ok . > > binging the dsi encoder has a question : > I used the panel-raspberrypi-touchscreen.c as panel driver , > this driver is a i2c device and it use a i2c command to read reg ID > if read success , it will do drm_panel_add. > > if I disconnect the panel ,it will not do drm_panel_add. > dsiTx will fail to find panel , The consequence is that the inputbridge cannot be created , > also outputbridge cannot be created. > for encoder bind , it will fail to find the input bridge of dsi. > Under this premise, although returning -EPROBE_DEFER allows bind to be executed multiple times, > the final result is that the entire bind fails. > > returning -EPROBE_DEFER can solve panel driver from lagging load , > but for no panel case , it will destory all pipeline (include hdmi and dsi). Yes, that's expected. > I did two things: > late_initcall_sync(vs_drm_init); to make sure the panel drive has been probed; > dsi encoder bind always return ok to make sure hdmi pipeline ok at lease. > component_bind_all do once . You should have a look at https://www.kernel.org/doc/html/latest/gpu/drm-kms-helpers.html#special-care-with-mipi-dsi-bridges Maxime
diff --git a/MAINTAINERS b/MAINTAINERS index 7caaadb83f3f..8dc9ebfe4605 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6887,6 +6887,7 @@ L: dri-devel@lists.freedesktop.org S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/starfive/ +F: drivers/gpu/drm/verisilicon/ DRM DRIVER FOR TI DLPC3433 MIPI DSI TO DMD BRIDGE M: Jagan Teki <jagan@amarulasolutions.com> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3eee8636f847..e8d53c2e7c86 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -394,6 +394,8 @@ source "drivers/gpu/drm/solomon/Kconfig" source "drivers/gpu/drm/sprd/Kconfig" +source "drivers/gpu/drm/verisilicon/Kconfig" + config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8e1bde059170..29e04acded06 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ obj-$(CONFIG_DRM_LOONGSON) += loongson/ +obj-$(CONFIG_DRM_VERISILICON) += verisilicon/ diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig new file mode 100644 index 000000000000..e10fa97635aa --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +config DRM_VERISILICON + tristate "DRM Support for VeriSilicon" + depends on DRM + select DRM_KMS_HELPER + select DRM_GEM_DMA_HELPER + select CMA + select DMA_CMA + help + Choose this option if you have a VeriSilicon soc chipset. + This driver provides VeriSilicon kernel mode + setting and buffer management. It does not + provide 2D or 3D acceleration. diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile new file mode 100644 index 000000000000..d785a1dfaa7f --- /dev/null +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +vs_drm-objs := vs_drv.o \ + vs_modeset.o + +obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c new file mode 100644 index 000000000000..4fb1f29ef84b --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_clk.h> +#include <linux/pm_runtime.h> + +#include <drm/drm_aperture.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_file.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_module.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + +#include "vs_drv.h" +#include "vs_modeset.h" + +#define DRV_NAME "verisilicon" +#define DRV_DESC "Verisilicon DRM driver" +#define DRV_DATE "20230516" +#define DRV_MAJOR 1 +#define DRV_MINOR 0 + +static int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct vs_drm_device *priv = to_vs_drm_private(dev); + unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + + args->pitch = ALIGN(pitch, priv->pitch_alignment); + return drm_gem_dma_dumb_create_internal(file, dev, args); +} + +DEFINE_DRM_GEM_FOPS(vs_drm_fops); + +static struct drm_driver vs_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, + + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create), + + .fops = &vs_drm_fops, + .name = DRV_NAME, + .desc = DRV_DESC, + .date = DRV_DATE, + .major = DRV_MAJOR, + .minor = DRV_MINOR, +}; + +static void vs_drm_device_release_clocks(void *res) +{ + struct vs_drm_device *priv = res; + unsigned int i; + + reset_control_bulk_assert(priv->nrsts, priv->rst_vout); + + for (i = 0; i < priv->clk_count; ++i) { + if (priv->clks[i]) { + clk_disable_unprepare(priv->clks[i]); + clk_put(priv->clks[i]); + } + } +} + +static const char * const vout_resets[] = { + "axi", + "ahb", + "core", +}; + +static int vs_drm_device_init_clocks(struct vs_drm_device *priv) +{ + struct drm_device *dev = &priv->base; + struct platform_device *pdev = to_platform_device(dev->dev); + struct device_node *of_node = pdev->dev.of_node; + struct clk *clock; + unsigned int i; + int ret; + + if (dev_get_platdata(&pdev->dev) || !of_node) + return 0; + + priv->nrsts = ARRAY_SIZE(priv->rst_vout); + for (int i = 0; i < priv->nrsts; ++i) + priv->rst_vout[i].id = vout_resets[i]; + ret = devm_reset_control_bulk_get_shared(dev->dev, priv->nrsts, + priv->rst_vout); + if (ret) { + drm_err(dev, "Failed to get reset controls\n"); + return ret; + } + + priv->clk_count = of_clk_get_parent_count(of_node); + if (!priv->clk_count) + return 0; + + priv->clks = drmm_kzalloc(dev, priv->clk_count * sizeof(priv->clks[0]), + GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + for (i = 0; i < priv->clk_count; ++i) { + clock = of_clk_get(of_node, i); + if (IS_ERR(clock)) { + ret = PTR_ERR(clock); + if (ret == -EPROBE_DEFER) + goto err; + drm_err(dev, "clock %u not found: %d\n", i, ret); + continue; + } + ret = clk_prepare_enable(clock); + if (ret) { + drm_err(dev, "failed to enable clock %u: %d\n", + i, ret); + clk_put(clock); + continue; + } + priv->clks[i] = clock; + } + + ret = reset_control_bulk_deassert(priv->nrsts, priv->rst_vout); + if (ret) + return ret; + + return devm_add_action_or_reset(&pdev->dev, + vs_drm_device_release_clocks, + priv); + +err: + while (i) { + --i; + if (priv->clks[i]) { + clk_disable_unprepare(priv->clks[i]); + clk_put(priv->clks[i]); + } + } + return ret; +} + +static int vs_drm_bind(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vs_drm_device *priv; + int ret; + struct drm_device *drm_dev; + + /* Remove existing drivers that may own the framebuffer memory. */ + ret = drm_aperture_remove_framebuffers(&vs_drm_driver); + if (ret) + return ret; + + priv = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_device, base); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + priv->pitch_alignment = 64; + + ret = dma_set_coherent_mask(priv->base.dev, DMA_BIT_MASK(40)); + if (ret) + return ret; + + drm_dev = &priv->base; + platform_set_drvdata(pdev, drm_dev); + + ret = vs_drm_device_init_clocks(priv); + if (ret) + return ret; + + vs_mode_config_init(drm_dev); + + /* Now try and bind all our sub-components */ + ret = component_bind_all(dev, drm_dev); + if (ret) + return ret; + + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); + if (ret) + return ret; + + drm_mode_config_reset(drm_dev); + + drm_kms_helper_poll_init(drm_dev); + + ret = drm_dev_register(drm_dev, 0); + if (ret) + return ret; + + drm_fbdev_generic_setup(drm_dev, 32); + + return 0; +} + +static void vs_drm_unbind(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + drm_dev_unregister(drm_dev); + drm_kms_helper_poll_fini(drm_dev); + component_unbind_all(drm_dev->dev, drm_dev); +} + +static const struct component_master_ops vs_drm_ops = { + .bind = vs_drm_bind, + .unbind = vs_drm_unbind, +}; + +static struct platform_driver *drm_sub_drivers[] = { +}; + +static struct component_match *vs_drm_match_add(struct device *dev) +{ + struct component_match *match = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(drm_sub_drivers); ++i) { + struct platform_driver *drv = drm_sub_drivers[i]; + struct device *p = NULL, *d; + + while ((d = platform_find_device_by_driver(p, &drv->driver))) { + put_device(p); + + drm_of_component_match_add(dev, &match, component_compare_of, + d->of_node); + p = d; + } + put_device(p); + } + + return match ? match : ERR_PTR(-ENODEV); +} + +static int vs_drm_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct component_match *match; + + match = vs_drm_match_add(dev); + if (IS_ERR(match)) + return PTR_ERR(match); + + return component_master_add_with_match(dev, &vs_drm_ops, match); +} + +static int vs_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &vs_drm_ops); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vs_drm_suspend(struct device *dev) +{ + return drm_mode_config_helper_suspend(dev_get_drvdata(dev)); +} + +static int vs_drm_resume(struct device *dev) +{ + drm_mode_config_helper_resume(dev_get_drvdata(dev)); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume); + +static const struct of_device_id vs_drm_dt_ids[] = { + { .compatible = "starfive,display-subsystem", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, vs_drm_dt_ids); + +static struct platform_driver vs_drm_platform_driver = { + .probe = vs_drm_platform_probe, + .remove = vs_drm_platform_remove, + + .driver = { + .name = DRV_NAME, + .of_match_table = vs_drm_dt_ids, + .pm = &vs_drm_pm_ops, + }, +}; + +static int __init vs_drm_init(void) +{ + int ret; + + ret = platform_register_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers)); + if (ret) + return ret; + + ret = drm_platform_driver_register(&vs_drm_platform_driver); + if (ret) + platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers)); + + return ret; +} + +static void __exit vs_drm_fini(void) +{ + platform_driver_unregister(&vs_drm_platform_driver); + platform_unregister_drivers(drm_sub_drivers, ARRAY_SIZE(drm_sub_drivers)); +} + +late_initcall_sync(vs_drm_init); +module_exit(vs_drm_fini); + +MODULE_DESCRIPTION("VeriSilicon DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h new file mode 100644 index 000000000000..ea2189772980 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_drv.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_DRV_H__ +#define __VS_DRV_H__ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_gem.h> +#include <drm/drm_managed.h> +#include <linux/clk.h> +#include <linux/reset.h> + +enum rst_vout { + RST_VOUT_AXI = 0, + RST_VOUT_AHB, + RST_VOUT_CORE, + RST_VOUT_NUM +}; + +/*@pitch_alignment: buffer pitch alignment required by sub-devices.*/ +struct vs_drm_device { + struct drm_device base; + unsigned int pitch_alignment; + /* clocks */ + unsigned int clk_count; + struct clk **clks; + + struct reset_control_bulk_data rst_vout[RST_VOUT_NUM]; + int nrsts; +}; + +static inline struct vs_drm_device * +to_vs_drm_private(const struct drm_device *dev) +{ + return container_of(dev, struct vs_drm_device, base); +} + +#endif /* __VS_DRV_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.c b/drivers/gpu/drm/verisilicon/vs_modeset.c new file mode 100644 index 000000000000..eaf406c1b7c7 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include <linux/module.h> + +#include <drm/drm_damage_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> + +#include "vs_modeset.h" + +static const struct drm_mode_config_funcs vs_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static struct drm_mode_config_helper_funcs vs_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +void vs_mode_config_init(struct drm_device *dev) +{ + int ret; + + ret = drmm_mode_config_init(dev); + if (ret) + return; + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + + dev->mode_config.funcs = &vs_mode_config_funcs; + dev->mode_config.helper_private = &vs_mode_config_helpers; +} diff --git a/drivers/gpu/drm/verisilicon/vs_modeset.h b/drivers/gpu/drm/verisilicon/vs_modeset.h new file mode 100644 index 000000000000..bd04f81d2ad2 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_modeset.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_MODESET_H__ +#define __VS_MODESET_H__ + +void vs_mode_config_init(struct drm_device *dev); +#endif /* __VS_FB_H__ */
Implement drm device registration interface Signed-off-by: Keith Zhao <keith.zhao@starfivetech.com> --- MAINTAINERS | 1 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/verisilicon/Kconfig | 13 + drivers/gpu/drm/verisilicon/Makefile | 6 + drivers/gpu/drm/verisilicon/vs_drv.c | 316 +++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_drv.h | 42 +++ drivers/gpu/drm/verisilicon/vs_modeset.c | 39 +++ drivers/gpu/drm/verisilicon/vs_modeset.h | 10 + 9 files changed, 430 insertions(+) create mode 100644 drivers/gpu/drm/verisilicon/Kconfig create mode 100644 drivers/gpu/drm/verisilicon/Makefile create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.c create mode 100644 drivers/gpu/drm/verisilicon/vs_drv.h create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.c create mode 100644 drivers/gpu/drm/verisilicon/vs_modeset.h