123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
- */
- #include <linux/of_graph.h>
- #include <linux/component.h>
- #include <linux/iommu.h>
- #include <linux/version.h>
- #include <drm/drm_of.h>
- #include <drm/drm_crtc.h>
- #include <drm/drm_crtc_helper.h>
- #include <drm/drm_probe_helper.h>
- #include <drm/drm_fb_helper.h>
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
- #include <drm/drm_drv.h>
- #include <drm/drm_file.h>
- #include <drm/drm_prime.h>
- #include <drm/drm_vblank.h>
- #include <drm/drm_ioctl.h>
- #include <drm/drm_fourcc.h>
- #include <drm/drm_debugfs.h>
- #endif
- #include "vs_drv.h"
- #include "vs_fb.h"
- #include "vs_gem.h"
- #include "vs_plane.h"
- #include "vs_crtc.h"
- #include "vs_simple_enc.h"
- #include "vs_dc.h"
- #include "vs_virtual.h"
- #include "dw_mipi_dsi.h"
- #include "dw_hdmi_light.h"
- #define DRV_NAME "vs-drm"
- #define DRV_DESC "VeriSilicon DRM driver"
- #define DRV_DATE "20191101"
- #define DRV_MAJOR 1
- #define DRV_MINOR 0
- static bool has_iommu = true;
- static struct platform_driver vs_drm_platform_driver;
- static const struct file_operations fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
- .compat_ioctl = drm_compat_ioctl,
- .poll = drm_poll,
- .read = drm_read,
- .mmap = vs_gem_mmap,
- };
- #ifdef CONFIG_DEBUG_FS
- static int vs_debugfs_planes_show(struct seq_file *s, void *data)
- {
- struct drm_info_node *node = (struct drm_info_node *)s->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_plane *plane;
- list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
- struct drm_plane_state *state = plane->state;
- struct vs_plane_state *plane_state = to_vs_plane_state(state);
- seq_printf(s, "plane[%u]: %s\n", plane->base.id, plane->name);
- seq_printf(s, "\tcrtc = %s\n", state->crtc ?
- state->crtc->name : "(null)");
- seq_printf(s, "\tcrtc id = %u\n", state->crtc ?
- state->crtc->base.id : 0);
- seq_printf(s, "\tcrtc-pos = " DRM_RECT_FMT "\n",
- DRM_RECT_ARG(&plane_state->status.dest));
- seq_printf(s, "\tsrc-pos = " DRM_RECT_FP_FMT "\n",
- DRM_RECT_FP_ARG(&plane_state->status.src));
- seq_printf(s, "\tformat = %s\n", state->fb ?
- plane_state->status.format_name.str : "(null)");
- seq_printf(s, "\trotation = 0x%x\n", state->rotation);
- seq_printf(s, "\ttiling = %u\n",
- plane_state->status.tile_mode);
- seq_puts(s, "\n");
- }
- return 0;
- }
- static struct drm_info_list vs_debugfs_list[] = {
- { "planes", vs_debugfs_planes_show, 0, NULL },
- };
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
- static void vs_debugfs_init(struct drm_minor *minor)
- {
- drm_debugfs_create_files(vs_debugfs_list,
- ARRAY_SIZE(vs_debugfs_list),
- minor->debugfs_root, minor);
- }
- #else
- static int vs_debugfs_init(struct drm_minor *minor)
- {
- struct drm_device *dev = minor->dev;
- int ret;
- ret = drm_debugfs_create_files(vs_debugfs_list,
- ARRAY_SIZE(vs_debugfs_list),
- minor->debugfs_root, minor);
- if (ret)
- dev_err(dev->dev, "could not install vs_debugfs_list.\n");
- return ret;
- }
- #endif
- #endif
- static struct drm_driver vs_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
- .lastclose = drm_fb_helper_lastclose,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_import = vs_gem_prime_import,
- .gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
- .gem_prime_mmap = vs_gem_prime_mmap,
- .dumb_create = vs_gem_dumb_create,
- #ifdef CONFIG_DEBUG_FS
- .debugfs_init = vs_debugfs_init,
- #endif
- .fops = &fops,
- .name = DRV_NAME,
- .desc = DRV_DESC,
- .date = DRV_DATE,
- .major = DRV_MAJOR,
- .minor = DRV_MINOR,
- };
- int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
- struct device *dev)
- {
- struct vs_drm_private *priv = drm_dev->dev_private;
- int ret;
- if (!has_iommu)
- return 0;
- if (!priv->domain) {
- priv->domain = iommu_get_domain_for_dev(dev);
- if (IS_ERR(priv->domain))
- return PTR_ERR(priv->domain);
- priv->dma_dev = dev;
- }
- ret = iommu_attach_device(priv->domain, dev);
- if (ret) {
- DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
- return ret;
- }
- return 0;
- }
- void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
- struct device *dev)
- {
- struct vs_drm_private *priv = drm_dev->dev_private;
- if (!has_iommu)
- return;
- iommu_detach_device(priv->domain, dev);
- if (priv->dma_dev == dev)
- priv->dma_dev = drm_dev->dev;
- }
- void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
- unsigned int alignment)
- {
- struct vs_drm_private *priv = drm_dev->dev_private;
- if (alignment > priv->pitch_alignment)
- priv->pitch_alignment = alignment;
- }
- static int vs_drm_bind(struct device *dev)
- {
- struct drm_device *drm_dev;
- struct vs_drm_private *priv;
- int ret;
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
- #ifdef CONFIG_VERISILICON_MMU
- static u64 dma_mask = DMA_BIT_MASK(40);
- #else
- static u64 dma_mask = DMA_BIT_MASK(32);
- #endif
- #else
- static u64 dma_mask = DMA_40BIT_MASK;
- #endif
- drm_dev = drm_dev_alloc(&vs_drm_driver, dev);
- if (IS_ERR(drm_dev))
- return PTR_ERR(drm_dev);
- dev_set_drvdata(dev, drm_dev);
- priv = devm_kzalloc(drm_dev->dev, sizeof(struct vs_drm_private),
- GFP_KERNEL);
- if (!priv) {
- ret = -ENOMEM;
- goto err_put_dev;
- }
- priv->pitch_alignment = 64;
- priv->dma_dev = drm_dev->dev;
- priv->dma_dev->coherent_dma_mask = dma_mask;
- drm_dev->dev_private = priv;
- drm_mode_config_init(drm_dev);
- /* Now try and bind all our sub-components */
- ret = component_bind_all(dev, drm_dev);
- if (ret)
- goto err_mode;
- vs_mode_config_init(drm_dev);
- ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
- if (ret)
- goto err_bind;
- drm_mode_config_reset(drm_dev);
- drm_dev->irq_enabled = true;
- drm_kms_helper_poll_init(drm_dev);
- ret = drm_dev_register(drm_dev, 0);
- if (ret)
- goto err_helper;
- drm_fbdev_generic_setup(drm_dev, 32);
- return 0;
- err_helper:
- drm_kms_helper_poll_fini(drm_dev);
- err_bind:
- component_unbind_all(drm_dev->dev, drm_dev);
- err_mode:
- drm_mode_config_cleanup(drm_dev);
- if (priv->domain)
- iommu_domain_free(priv->domain);
- err_put_dev:
- drm_dev->dev_private = NULL;
- dev_set_drvdata(dev, NULL);
- drm_dev_put(drm_dev);
- return ret;
- }
- static void vs_drm_unbind(struct device *dev)
- {
- struct drm_device *drm_dev = dev_get_drvdata(dev);
- struct vs_drm_private *priv = drm_dev->dev_private;
- drm_dev_unregister(drm_dev);
- drm_kms_helper_poll_fini(drm_dev);
- component_unbind_all(drm_dev->dev, drm_dev);
- drm_mode_config_cleanup(drm_dev);
- if (priv->domain) {
- iommu_domain_free(priv->domain);
- priv->domain = NULL;
- }
- drm_dev->dev_private = NULL;
- dev_set_drvdata(dev, NULL);
- drm_dev_put(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[] = {
- /* put display control driver at start */
- &dc_platform_driver,
- /* connector */
- /* bridge */
- #ifdef CONFIG_VERISILICON_DW_MIPI_DSI
- &dw_mipi_dsi_driver,
- #endif
- #ifdef CONFIG_VERISILICON_DW_HDMI_LIGHT
- &dw_hdmi_light_platform_driver,
- #endif
- /* encoder */
- &simple_encoder_driver,
- #ifdef CONFIG_VERISILICON_VIRTUAL_DISPLAY
- &virtual_display_platform_driver,
- #endif
- };
- #define NUM_DRM_DRIVERS \
- (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
- static int compare_dev(struct device *dev, void *data)
- {
- return dev == (struct device *)data;
- }
- static struct component_match *vs_drm_match_add(struct device *dev)
- {
- struct component_match *match = NULL;
- int i;
- for (i = 0; i < NUM_DRM_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);
- component_match_add(dev, &match, compare_dev, d);
- p = d;
- }
- put_device(p);
- }
- return match ?: ERR_PTR(-ENODEV);
- }
- static int vs_drm_platform_of_probe(struct device *dev)
- {
- struct device_node *np = dev->of_node;
- struct device_node *port;
- bool found = false;
- int i;
- if (!np)
- return -ENODEV;
- for (i = 0;; i++) {
- struct device_node *iommu;
- port = of_parse_phandle(np, "ports", i);
- if (!port)
- break;
- if (!of_device_is_available(port->parent)) {
- of_node_put(port);
- continue;
- }
- iommu = of_parse_phandle(port->parent, "iommus", 0);
- /*
- * if there is a crtc not support iommu, force set all
- * crtc use non-iommu buffer.
- */
- if (!iommu || !of_device_is_available(iommu->parent))
- has_iommu = false;
- found = true;
- of_node_put(iommu);
- of_node_put(port);
- }
- if (i == 0) {
- DRM_DEV_ERROR(dev, "missing 'ports' property\n");
- return -ENODEV;
- }
- if (!found) {
- DRM_DEV_ERROR(dev, "No available DC found.\n");
- return -ENODEV;
- }
- return 0;
- }
- static int vs_drm_platform_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct component_match *match;
- int ret;
- ret = vs_drm_platform_of_probe(dev);
- if (ret)
- return ret;
- 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)
- {
- struct drm_device *drm = dev_get_drvdata(dev);
- return drm_mode_config_helper_suspend(drm);
- }
- static int vs_drm_resume(struct device *dev)
- {
- struct drm_device *drm = dev_get_drvdata(dev);
- return drm_mode_config_helper_resume(drm);
- }
- #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 = "verisilicon,display-subsystem", },
- { /* sentinel */ },
- };
- 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, NUM_DRM_DRIVERS);
- if (ret)
- return ret;
- ret = platform_driver_register(&vs_drm_platform_driver);
- if (ret)
- platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
- return ret;
- }
- static void __exit vs_drm_fini(void)
- {
- platform_driver_unregister(&vs_drm_platform_driver);
- platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
- }
- module_init(vs_drm_init);
- module_exit(vs_drm_fini);
- MODULE_DESCRIPTION("VeriSilicon DRM Driver");
- MODULE_LICENSE("GPL v2");
|