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> 148976eeeeSNeil Armstrong #include <linux/sys_soc.h> 1566620f48SSam Ravnborg #include <linux/platform_device.h> 1666620f48SSam Ravnborg #include <linux/soc/amlogic/meson-canvas.h> 17bbbe775eSNeil Armstrong 18bbbe775eSNeil Armstrong #include <drm/drm_atomic_helper.h> 1966620f48SSam Ravnborg #include <drm/drm_drv.h> 20fcd70cd3SDaniel Vetter #include <drm/drm_fb_helper.h> 21bbbe775eSNeil Armstrong #include <drm/drm_gem_cma_helper.h> 2224ef8157SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 2366620f48SSam Ravnborg #include <drm/drm_irq.h> 2466620f48SSam Ravnborg #include <drm/drm_modeset_helper_vtables.h> 25fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 2666620f48SSam Ravnborg #include <drm/drm_vblank.h> 27bbbe775eSNeil Armstrong 28bbbe775eSNeil Armstrong #include "meson_crtc.h" 2966620f48SSam Ravnborg #include "meson_drv.h" 3066620f48SSam Ravnborg #include "meson_overlay.h" 3166620f48SSam Ravnborg #include "meson_plane.h" 32d1b5e41eSNeil Armstrong #include "meson_osd_afbcd.h" 33bbbe775eSNeil Armstrong #include "meson_registers.h" 3466620f48SSam Ravnborg #include "meson_venc_cvbs.h" 3566620f48SSam Ravnborg #include "meson_viu.h" 3666620f48SSam Ravnborg #include "meson_vpp.h" 37d1b5e41eSNeil Armstrong #include "meson_rdma.h" 38bbbe775eSNeil Armstrong 39bbbe775eSNeil Armstrong #define DRIVER_NAME "meson" 40bbbe775eSNeil Armstrong #define DRIVER_DESC "Amlogic Meson DRM driver" 41bbbe775eSNeil Armstrong 422021d5b7SNeil Armstrong /** 432021d5b7SNeil Armstrong * DOC: Video Processing Unit 44bbbe775eSNeil Armstrong * 45bbbe775eSNeil Armstrong * VPU Handles the Global Video Processing, it includes management of the 46bbbe775eSNeil Armstrong * clocks gates, blocks reset lines and power domains. 47bbbe775eSNeil Armstrong * 48bbbe775eSNeil Armstrong * What is missing : 492021d5b7SNeil Armstrong * 50bbbe775eSNeil Armstrong * - Full reset of entire video processing HW blocks 51bbbe775eSNeil Armstrong * - Scaling and setup of the VPU clock 52bbbe775eSNeil Armstrong * - Bus clock gates 53bbbe775eSNeil Armstrong * - Powering up video processing HW blocks 54bbbe775eSNeil Armstrong * - Powering Up HDMI controller and PHY 55bbbe775eSNeil Armstrong */ 56bbbe775eSNeil Armstrong 57bbbe775eSNeil Armstrong static const struct drm_mode_config_funcs meson_mode_config_funcs = { 58bbbe775eSNeil Armstrong .atomic_check = drm_atomic_helper_check, 59bbbe775eSNeil Armstrong .atomic_commit = drm_atomic_helper_commit, 6024ef8157SNoralf Trønnes .fb_create = drm_gem_fb_create, 61bbbe775eSNeil Armstrong }; 62bbbe775eSNeil Armstrong 63ce0210c1SNeil Armstrong static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = { 64ce0210c1SNeil Armstrong .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 65ce0210c1SNeil Armstrong }; 66ce0210c1SNeil Armstrong 67bbbe775eSNeil Armstrong static irqreturn_t meson_irq(int irq, void *arg) 68bbbe775eSNeil Armstrong { 69bbbe775eSNeil Armstrong struct drm_device *dev = arg; 70bbbe775eSNeil Armstrong struct meson_drm *priv = dev->dev_private; 71bbbe775eSNeil Armstrong 72bbbe775eSNeil Armstrong (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); 73bbbe775eSNeil Armstrong 74bbbe775eSNeil Armstrong meson_crtc_irq(priv); 75bbbe775eSNeil Armstrong 76bbbe775eSNeil Armstrong return IRQ_HANDLED; 77bbbe775eSNeil Armstrong } 78bbbe775eSNeil Armstrong 79852ce728SNeil Armstrong static int meson_dumb_create(struct drm_file *file, struct drm_device *dev, 80852ce728SNeil Armstrong struct drm_mode_create_dumb *args) 81852ce728SNeil Armstrong { 82852ce728SNeil Armstrong /* 83852ce728SNeil Armstrong * We need 64bytes aligned stride, and PAGE aligned size 84852ce728SNeil Armstrong */ 85852ce728SNeil Armstrong args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64); 86852ce728SNeil Armstrong args->size = PAGE_ALIGN(args->pitch * args->height); 87852ce728SNeil Armstrong 88852ce728SNeil Armstrong return drm_gem_cma_dumb_create_internal(file, dev, args); 89852ce728SNeil Armstrong } 90852ce728SNeil Armstrong 91d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops); 92bbbe775eSNeil Armstrong 93bbbe775eSNeil Armstrong static struct drm_driver meson_driver = { 940424fdafSDaniel Vetter .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 95bbbe775eSNeil Armstrong 96bbbe775eSNeil Armstrong /* IRQ */ 97bbbe775eSNeil Armstrong .irq_handler = meson_irq, 98bbbe775eSNeil Armstrong 99b54d830cSThomas Zimmermann /* CMA Ops */ 100b54d830cSThomas Zimmermann DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create), 101bbbe775eSNeil Armstrong 102bbbe775eSNeil Armstrong /* Misc */ 103bbbe775eSNeil Armstrong .fops = &fops, 104bbbe775eSNeil Armstrong .name = DRIVER_NAME, 105bbbe775eSNeil Armstrong .desc = DRIVER_DESC, 106bbbe775eSNeil Armstrong .date = "20161109", 107bbbe775eSNeil Armstrong .major = 1, 108bbbe775eSNeil Armstrong .minor = 0, 109bbbe775eSNeil Armstrong }; 110bbbe775eSNeil Armstrong 111bbbe775eSNeil Armstrong static bool meson_vpu_has_available_connectors(struct device *dev) 112bbbe775eSNeil Armstrong { 113bbbe775eSNeil Armstrong struct device_node *ep, *remote; 114bbbe775eSNeil Armstrong 115bbbe775eSNeil Armstrong /* Parses each endpoint and check if remote exists */ 116bbbe775eSNeil Armstrong for_each_endpoint_of_node(dev->of_node, ep) { 117bbbe775eSNeil Armstrong /* If the endpoint node exists, consider it enabled */ 118bbbe775eSNeil Armstrong remote = of_graph_get_remote_port(ep); 119bbbe775eSNeil Armstrong if (remote) 120bbbe775eSNeil Armstrong return true; 121bbbe775eSNeil Armstrong } 122bbbe775eSNeil Armstrong 123bbbe775eSNeil Armstrong return false; 124bbbe775eSNeil Armstrong } 125bbbe775eSNeil Armstrong 126bbbe775eSNeil Armstrong static struct regmap_config meson_regmap_config = { 127bbbe775eSNeil Armstrong .reg_bits = 32, 128bbbe775eSNeil Armstrong .val_bits = 32, 129bbbe775eSNeil Armstrong .reg_stride = 4, 130bbbe775eSNeil Armstrong .max_register = 0x1000, 131bbbe775eSNeil Armstrong }; 132bbbe775eSNeil Armstrong 13309762525SNeil Armstrong static void meson_vpu_init(struct meson_drm *priv) 13409762525SNeil Armstrong { 135bfb86819SJulien Masson u32 value; 136bfb86819SJulien Masson 137bfb86819SJulien Masson /* 138bfb86819SJulien Masson * Slave dc0 and dc5 connected to master port 1. 139bfb86819SJulien Masson * By default other slaves are connected to master port 0. 140bfb86819SJulien Masson */ 141bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | 142bfb86819SJulien Masson VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); 143bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); 144bfb86819SJulien Masson 145bfb86819SJulien Masson /* Slave dc0 connected to master port 1 */ 146bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); 147bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); 148bfb86819SJulien Masson 149bfb86819SJulien Masson /* Slave dc4 and dc7 connected to master port 1 */ 150bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | 151bfb86819SJulien Masson VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); 152bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); 153bfb86819SJulien Masson 154bfb86819SJulien Masson /* Slave dc1 connected to master port 1 */ 155bfb86819SJulien Masson value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); 156bfb86819SJulien Masson writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); 15709762525SNeil Armstrong } 15809762525SNeil Armstrong 159e3de0aa6SMaxime Jourdan static void meson_remove_framebuffers(void) 160e3de0aa6SMaxime Jourdan { 161e3de0aa6SMaxime Jourdan struct apertures_struct *ap; 162e3de0aa6SMaxime Jourdan 163e3de0aa6SMaxime Jourdan ap = alloc_apertures(1); 164e3de0aa6SMaxime Jourdan if (!ap) 165e3de0aa6SMaxime Jourdan return; 166e3de0aa6SMaxime Jourdan 167e3de0aa6SMaxime Jourdan /* The framebuffer can be located anywhere in RAM */ 168e3de0aa6SMaxime Jourdan ap->ranges[0].base = 0; 169e3de0aa6SMaxime Jourdan ap->ranges[0].size = ~0; 170e3de0aa6SMaxime Jourdan 171e3de0aa6SMaxime Jourdan drm_fb_helper_remove_conflicting_framebuffers(ap, "meson-drm-fb", 172e3de0aa6SMaxime Jourdan false); 173e3de0aa6SMaxime Jourdan kfree(ap); 174e3de0aa6SMaxime Jourdan } 175e3de0aa6SMaxime Jourdan 1768976eeeeSNeil Armstrong struct meson_drm_soc_attr { 1778976eeeeSNeil Armstrong struct meson_drm_soc_limits limits; 1788976eeeeSNeil Armstrong const struct soc_device_attribute *attrs; 1798976eeeeSNeil Armstrong }; 1808976eeeeSNeil Armstrong 1818976eeeeSNeil Armstrong static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = { 1828976eeeeSNeil Armstrong /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */ 1838976eeeeSNeil Armstrong { 1848976eeeeSNeil Armstrong .limits = { 1858976eeeeSNeil Armstrong .max_hdmi_phy_freq = 1650000, 1868976eeeeSNeil Armstrong }, 1878976eeeeSNeil Armstrong .attrs = (const struct soc_device_attribute []) { 1888976eeeeSNeil Armstrong { .soc_id = "GXL (S805*)", }, 1898976eeeeSNeil Armstrong { /* sentinel */ }, 1908976eeeeSNeil Armstrong } 1918976eeeeSNeil Armstrong }, 1928976eeeeSNeil Armstrong }; 1938976eeeeSNeil Armstrong 1948604889fSNeil Armstrong static int meson_drv_bind_master(struct device *dev, bool has_components) 195bbbe775eSNeil Armstrong { 196a41e82e6SNeil Armstrong struct platform_device *pdev = to_platform_device(dev); 197d1b5e41eSNeil Armstrong const struct meson_drm_match_data *match; 198bbbe775eSNeil Armstrong struct meson_drm *priv; 199bbbe775eSNeil Armstrong struct drm_device *drm; 200bbbe775eSNeil Armstrong struct resource *res; 201bbbe775eSNeil Armstrong void __iomem *regs; 2028976eeeeSNeil Armstrong int ret, i; 203bbbe775eSNeil Armstrong 204bbbe775eSNeil Armstrong /* Checks if an output connector is available */ 205bbbe775eSNeil Armstrong if (!meson_vpu_has_available_connectors(dev)) { 206bbbe775eSNeil Armstrong dev_err(dev, "No output connector available\n"); 207bbbe775eSNeil Armstrong return -ENODEV; 208bbbe775eSNeil Armstrong } 209bbbe775eSNeil Armstrong 210d1b5e41eSNeil Armstrong match = of_device_get_match_data(dev); 211d1b5e41eSNeil Armstrong if (!match) 212d1b5e41eSNeil Armstrong return -ENODEV; 213d1b5e41eSNeil Armstrong 214bbbe775eSNeil Armstrong drm = drm_dev_alloc(&meson_driver, dev); 215bbbe775eSNeil Armstrong if (IS_ERR(drm)) 216bbbe775eSNeil Armstrong return PTR_ERR(drm); 217bbbe775eSNeil Armstrong 218bbbe775eSNeil Armstrong priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 219bbbe775eSNeil Armstrong if (!priv) { 220bbbe775eSNeil Armstrong ret = -ENOMEM; 221bbbe775eSNeil Armstrong goto free_drm; 222bbbe775eSNeil Armstrong } 223bbbe775eSNeil Armstrong drm->dev_private = priv; 224bbbe775eSNeil Armstrong priv->drm = drm; 225bbbe775eSNeil Armstrong priv->dev = dev; 226d1b5e41eSNeil Armstrong priv->compat = match->compat; 227d1b5e41eSNeil Armstrong priv->afbcd.ops = match->afbcd_ops; 228528a25d0SJulien Masson 229bbbe775eSNeil Armstrong res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); 230bbbe775eSNeil Armstrong regs = devm_ioremap_resource(dev, res); 2312c18107bSChristophe JAILLET if (IS_ERR(regs)) { 2322c18107bSChristophe JAILLET ret = PTR_ERR(regs); 2332c18107bSChristophe JAILLET goto free_drm; 2342c18107bSChristophe JAILLET } 235bbbe775eSNeil Armstrong 236bbbe775eSNeil Armstrong priv->io_base = regs; 237bbbe775eSNeil Armstrong 238bbbe775eSNeil Armstrong res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); 23901a9e949SChristophe JAILLET if (!res) { 24001a9e949SChristophe JAILLET ret = -EINVAL; 24101a9e949SChristophe JAILLET goto free_drm; 24201a9e949SChristophe JAILLET } 243bbbe775eSNeil Armstrong /* Simply ioremap since it may be a shared register zone */ 244bbbe775eSNeil Armstrong regs = devm_ioremap(dev, res->start, resource_size(res)); 2452c18107bSChristophe JAILLET if (!regs) { 2462c18107bSChristophe JAILLET ret = -EADDRNOTAVAIL; 2472c18107bSChristophe JAILLET goto free_drm; 2482c18107bSChristophe JAILLET } 249bbbe775eSNeil Armstrong 250bbbe775eSNeil Armstrong priv->hhi = devm_regmap_init_mmio(dev, regs, 251bbbe775eSNeil Armstrong &meson_regmap_config); 252bbbe775eSNeil Armstrong if (IS_ERR(priv->hhi)) { 253bbbe775eSNeil Armstrong dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); 2542c18107bSChristophe JAILLET ret = PTR_ERR(priv->hhi); 2552c18107bSChristophe JAILLET goto free_drm; 256bbbe775eSNeil Armstrong } 257bbbe775eSNeil Armstrong 25866cae477SMaxime Jourdan priv->canvas = meson_canvas_get(dev); 2592bf6b5b0SMaxime Jourdan if (IS_ERR(priv->canvas)) { 2602bf6b5b0SMaxime Jourdan ret = PTR_ERR(priv->canvas); 2612bf6b5b0SMaxime Jourdan goto free_drm; 2622bf6b5b0SMaxime Jourdan } 2632bf6b5b0SMaxime Jourdan 26466cae477SMaxime Jourdan ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); 26566cae477SMaxime Jourdan if (ret) 26666cae477SMaxime Jourdan goto free_drm; 267f9a23481SNeil Armstrong ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); 268f9a23481SNeil Armstrong if (ret) { 269f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 270f9a23481SNeil Armstrong goto free_drm; 271f9a23481SNeil Armstrong } 272f9a23481SNeil Armstrong ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); 273f9a23481SNeil Armstrong if (ret) { 274f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 275f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 276f9a23481SNeil Armstrong goto free_drm; 277f9a23481SNeil Armstrong } 278f9a23481SNeil Armstrong ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); 279f9a23481SNeil Armstrong if (ret) { 280f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 281f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 282f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 283f9a23481SNeil Armstrong goto free_drm; 284f9a23481SNeil Armstrong } 285bbbe775eSNeil Armstrong 286bbbe775eSNeil Armstrong priv->vsync_irq = platform_get_irq(pdev, 0); 287bbbe775eSNeil Armstrong 288e770f6bfSChristophe JAILLET ret = drm_vblank_init(drm, 1); 289e770f6bfSChristophe JAILLET if (ret) 290e770f6bfSChristophe JAILLET goto free_drm; 291e770f6bfSChristophe JAILLET 2928976eeeeSNeil Armstrong /* Assign limits per soc revision/package */ 2938976eeeeSNeil Armstrong for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) { 2948976eeeeSNeil Armstrong if (soc_device_match(meson_drm_soc_attrs[i].attrs)) { 2958976eeeeSNeil Armstrong priv->limits = &meson_drm_soc_attrs[i].limits; 2968976eeeeSNeil Armstrong break; 2978976eeeeSNeil Armstrong } 2988976eeeeSNeil Armstrong } 2998976eeeeSNeil Armstrong 300e3de0aa6SMaxime Jourdan /* Remove early framebuffers (ie. simplefb) */ 301e3de0aa6SMaxime Jourdan meson_remove_framebuffers(); 302e3de0aa6SMaxime Jourdan 303bd9ff7b5SDaniel Vetter ret = drmm_mode_config_init(drm); 304bd9ff7b5SDaniel Vetter if (ret) 305bd9ff7b5SDaniel Vetter goto free_drm; 306a41e82e6SNeil Armstrong drm->mode_config.max_width = 3840; 307a41e82e6SNeil Armstrong drm->mode_config.max_height = 2160; 308a41e82e6SNeil Armstrong drm->mode_config.funcs = &meson_mode_config_funcs; 309ce0210c1SNeil Armstrong drm->mode_config.helper_private = &meson_mode_config_helpers; 310a41e82e6SNeil Armstrong 311a41e82e6SNeil Armstrong /* Hardware Initialization */ 312a41e82e6SNeil Armstrong 31309762525SNeil Armstrong meson_vpu_init(priv); 314a41e82e6SNeil Armstrong meson_venc_init(priv); 315a41e82e6SNeil Armstrong meson_vpp_init(priv); 316a41e82e6SNeil Armstrong meson_viu_init(priv); 317d1b5e41eSNeil Armstrong if (priv->afbcd.ops) { 318d1b5e41eSNeil Armstrong ret = priv->afbcd.ops->init(priv); 319d1b5e41eSNeil Armstrong if (ret) 320d1b5e41eSNeil Armstrong return ret; 321d1b5e41eSNeil Armstrong } 322bbbe775eSNeil Armstrong 323bbbe775eSNeil Armstrong /* Encoder Initialization */ 324bbbe775eSNeil Armstrong 325bbbe775eSNeil Armstrong ret = meson_venc_cvbs_create(priv); 326bbbe775eSNeil Armstrong if (ret) 327bbbe775eSNeil Armstrong goto free_drm; 328bbbe775eSNeil Armstrong 3298604889fSNeil Armstrong if (has_components) { 330a41e82e6SNeil Armstrong ret = component_bind_all(drm->dev, drm); 331a41e82e6SNeil Armstrong if (ret) { 332a41e82e6SNeil Armstrong dev_err(drm->dev, "Couldn't bind all components\n"); 333a41e82e6SNeil Armstrong goto free_drm; 334a41e82e6SNeil Armstrong } 3358604889fSNeil Armstrong } 336bbbe775eSNeil Armstrong 337bbbe775eSNeil Armstrong ret = meson_plane_create(priv); 338bbbe775eSNeil Armstrong if (ret) 339bbbe775eSNeil Armstrong goto free_drm; 340bbbe775eSNeil Armstrong 341f9a23481SNeil Armstrong ret = meson_overlay_create(priv); 342f9a23481SNeil Armstrong if (ret) 343f9a23481SNeil Armstrong goto free_drm; 344f9a23481SNeil Armstrong 345bbbe775eSNeil Armstrong ret = meson_crtc_create(priv); 346bbbe775eSNeil Armstrong if (ret) 347bbbe775eSNeil Armstrong goto free_drm; 348bbbe775eSNeil Armstrong 349bbbe775eSNeil Armstrong ret = drm_irq_install(drm, priv->vsync_irq); 350bbbe775eSNeil Armstrong if (ret) 351bbbe775eSNeil Armstrong goto free_drm; 352bbbe775eSNeil Armstrong 353bbbe775eSNeil Armstrong drm_mode_config_reset(drm); 354bbbe775eSNeil Armstrong 355bbbe775eSNeil Armstrong drm_kms_helper_poll_init(drm); 356bbbe775eSNeil Armstrong 357bbbe775eSNeil Armstrong platform_set_drvdata(pdev, priv); 358bbbe775eSNeil Armstrong 359bbbe775eSNeil Armstrong ret = drm_dev_register(drm, 0); 360bbbe775eSNeil Armstrong if (ret) 3612d8f9289SJean-Philippe Brucker goto uninstall_irq; 362bbbe775eSNeil Armstrong 363efbb9df9SNoralf Trønnes drm_fbdev_generic_setup(drm, 32); 364efbb9df9SNoralf Trønnes 365bbbe775eSNeil Armstrong return 0; 366bbbe775eSNeil Armstrong 3672d8f9289SJean-Philippe Brucker uninstall_irq: 3682d8f9289SJean-Philippe Brucker drm_irq_uninstall(drm); 369bbbe775eSNeil Armstrong free_drm: 370dcacf651SChristophe JAILLET drm_dev_put(drm); 371bbbe775eSNeil Armstrong 372bbbe775eSNeil Armstrong return ret; 373bbbe775eSNeil Armstrong } 374bbbe775eSNeil Armstrong 3758604889fSNeil Armstrong static int meson_drv_bind(struct device *dev) 3768604889fSNeil Armstrong { 3778604889fSNeil Armstrong return meson_drv_bind_master(dev, true); 3788604889fSNeil Armstrong } 3798604889fSNeil Armstrong 380a41e82e6SNeil Armstrong static void meson_drv_unbind(struct device *dev) 381bbbe775eSNeil Armstrong { 382776e7867SJean-Philippe Brucker struct meson_drm *priv = dev_get_drvdata(dev); 383776e7867SJean-Philippe Brucker struct drm_device *drm = priv->drm; 38466cae477SMaxime Jourdan 385f9a23481SNeil Armstrong if (priv->canvas) { 38666cae477SMaxime Jourdan meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 387f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 388f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 389f9a23481SNeil Armstrong meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); 390f9a23481SNeil Armstrong } 391bbbe775eSNeil Armstrong 392d1b5e41eSNeil Armstrong if (priv->afbcd.ops) { 393d1b5e41eSNeil Armstrong priv->afbcd.ops->reset(priv); 394d1b5e41eSNeil Armstrong meson_rdma_free(priv); 395d1b5e41eSNeil Armstrong } 396d1b5e41eSNeil Armstrong 397bbbe775eSNeil Armstrong drm_dev_unregister(drm); 3982d8f9289SJean-Philippe Brucker drm_irq_uninstall(drm); 399bbbe775eSNeil Armstrong drm_kms_helper_poll_fini(drm); 400dcacf651SChristophe JAILLET drm_dev_put(drm); 401bbbe775eSNeil Armstrong } 402bbbe775eSNeil Armstrong 403a41e82e6SNeil Armstrong static const struct component_master_ops meson_drv_master_ops = { 404a41e82e6SNeil Armstrong .bind = meson_drv_bind, 405a41e82e6SNeil Armstrong .unbind = meson_drv_unbind, 406a41e82e6SNeil Armstrong }; 407a41e82e6SNeil Armstrong 408cf3d4e53SNeil Armstrong static int __maybe_unused meson_drv_pm_suspend(struct device *dev) 409cf3d4e53SNeil Armstrong { 410cf3d4e53SNeil Armstrong struct meson_drm *priv = dev_get_drvdata(dev); 411cf3d4e53SNeil Armstrong 412cf3d4e53SNeil Armstrong if (!priv) 413cf3d4e53SNeil Armstrong return 0; 414cf3d4e53SNeil Armstrong 415cf3d4e53SNeil Armstrong return drm_mode_config_helper_suspend(priv->drm); 416cf3d4e53SNeil Armstrong } 417cf3d4e53SNeil Armstrong 418cf3d4e53SNeil Armstrong static int __maybe_unused meson_drv_pm_resume(struct device *dev) 419cf3d4e53SNeil Armstrong { 420cf3d4e53SNeil Armstrong struct meson_drm *priv = dev_get_drvdata(dev); 421cf3d4e53SNeil Armstrong 422cf3d4e53SNeil Armstrong if (!priv) 423cf3d4e53SNeil Armstrong return 0; 424cf3d4e53SNeil Armstrong 425cf3d4e53SNeil Armstrong meson_vpu_init(priv); 426cf3d4e53SNeil Armstrong meson_venc_init(priv); 427cf3d4e53SNeil Armstrong meson_vpp_init(priv); 428cf3d4e53SNeil Armstrong meson_viu_init(priv); 429d1b5e41eSNeil Armstrong if (priv->afbcd.ops) 430d1b5e41eSNeil Armstrong priv->afbcd.ops->init(priv); 431cf3d4e53SNeil Armstrong 432cf3d4e53SNeil Armstrong drm_mode_config_helper_resume(priv->drm); 433cf3d4e53SNeil Armstrong 434cf3d4e53SNeil Armstrong return 0; 435cf3d4e53SNeil Armstrong } 436cf3d4e53SNeil Armstrong 437a41e82e6SNeil Armstrong static int compare_of(struct device *dev, void *data) 438a41e82e6SNeil Armstrong { 4394bf99144SRob Herring DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n", 4404bf99144SRob Herring dev->of_node, data); 441a41e82e6SNeil Armstrong 442a41e82e6SNeil Armstrong return dev->of_node == data; 443a41e82e6SNeil Armstrong } 444a41e82e6SNeil Armstrong 445a41e82e6SNeil Armstrong /* Possible connectors nodes to ignore */ 446a41e82e6SNeil Armstrong static const struct of_device_id connectors_match[] = { 447a41e82e6SNeil Armstrong { .compatible = "composite-video-connector" }, 448a41e82e6SNeil Armstrong { .compatible = "svideo-connector" }, 449a41e82e6SNeil Armstrong { .compatible = "hdmi-connector" }, 450a41e82e6SNeil Armstrong { .compatible = "dvi-connector" }, 451a41e82e6SNeil Armstrong {} 452a41e82e6SNeil Armstrong }; 453a41e82e6SNeil Armstrong 454a41e82e6SNeil Armstrong static int meson_probe_remote(struct platform_device *pdev, 455a41e82e6SNeil Armstrong struct component_match **match, 456a41e82e6SNeil Armstrong struct device_node *parent, 457a41e82e6SNeil Armstrong struct device_node *remote) 458a41e82e6SNeil Armstrong { 459a41e82e6SNeil Armstrong struct device_node *ep, *remote_node; 460a41e82e6SNeil Armstrong int count = 1; 461a41e82e6SNeil Armstrong 462a41e82e6SNeil Armstrong /* If node is a connector, return and do not add to match table */ 463a41e82e6SNeil Armstrong if (of_match_node(connectors_match, remote)) 464a41e82e6SNeil Armstrong return 1; 465a41e82e6SNeil Armstrong 466a41e82e6SNeil Armstrong component_match_add(&pdev->dev, match, compare_of, remote); 467a41e82e6SNeil Armstrong 468a41e82e6SNeil Armstrong for_each_endpoint_of_node(remote, ep) { 469a41e82e6SNeil Armstrong remote_node = of_graph_get_remote_port_parent(ep); 470a41e82e6SNeil Armstrong if (!remote_node || 471a41e82e6SNeil Armstrong remote_node == parent || /* Ignore parent endpoint */ 472f672b93eSJulia Lawall !of_device_is_available(remote_node)) { 473f672b93eSJulia Lawall of_node_put(remote_node); 474a41e82e6SNeil Armstrong continue; 475f672b93eSJulia Lawall } 476a41e82e6SNeil Armstrong 477a41e82e6SNeil Armstrong count += meson_probe_remote(pdev, match, remote, remote_node); 478a41e82e6SNeil Armstrong 479a41e82e6SNeil Armstrong of_node_put(remote_node); 480a41e82e6SNeil Armstrong } 481a41e82e6SNeil Armstrong 482a41e82e6SNeil Armstrong return count; 483a41e82e6SNeil Armstrong } 484a41e82e6SNeil Armstrong 485a41e82e6SNeil Armstrong static int meson_drv_probe(struct platform_device *pdev) 486a41e82e6SNeil Armstrong { 487a41e82e6SNeil Armstrong struct component_match *match = NULL; 488a41e82e6SNeil Armstrong struct device_node *np = pdev->dev.of_node; 489a41e82e6SNeil Armstrong struct device_node *ep, *remote; 490a41e82e6SNeil Armstrong int count = 0; 491a41e82e6SNeil Armstrong 492a41e82e6SNeil Armstrong for_each_endpoint_of_node(np, ep) { 493a41e82e6SNeil Armstrong remote = of_graph_get_remote_port_parent(ep); 494f672b93eSJulia Lawall if (!remote || !of_device_is_available(remote)) { 495f672b93eSJulia Lawall of_node_put(remote); 496a41e82e6SNeil Armstrong continue; 497f672b93eSJulia Lawall } 498a41e82e6SNeil Armstrong 499a41e82e6SNeil Armstrong count += meson_probe_remote(pdev, &match, np, remote); 500f672b93eSJulia Lawall of_node_put(remote); 501a41e82e6SNeil Armstrong } 502a41e82e6SNeil Armstrong 5038604889fSNeil Armstrong if (count && !match) 5048604889fSNeil Armstrong return meson_drv_bind_master(&pdev->dev, false); 5058604889fSNeil Armstrong 506a41e82e6SNeil Armstrong /* If some endpoints were found, initialize the nodes */ 507a41e82e6SNeil Armstrong if (count) { 508a41e82e6SNeil Armstrong dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); 509a41e82e6SNeil Armstrong 510a41e82e6SNeil Armstrong return component_master_add_with_match(&pdev->dev, 511a41e82e6SNeil Armstrong &meson_drv_master_ops, 512a41e82e6SNeil Armstrong match); 513a41e82e6SNeil Armstrong } 514a41e82e6SNeil Armstrong 515a41e82e6SNeil Armstrong /* If no output endpoints were available, simply bail out */ 516a41e82e6SNeil Armstrong return 0; 517a41e82e6SNeil Armstrong }; 518a41e82e6SNeil Armstrong 519d1b5e41eSNeil Armstrong static struct meson_drm_match_data meson_drm_gxbb_data = { 520d1b5e41eSNeil Armstrong .compat = VPU_COMPATIBLE_GXBB, 521d1b5e41eSNeil Armstrong }; 522d1b5e41eSNeil Armstrong 523d1b5e41eSNeil Armstrong static struct meson_drm_match_data meson_drm_gxl_data = { 524d1b5e41eSNeil Armstrong .compat = VPU_COMPATIBLE_GXL, 525d1b5e41eSNeil Armstrong }; 526d1b5e41eSNeil Armstrong 527d1b5e41eSNeil Armstrong static struct meson_drm_match_data meson_drm_gxm_data = { 528d1b5e41eSNeil Armstrong .compat = VPU_COMPATIBLE_GXM, 529d1b5e41eSNeil Armstrong .afbcd_ops = &meson_afbcd_gxm_ops, 530d1b5e41eSNeil Armstrong }; 531d1b5e41eSNeil Armstrong 532d1b5e41eSNeil Armstrong static struct meson_drm_match_data meson_drm_g12a_data = { 533d1b5e41eSNeil Armstrong .compat = VPU_COMPATIBLE_G12A, 534d1b5e41eSNeil Armstrong .afbcd_ops = &meson_afbcd_g12a_ops, 535d1b5e41eSNeil Armstrong }; 536d1b5e41eSNeil Armstrong 537bbbe775eSNeil Armstrong static const struct of_device_id dt_match[] = { 538528a25d0SJulien Masson { .compatible = "amlogic,meson-gxbb-vpu", 539d1b5e41eSNeil Armstrong .data = (void *)&meson_drm_gxbb_data }, 540528a25d0SJulien Masson { .compatible = "amlogic,meson-gxl-vpu", 541d1b5e41eSNeil Armstrong .data = (void *)&meson_drm_gxl_data }, 542528a25d0SJulien Masson { .compatible = "amlogic,meson-gxm-vpu", 543d1b5e41eSNeil Armstrong .data = (void *)&meson_drm_gxm_data }, 544528a25d0SJulien Masson { .compatible = "amlogic,meson-g12a-vpu", 545d1b5e41eSNeil Armstrong .data = (void *)&meson_drm_g12a_data }, 546bbbe775eSNeil Armstrong {} 547bbbe775eSNeil Armstrong }; 548bbbe775eSNeil Armstrong MODULE_DEVICE_TABLE(of, dt_match); 549bbbe775eSNeil Armstrong 550cf3d4e53SNeil Armstrong static const struct dev_pm_ops meson_drv_pm_ops = { 551cf3d4e53SNeil Armstrong SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume) 552cf3d4e53SNeil Armstrong }; 553cf3d4e53SNeil Armstrong 554bbbe775eSNeil Armstrong static struct platform_driver meson_drm_platform_driver = { 555bbbe775eSNeil Armstrong .probe = meson_drv_probe, 556bbbe775eSNeil Armstrong .driver = { 5578aaacbc0SNeil Armstrong .name = "meson-drm", 558bbbe775eSNeil Armstrong .of_match_table = dt_match, 559cf3d4e53SNeil Armstrong .pm = &meson_drv_pm_ops, 560bbbe775eSNeil Armstrong }, 561bbbe775eSNeil Armstrong }; 562bbbe775eSNeil Armstrong 563bbbe775eSNeil Armstrong module_platform_driver(meson_drm_platform_driver); 564bbbe775eSNeil Armstrong 565bbbe775eSNeil Armstrong MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); 566bbbe775eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 567bbbe775eSNeil Armstrong MODULE_DESCRIPTION(DRIVER_DESC); 568bbbe775eSNeil Armstrong MODULE_LICENSE("GPL"); 569