11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2bbbe775eSNeil Armstrong /* 3bbbe775eSNeil Armstrong * Copyright (C) 2016 BayLibre, SAS 4bbbe775eSNeil Armstrong * Author: Neil Armstrong <narmstrong@baylibre.com> 5bbbe775eSNeil Armstrong * Copyright (C) 2014 Endless Mobile 6bbbe775eSNeil Armstrong * 7bbbe775eSNeil Armstrong * Written by: 8bbbe775eSNeil Armstrong * Jasper St. Pierre <jstpierre@mecheye.net> 9bbbe775eSNeil Armstrong */ 10bbbe775eSNeil Armstrong 11a41e82e6SNeil Armstrong #include <linux/component.h> 1266620f48SSam Ravnborg #include <linux/module.h> 13bbbe775eSNeil Armstrong #include <linux/of_graph.h> 1466620f48SSam Ravnborg #include <linux/platform_device.h> 1566620f48SSam Ravnborg #include <linux/soc/amlogic/meson-canvas.h> 16bbbe775eSNeil Armstrong 17bbbe775eSNeil Armstrong #include <drm/drm_atomic_helper.h> 1866620f48SSam Ravnborg #include <drm/drm_drv.h> 19fcd70cd3SDaniel Vetter #include <drm/drm_fb_helper.h> 20bbbe775eSNeil Armstrong #include <drm/drm_gem_cma_helper.h> 2124ef8157SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 2266620f48SSam Ravnborg #include <drm/drm_irq.h> 2366620f48SSam Ravnborg #include <drm/drm_modeset_helper_vtables.h> 24fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 2566620f48SSam Ravnborg #include <drm/drm_vblank.h> 26bbbe775eSNeil Armstrong 27bbbe775eSNeil Armstrong #include "meson_crtc.h" 2866620f48SSam Ravnborg #include "meson_drv.h" 2966620f48SSam Ravnborg #include "meson_overlay.h" 3066620f48SSam Ravnborg #include "meson_plane.h" 31bbbe775eSNeil Armstrong #include "meson_registers.h" 3266620f48SSam Ravnborg #include "meson_venc_cvbs.h" 3366620f48SSam Ravnborg #include "meson_viu.h" 3466620f48SSam Ravnborg #include "meson_vpp.h" 35bbbe775eSNeil Armstrong 36bbbe775eSNeil Armstrong #define DRIVER_NAME "meson" 37bbbe775eSNeil Armstrong #define DRIVER_DESC "Amlogic Meson DRM driver" 38bbbe775eSNeil Armstrong 392021d5b7SNeil Armstrong /** 402021d5b7SNeil Armstrong * DOC: Video Processing Unit 41bbbe775eSNeil Armstrong * 42bbbe775eSNeil Armstrong * VPU Handles the Global Video Processing, it includes management of the 43bbbe775eSNeil Armstrong * clocks gates, blocks reset lines and power domains. 44bbbe775eSNeil Armstrong * 45bbbe775eSNeil Armstrong * What is missing : 462021d5b7SNeil Armstrong * 47bbbe775eSNeil Armstrong * - Full reset of entire video processing HW blocks 48bbbe775eSNeil Armstrong * - Scaling and setup of the VPU clock 49bbbe775eSNeil Armstrong * - Bus clock gates 50bbbe775eSNeil Armstrong * - Powering up video processing HW blocks 51bbbe775eSNeil Armstrong * - Powering Up HDMI controller and PHY 52bbbe775eSNeil Armstrong */ 53bbbe775eSNeil Armstrong 54bbbe775eSNeil Armstrong static const struct drm_mode_config_funcs meson_mode_config_funcs = { 55bbbe775eSNeil Armstrong .atomic_check = drm_atomic_helper_check, 56bbbe775eSNeil Armstrong .atomic_commit = drm_atomic_helper_commit, 5724ef8157SNoralf Trønnes .fb_create = drm_gem_fb_create, 58bbbe775eSNeil Armstrong }; 59bbbe775eSNeil Armstrong 60ce0210c1SNeil Armstrong static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = { 61ce0210c1SNeil Armstrong .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 62ce0210c1SNeil Armstrong }; 63ce0210c1SNeil Armstrong 64bbbe775eSNeil Armstrong static irqreturn_t meson_irq(int irq, void *arg) 65bbbe775eSNeil Armstrong { 66bbbe775eSNeil Armstrong struct drm_device *dev = arg; 67bbbe775eSNeil Armstrong struct meson_drm *priv = dev->dev_private; 68bbbe775eSNeil Armstrong 69bbbe775eSNeil Armstrong (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); 70bbbe775eSNeil Armstrong 71bbbe775eSNeil Armstrong meson_crtc_irq(priv); 72bbbe775eSNeil Armstrong 73bbbe775eSNeil Armstrong return IRQ_HANDLED; 74bbbe775eSNeil Armstrong } 75bbbe775eSNeil Armstrong 76852ce728SNeil Armstrong static int meson_dumb_create(struct drm_file *file, struct drm_device *dev, 77852ce728SNeil Armstrong struct drm_mode_create_dumb *args) 78852ce728SNeil Armstrong { 79852ce728SNeil Armstrong /* 80852ce728SNeil Armstrong * We need 64bytes aligned stride, and PAGE aligned size 81852ce728SNeil Armstrong */ 82852ce728SNeil Armstrong args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64); 83852ce728SNeil Armstrong args->size = PAGE_ALIGN(args->pitch * args->height); 84852ce728SNeil Armstrong 85852ce728SNeil Armstrong return drm_gem_cma_dumb_create_internal(file, dev, args); 86852ce728SNeil Armstrong } 87852ce728SNeil Armstrong 88d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops); 89bbbe775eSNeil Armstrong 90bbbe775eSNeil Armstrong static struct drm_driver meson_driver = { 910424fdafSDaniel Vetter .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 92bbbe775eSNeil Armstrong 93bbbe775eSNeil Armstrong /* IRQ */ 94bbbe775eSNeil Armstrong .irq_handler = meson_irq, 95bbbe775eSNeil Armstrong 96bbbe775eSNeil Armstrong /* PRIME Ops */ 97bbbe775eSNeil Armstrong .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 98bbbe775eSNeil Armstrong .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 99bbbe775eSNeil Armstrong .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 100bbbe775eSNeil Armstrong .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 101bbbe775eSNeil Armstrong .gem_prime_vmap = drm_gem_cma_prime_vmap, 102bbbe775eSNeil Armstrong .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 103bbbe775eSNeil Armstrong .gem_prime_mmap = drm_gem_cma_prime_mmap, 104bbbe775eSNeil Armstrong 105bbbe775eSNeil Armstrong /* GEM Ops */ 106852ce728SNeil Armstrong .dumb_create = meson_dumb_create, 107bbbe775eSNeil Armstrong .gem_free_object_unlocked = drm_gem_cma_free_object, 108bbbe775eSNeil Armstrong .gem_vm_ops = &drm_gem_cma_vm_ops, 109bbbe775eSNeil Armstrong 110bbbe775eSNeil Armstrong /* Misc */ 111bbbe775eSNeil Armstrong .fops = &fops, 112bbbe775eSNeil Armstrong .name = DRIVER_NAME, 113bbbe775eSNeil Armstrong .desc = DRIVER_DESC, 114bbbe775eSNeil Armstrong .date = "20161109", 115bbbe775eSNeil Armstrong .major = 1, 116bbbe775eSNeil Armstrong .minor = 0, 117bbbe775eSNeil Armstrong }; 118bbbe775eSNeil Armstrong 119bbbe775eSNeil Armstrong static bool meson_vpu_has_available_connectors(struct device *dev) 120bbbe775eSNeil Armstrong { 121bbbe775eSNeil Armstrong struct device_node *ep, *remote; 122bbbe775eSNeil Armstrong 123bbbe775eSNeil Armstrong /* Parses each endpoint and check if remote exists */ 124bbbe775eSNeil Armstrong for_each_endpoint_of_node(dev->of_node, ep) { 125bbbe775eSNeil Armstrong /* If the endpoint node exists, consider it enabled */ 126bbbe775eSNeil Armstrong remote = of_graph_get_remote_port(ep); 127bbbe775eSNeil Armstrong if (remote) 128bbbe775eSNeil Armstrong return true; 129bbbe775eSNeil Armstrong } 130bbbe775eSNeil Armstrong 131bbbe775eSNeil Armstrong return false; 132bbbe775eSNeil Armstrong } 133bbbe775eSNeil Armstrong 134bbbe775eSNeil Armstrong static struct regmap_config meson_regmap_config = { 135bbbe775eSNeil Armstrong .reg_bits = 32, 136bbbe775eSNeil Armstrong .val_bits = 32, 137bbbe775eSNeil Armstrong .reg_stride = 4, 138bbbe775eSNeil Armstrong .max_register = 0x1000, 139bbbe775eSNeil Armstrong }; 140bbbe775eSNeil Armstrong 14109762525SNeil Armstrong static void meson_vpu_init(struct meson_drm *priv) 14209762525SNeil Armstrong { 143bfb86819SJulien Masson u32 value; 144bfb86819SJulien Masson 145bfb86819SJulien Masson /* 146bfb86819SJulien Masson * Slave dc0 and dc5 connected to master port 1. 147bfb86819SJulien Masson * By default other slaves are connected to master port 0. 148bfb86819SJulien Masson */ 149bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | 150bfb86819SJulien Masson VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); 151bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); 152bfb86819SJulien Masson 153bfb86819SJulien Masson /* Slave dc0 connected to master port 1 */ 154bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); 155bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); 156bfb86819SJulien Masson 157bfb86819SJulien Masson /* Slave dc4 and dc7 connected to master port 1 */ 158bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | 159bfb86819SJulien Masson VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); 160bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); 161bfb86819SJulien Masson 162bfb86819SJulien Masson /* Slave dc1 connected to master port 1 */ 163bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); 164bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); 16509762525SNeil Armstrong } 16609762525SNeil Armstrong 167e3de0aa6SMaxime Jourdan static void meson_remove_framebuffers(void) 168e3de0aa6SMaxime Jourdan { 169e3de0aa6SMaxime Jourdan struct apertures_struct *ap; 170e3de0aa6SMaxime Jourdan 171e3de0aa6SMaxime Jourdan ap = alloc_apertures(1); 172e3de0aa6SMaxime Jourdan if (!ap) 173e3de0aa6SMaxime Jourdan return; 174e3de0aa6SMaxime Jourdan 175e3de0aa6SMaxime Jourdan /* The framebuffer can be located anywhere in RAM */ 176e3de0aa6SMaxime Jourdan ap->ranges[0].base = 0; 177e3de0aa6SMaxime Jourdan ap->ranges[0].size = ~0; 178e3de0aa6SMaxime Jourdan 179e3de0aa6SMaxime Jourdan drm_fb_helper_remove_conflicting_framebuffers(ap, "meson-drm-fb", 180e3de0aa6SMaxime Jourdan false); 181e3de0aa6SMaxime Jourdan kfree(ap); 182e3de0aa6SMaxime Jourdan } 183e3de0aa6SMaxime Jourdan 1848604889fSNeil Armstrong static int meson_drv_bind_master(struct device *dev, bool has_components) 185bbbe775eSNeil Armstrong { 186a41e82e6SNeil Armstrong struct platform_device *pdev = to_platform_device(dev); 187bbbe775eSNeil Armstrong struct meson_drm *priv; 188bbbe775eSNeil Armstrong struct drm_device *drm; 189bbbe775eSNeil Armstrong struct resource *res; 190bbbe775eSNeil Armstrong void __iomem *regs; 191bbbe775eSNeil Armstrong int ret; 192bbbe775eSNeil Armstrong 193bbbe775eSNeil Armstrong /* Checks if an output connector is available */ 194bbbe775eSNeil Armstrong if (!meson_vpu_has_available_connectors(dev)) { 195bbbe775eSNeil Armstrong dev_err(dev, "No output connector available\n"); 196bbbe775eSNeil Armstrong return -ENODEV; 197bbbe775eSNeil Armstrong } 198bbbe775eSNeil Armstrong 199bbbe775eSNeil Armstrong drm = drm_dev_alloc(&meson_driver, dev); 200bbbe775eSNeil Armstrong if (IS_ERR(drm)) 201bbbe775eSNeil Armstrong return PTR_ERR(drm); 202bbbe775eSNeil Armstrong 203bbbe775eSNeil Armstrong priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 204bbbe775eSNeil Armstrong if (!priv) { 205bbbe775eSNeil Armstrong ret = -ENOMEM; 206bbbe775eSNeil Armstrong goto free_drm; 207bbbe775eSNeil Armstrong } 208bbbe775eSNeil Armstrong drm->dev_private = priv; 209bbbe775eSNeil Armstrong priv->drm = drm; 210bbbe775eSNeil Armstrong priv->dev = dev; 211bbbe775eSNeil Armstrong 212*528a25d0SJulien Masson priv->compat = (enum vpu_compatible)of_device_get_match_data(priv->dev); 213*528a25d0SJulien Masson 214bbbe775eSNeil Armstrong res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); 215bbbe775eSNeil Armstrong regs = devm_ioremap_resource(dev, res); 2162c18107bSChristophe JAILLET if (IS_ERR(regs)) { 2172c18107bSChristophe JAILLET ret = PTR_ERR(regs); 2182c18107bSChristophe JAILLET goto free_drm; 2192c18107bSChristophe JAILLET } 220bbbe775eSNeil Armstrong 221bbbe775eSNeil Armstrong priv->io_base = regs; 222bbbe775eSNeil Armstrong 223bbbe775eSNeil Armstrong res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); 22401a9e949SChristophe JAILLET if (!res) { 22501a9e949SChristophe JAILLET ret = -EINVAL; 22601a9e949SChristophe JAILLET goto free_drm; 22701a9e949SChristophe JAILLET } 228bbbe775eSNeil Armstrong /* Simply ioremap since it may be a shared register zone */ 229bbbe775eSNeil Armstrong regs = devm_ioremap(dev, res->start, resource_size(res)); 2302c18107bSChristophe JAILLET if (!regs) { 2312c18107bSChristophe JAILLET ret = -EADDRNOTAVAIL; 2322c18107bSChristophe JAILLET goto free_drm; 2332c18107bSChristophe JAILLET } 234bbbe775eSNeil Armstrong 235bbbe775eSNeil Armstrong priv->hhi = devm_regmap_init_mmio(dev, regs, 236bbbe775eSNeil Armstrong &meson_regmap_config); 237bbbe775eSNeil Armstrong if (IS_ERR(priv->hhi)) { 238bbbe775eSNeil Armstrong dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); 2392c18107bSChristophe JAILLET ret = PTR_ERR(priv->hhi); 2402c18107bSChristophe JAILLET goto free_drm; 241bbbe775eSNeil Armstrong } 242bbbe775eSNeil Armstrong 24366cae477SMaxime Jourdan priv->canvas = meson_canvas_get(dev); 2442bf6b5b0SMaxime Jourdan if (IS_ERR(priv->canvas)) { 2452bf6b5b0SMaxime Jourdan ret = PTR_ERR(priv->canvas); 2462bf6b5b0SMaxime Jourdan goto free_drm; 2472bf6b5b0SMaxime Jourdan } 2482bf6b5b0SMaxime Jourdan 24966cae477SMaxime Jourdan ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); 25066cae477SMaxime Jourdan if (ret) 25166cae477SMaxime Jourdan goto free_drm; 252f9a23481SNeil Armstrong ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); 253f9a23481SNeil Armstrong if (ret) { 254f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 255f9a23481SNeil Armstrong goto free_drm; 256f9a23481SNeil Armstrong } 257f9a23481SNeil Armstrong ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); 258f9a23481SNeil Armstrong if (ret) { 259f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 260f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 261f9a23481SNeil Armstrong goto free_drm; 262f9a23481SNeil Armstrong } 263f9a23481SNeil Armstrong ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); 264f9a23481SNeil Armstrong if (ret) { 265f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 266f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 267f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 268f9a23481SNeil Armstrong goto free_drm; 269f9a23481SNeil Armstrong } 270bbbe775eSNeil Armstrong 271bbbe775eSNeil Armstrong priv->vsync_irq = platform_get_irq(pdev, 0); 272bbbe775eSNeil Armstrong 273e770f6bfSChristophe JAILLET ret = drm_vblank_init(drm, 1); 274e770f6bfSChristophe JAILLET if (ret) 275e770f6bfSChristophe JAILLET goto free_drm; 276e770f6bfSChristophe JAILLET 277e3de0aa6SMaxime Jourdan /* Remove early framebuffers (ie. simplefb) */ 278e3de0aa6SMaxime Jourdan meson_remove_framebuffers(); 279e3de0aa6SMaxime Jourdan 280bbbe775eSNeil Armstrong drm_mode_config_init(drm); 281a41e82e6SNeil Armstrong drm->mode_config.max_width = 3840; 282a41e82e6SNeil Armstrong drm->mode_config.max_height = 2160; 283a41e82e6SNeil Armstrong drm->mode_config.funcs = &meson_mode_config_funcs; 284ce0210c1SNeil Armstrong drm->mode_config.helper_private = &meson_mode_config_helpers; 285a41e82e6SNeil Armstrong 286a41e82e6SNeil Armstrong /* Hardware Initialization */ 287a41e82e6SNeil Armstrong 28809762525SNeil Armstrong meson_vpu_init(priv); 289a41e82e6SNeil Armstrong meson_venc_init(priv); 290a41e82e6SNeil Armstrong meson_vpp_init(priv); 291a41e82e6SNeil Armstrong meson_viu_init(priv); 292bbbe775eSNeil Armstrong 293bbbe775eSNeil Armstrong /* Encoder Initialization */ 294bbbe775eSNeil Armstrong 295bbbe775eSNeil Armstrong ret = meson_venc_cvbs_create(priv); 296bbbe775eSNeil Armstrong if (ret) 297bbbe775eSNeil Armstrong goto free_drm; 298bbbe775eSNeil Armstrong 2998604889fSNeil Armstrong if (has_components) { 300a41e82e6SNeil Armstrong ret = component_bind_all(drm->dev, drm); 301a41e82e6SNeil Armstrong if (ret) { 302a41e82e6SNeil Armstrong dev_err(drm->dev, "Couldn't bind all components\n"); 303a41e82e6SNeil Armstrong goto free_drm; 304a41e82e6SNeil Armstrong } 3058604889fSNeil Armstrong } 306bbbe775eSNeil Armstrong 307bbbe775eSNeil Armstrong ret = meson_plane_create(priv); 308bbbe775eSNeil Armstrong if (ret) 309bbbe775eSNeil Armstrong goto free_drm; 310bbbe775eSNeil Armstrong 311f9a23481SNeil Armstrong ret = meson_overlay_create(priv); 312f9a23481SNeil Armstrong if (ret) 313f9a23481SNeil Armstrong goto free_drm; 314f9a23481SNeil Armstrong 315bbbe775eSNeil Armstrong ret = meson_crtc_create(priv); 316bbbe775eSNeil Armstrong if (ret) 317bbbe775eSNeil Armstrong goto free_drm; 318bbbe775eSNeil Armstrong 319bbbe775eSNeil Armstrong ret = drm_irq_install(drm, priv->vsync_irq); 320bbbe775eSNeil Armstrong if (ret) 321bbbe775eSNeil Armstrong goto free_drm; 322bbbe775eSNeil Armstrong 323bbbe775eSNeil Armstrong drm_mode_config_reset(drm); 324bbbe775eSNeil Armstrong 325bbbe775eSNeil Armstrong drm_kms_helper_poll_init(drm); 326bbbe775eSNeil Armstrong 327bbbe775eSNeil Armstrong platform_set_drvdata(pdev, priv); 328bbbe775eSNeil Armstrong 329bbbe775eSNeil Armstrong ret = drm_dev_register(drm, 0); 330bbbe775eSNeil Armstrong if (ret) 3312d8f9289SJean-Philippe Brucker goto uninstall_irq; 332bbbe775eSNeil Armstrong 333efbb9df9SNoralf Trønnes drm_fbdev_generic_setup(drm, 32); 334efbb9df9SNoralf Trønnes 335bbbe775eSNeil Armstrong return 0; 336bbbe775eSNeil Armstrong 3372d8f9289SJean-Philippe Brucker uninstall_irq: 3382d8f9289SJean-Philippe Brucker drm_irq_uninstall(drm); 339bbbe775eSNeil Armstrong free_drm: 340dcacf651SChristophe JAILLET drm_dev_put(drm); 341bbbe775eSNeil Armstrong 342bbbe775eSNeil Armstrong return ret; 343bbbe775eSNeil Armstrong } 344bbbe775eSNeil Armstrong 3458604889fSNeil Armstrong static int meson_drv_bind(struct device *dev) 3468604889fSNeil Armstrong { 3478604889fSNeil Armstrong return meson_drv_bind_master(dev, true); 3488604889fSNeil Armstrong } 3498604889fSNeil Armstrong 350a41e82e6SNeil Armstrong static void meson_drv_unbind(struct device *dev) 351bbbe775eSNeil Armstrong { 352776e7867SJean-Philippe Brucker struct meson_drm *priv = dev_get_drvdata(dev); 353776e7867SJean-Philippe Brucker struct drm_device *drm = priv->drm; 35466cae477SMaxime Jourdan 355f9a23481SNeil Armstrong if (priv->canvas) { 35666cae477SMaxime Jourdan meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 357f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 358f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 359f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); 360f9a23481SNeil Armstrong } 361bbbe775eSNeil Armstrong 362bbbe775eSNeil Armstrong drm_dev_unregister(drm); 3632d8f9289SJean-Philippe Brucker drm_irq_uninstall(drm); 364bbbe775eSNeil Armstrong drm_kms_helper_poll_fini(drm); 365bbbe775eSNeil Armstrong drm_mode_config_cleanup(drm); 366dcacf651SChristophe JAILLET drm_dev_put(drm); 367bbbe775eSNeil Armstrong 368bbbe775eSNeil Armstrong } 369bbbe775eSNeil Armstrong 370a41e82e6SNeil Armstrong static const struct component_master_ops meson_drv_master_ops = { 371a41e82e6SNeil Armstrong .bind = meson_drv_bind, 372a41e82e6SNeil Armstrong .unbind = meson_drv_unbind, 373a41e82e6SNeil Armstrong }; 374a41e82e6SNeil Armstrong 375a41e82e6SNeil Armstrong static int compare_of(struct device *dev, void *data) 376a41e82e6SNeil Armstrong { 3774bf99144SRob Herring DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n", 3784bf99144SRob Herring dev->of_node, data); 379a41e82e6SNeil Armstrong 380a41e82e6SNeil Armstrong return dev->of_node == data; 381a41e82e6SNeil Armstrong } 382a41e82e6SNeil Armstrong 383a41e82e6SNeil Armstrong /* Possible connectors nodes to ignore */ 384a41e82e6SNeil Armstrong static const struct of_device_id connectors_match[] = { 385a41e82e6SNeil Armstrong { .compatible = "composite-video-connector" }, 386a41e82e6SNeil Armstrong { .compatible = "svideo-connector" }, 387a41e82e6SNeil Armstrong { .compatible = "hdmi-connector" }, 388a41e82e6SNeil Armstrong { .compatible = "dvi-connector" }, 389a41e82e6SNeil Armstrong {} 390a41e82e6SNeil Armstrong }; 391a41e82e6SNeil Armstrong 392a41e82e6SNeil Armstrong static int meson_probe_remote(struct platform_device *pdev, 393a41e82e6SNeil Armstrong struct component_match **match, 394a41e82e6SNeil Armstrong struct device_node *parent, 395a41e82e6SNeil Armstrong struct device_node *remote) 396a41e82e6SNeil Armstrong { 397a41e82e6SNeil Armstrong struct device_node *ep, *remote_node; 398a41e82e6SNeil Armstrong int count = 1; 399a41e82e6SNeil Armstrong 400a41e82e6SNeil Armstrong /* If node is a connector, return and do not add to match table */ 401a41e82e6SNeil Armstrong if (of_match_node(connectors_match, remote)) 402a41e82e6SNeil Armstrong return 1; 403a41e82e6SNeil Armstrong 404a41e82e6SNeil Armstrong component_match_add(&pdev->dev, match, compare_of, remote); 405a41e82e6SNeil Armstrong 406a41e82e6SNeil Armstrong for_each_endpoint_of_node(remote, ep) { 407a41e82e6SNeil Armstrong remote_node = of_graph_get_remote_port_parent(ep); 408a41e82e6SNeil Armstrong if (!remote_node || 409a41e82e6SNeil Armstrong remote_node == parent || /* Ignore parent endpoint */ 410f672b93eSJulia Lawall !of_device_is_available(remote_node)) { 411f672b93eSJulia Lawall of_node_put(remote_node); 412a41e82e6SNeil Armstrong continue; 413f672b93eSJulia Lawall } 414a41e82e6SNeil Armstrong 415a41e82e6SNeil Armstrong count += meson_probe_remote(pdev, match, remote, remote_node); 416a41e82e6SNeil Armstrong 417a41e82e6SNeil Armstrong of_node_put(remote_node); 418a41e82e6SNeil Armstrong } 419a41e82e6SNeil Armstrong 420a41e82e6SNeil Armstrong return count; 421a41e82e6SNeil Armstrong } 422a41e82e6SNeil Armstrong 423a41e82e6SNeil Armstrong static int meson_drv_probe(struct platform_device *pdev) 424a41e82e6SNeil Armstrong { 425a41e82e6SNeil Armstrong struct component_match *match = NULL; 426a41e82e6SNeil Armstrong struct device_node *np = pdev->dev.of_node; 427a41e82e6SNeil Armstrong struct device_node *ep, *remote; 428a41e82e6SNeil Armstrong int count = 0; 429a41e82e6SNeil Armstrong 430a41e82e6SNeil Armstrong for_each_endpoint_of_node(np, ep) { 431a41e82e6SNeil Armstrong remote = of_graph_get_remote_port_parent(ep); 432f672b93eSJulia Lawall if (!remote || !of_device_is_available(remote)) { 433f672b93eSJulia Lawall of_node_put(remote); 434a41e82e6SNeil Armstrong continue; 435f672b93eSJulia Lawall } 436a41e82e6SNeil Armstrong 437a41e82e6SNeil Armstrong count += meson_probe_remote(pdev, &match, np, remote); 438f672b93eSJulia Lawall of_node_put(remote); 439a41e82e6SNeil Armstrong } 440a41e82e6SNeil Armstrong 4418604889fSNeil Armstrong if (count && !match) 4428604889fSNeil Armstrong return meson_drv_bind_master(&pdev->dev, false); 4438604889fSNeil Armstrong 444a41e82e6SNeil Armstrong /* If some endpoints were found, initialize the nodes */ 445a41e82e6SNeil Armstrong if (count) { 446a41e82e6SNeil Armstrong dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); 447a41e82e6SNeil Armstrong 448a41e82e6SNeil Armstrong return component_master_add_with_match(&pdev->dev, 449a41e82e6SNeil Armstrong &meson_drv_master_ops, 450a41e82e6SNeil Armstrong match); 451a41e82e6SNeil Armstrong } 452a41e82e6SNeil Armstrong 453a41e82e6SNeil Armstrong /* If no output endpoints were available, simply bail out */ 454a41e82e6SNeil Armstrong return 0; 455a41e82e6SNeil Armstrong }; 456a41e82e6SNeil Armstrong 457bbbe775eSNeil Armstrong static const struct of_device_id dt_match[] = { 458*528a25d0SJulien Masson { .compatible = "amlogic,meson-gxbb-vpu", 459*528a25d0SJulien Masson .data = (void *)VPU_COMPATIBLE_GXBB }, 460*528a25d0SJulien Masson { .compatible = "amlogic,meson-gxl-vpu", 461*528a25d0SJulien Masson .data = (void *)VPU_COMPATIBLE_GXL }, 462*528a25d0SJulien Masson { .compatible = "amlogic,meson-gxm-vpu", 463*528a25d0SJulien Masson .data = (void *)VPU_COMPATIBLE_GXM }, 464*528a25d0SJulien Masson { .compatible = "amlogic,meson-g12a-vpu", 465*528a25d0SJulien Masson .data = (void *)VPU_COMPATIBLE_G12A }, 466bbbe775eSNeil Armstrong {} 467bbbe775eSNeil Armstrong }; 468bbbe775eSNeil Armstrong MODULE_DEVICE_TABLE(of, dt_match); 469bbbe775eSNeil Armstrong 470bbbe775eSNeil Armstrong static struct platform_driver meson_drm_platform_driver = { 471bbbe775eSNeil Armstrong .probe = meson_drv_probe, 472bbbe775eSNeil Armstrong .driver = { 4738aaacbc0SNeil Armstrong .name = "meson-drm", 474bbbe775eSNeil Armstrong .of_match_table = dt_match, 475bbbe775eSNeil Armstrong }, 476bbbe775eSNeil Armstrong }; 477bbbe775eSNeil Armstrong 478bbbe775eSNeil Armstrong module_platform_driver(meson_drm_platform_driver); 479bbbe775eSNeil Armstrong 480bbbe775eSNeil Armstrong MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); 481bbbe775eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 482bbbe775eSNeil Armstrong MODULE_DESCRIPTION(DRIVER_DESC); 483bbbe775eSNeil Armstrong MODULE_LICENSE("GPL"); 484