162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 BayLibre, SAS 462306a36Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com> 562306a36Sopenharmony_ci * Copyright (C) 2014 Endless Mobile 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Written by: 862306a36Sopenharmony_ci * Jasper St. Pierre <jstpierre@mecheye.net> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/component.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of_graph.h> 1462306a36Sopenharmony_ci#include <linux/sys_soc.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/soc/amlogic/meson-canvas.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <drm/drm_aperture.h> 1962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2062306a36Sopenharmony_ci#include <drm/drm_drv.h> 2162306a36Sopenharmony_ci#include <drm/drm_fbdev_dma.h> 2262306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 2462306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 2562306a36Sopenharmony_ci#include <drm/drm_module.h> 2662306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2762306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "meson_crtc.h" 3062306a36Sopenharmony_ci#include "meson_drv.h" 3162306a36Sopenharmony_ci#include "meson_overlay.h" 3262306a36Sopenharmony_ci#include "meson_plane.h" 3362306a36Sopenharmony_ci#include "meson_osd_afbcd.h" 3462306a36Sopenharmony_ci#include "meson_registers.h" 3562306a36Sopenharmony_ci#include "meson_encoder_cvbs.h" 3662306a36Sopenharmony_ci#include "meson_encoder_hdmi.h" 3762306a36Sopenharmony_ci#include "meson_encoder_dsi.h" 3862306a36Sopenharmony_ci#include "meson_viu.h" 3962306a36Sopenharmony_ci#include "meson_vpp.h" 4062306a36Sopenharmony_ci#include "meson_rdma.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define DRIVER_NAME "meson" 4362306a36Sopenharmony_ci#define DRIVER_DESC "Amlogic Meson DRM driver" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/** 4662306a36Sopenharmony_ci * DOC: Video Processing Unit 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * VPU Handles the Global Video Processing, it includes management of the 4962306a36Sopenharmony_ci * clocks gates, blocks reset lines and power domains. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * What is missing : 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * - Full reset of entire video processing HW blocks 5462306a36Sopenharmony_ci * - Scaling and setup of the VPU clock 5562306a36Sopenharmony_ci * - Bus clock gates 5662306a36Sopenharmony_ci * - Powering up video processing HW blocks 5762306a36Sopenharmony_ci * - Powering Up HDMI controller and PHY 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct drm_mode_config_funcs meson_mode_config_funcs = { 6162306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 6262306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 6362306a36Sopenharmony_ci .fb_create = drm_gem_fb_create, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct drm_mode_config_helper_funcs meson_mode_config_helpers = { 6762306a36Sopenharmony_ci .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic irqreturn_t meson_irq(int irq, void *arg) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct drm_device *dev = arg; 7362306a36Sopenharmony_ci struct meson_drm *priv = dev->dev_private; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci meson_crtc_irq(priv); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return IRQ_HANDLED; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int meson_dumb_create(struct drm_file *file, struct drm_device *dev, 8362306a36Sopenharmony_ci struct drm_mode_create_dumb *args) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * We need 64bytes aligned stride, and PAGE aligned size 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64); 8962306a36Sopenharmony_ci args->size = PAGE_ALIGN(args->pitch * args->height); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return drm_gem_dma_dumb_create_internal(file, dev, args); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciDEFINE_DRM_GEM_DMA_FOPS(fops); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct drm_driver meson_driver = { 9762306a36Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* DMA Ops */ 10062306a36Sopenharmony_ci DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create), 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Misc */ 10362306a36Sopenharmony_ci .fops = &fops, 10462306a36Sopenharmony_ci .name = DRIVER_NAME, 10562306a36Sopenharmony_ci .desc = DRIVER_DESC, 10662306a36Sopenharmony_ci .date = "20161109", 10762306a36Sopenharmony_ci .major = 1, 10862306a36Sopenharmony_ci .minor = 0, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic bool meson_vpu_has_available_connectors(struct device *dev) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct device_node *ep, *remote; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Parses each endpoint and check if remote exists */ 11662306a36Sopenharmony_ci for_each_endpoint_of_node(dev->of_node, ep) { 11762306a36Sopenharmony_ci /* If the endpoint node exists, consider it enabled */ 11862306a36Sopenharmony_ci remote = of_graph_get_remote_port(ep); 11962306a36Sopenharmony_ci if (remote) { 12062306a36Sopenharmony_ci of_node_put(remote); 12162306a36Sopenharmony_ci of_node_put(ep); 12262306a36Sopenharmony_ci return true; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return false; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct regmap_config meson_regmap_config = { 13062306a36Sopenharmony_ci .reg_bits = 32, 13162306a36Sopenharmony_ci .val_bits = 32, 13262306a36Sopenharmony_ci .reg_stride = 4, 13362306a36Sopenharmony_ci .max_register = 0x1000, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void meson_vpu_init(struct meson_drm *priv) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci u32 value; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * Slave dc0 and dc5 connected to master port 1. 14262306a36Sopenharmony_ci * By default other slaves are connected to master port 0. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | 14562306a36Sopenharmony_ci VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); 14662306a36Sopenharmony_ci writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Slave dc0 connected to master port 1 */ 14962306a36Sopenharmony_ci value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); 15062306a36Sopenharmony_ci writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Slave dc4 and dc7 connected to master port 1 */ 15362306a36Sopenharmony_ci value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | 15462306a36Sopenharmony_ci VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); 15562306a36Sopenharmony_ci writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Slave dc1 connected to master port 1 */ 15862306a36Sopenharmony_ci value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); 15962306a36Sopenharmony_ci writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistruct meson_drm_soc_attr { 16362306a36Sopenharmony_ci struct meson_drm_soc_limits limits; 16462306a36Sopenharmony_ci const struct soc_device_attribute *attrs; 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct meson_drm_soc_attr meson_drm_soc_attrs[] = { 16862306a36Sopenharmony_ci /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */ 16962306a36Sopenharmony_ci { 17062306a36Sopenharmony_ci .limits = { 17162306a36Sopenharmony_ci .max_hdmi_phy_freq = 1650000, 17262306a36Sopenharmony_ci }, 17362306a36Sopenharmony_ci .attrs = (const struct soc_device_attribute []) { 17462306a36Sopenharmony_ci { .soc_id = "GXL (S805*)", }, 17562306a36Sopenharmony_ci { /* sentinel */ } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci }, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int meson_drv_bind_master(struct device *dev, bool has_components) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 18362306a36Sopenharmony_ci const struct meson_drm_match_data *match; 18462306a36Sopenharmony_ci struct meson_drm *priv; 18562306a36Sopenharmony_ci struct drm_device *drm; 18662306a36Sopenharmony_ci struct resource *res; 18762306a36Sopenharmony_ci void __iomem *regs; 18862306a36Sopenharmony_ci int ret, i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Checks if an output connector is available */ 19162306a36Sopenharmony_ci if (!meson_vpu_has_available_connectors(dev)) { 19262306a36Sopenharmony_ci dev_err(dev, "No output connector available\n"); 19362306a36Sopenharmony_ci return -ENODEV; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci match = of_device_get_match_data(dev); 19762306a36Sopenharmony_ci if (!match) 19862306a36Sopenharmony_ci return -ENODEV; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci drm = drm_dev_alloc(&meson_driver, dev); 20162306a36Sopenharmony_ci if (IS_ERR(drm)) 20262306a36Sopenharmony_ci return PTR_ERR(drm); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 20562306a36Sopenharmony_ci if (!priv) { 20662306a36Sopenharmony_ci ret = -ENOMEM; 20762306a36Sopenharmony_ci goto free_drm; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci drm->dev_private = priv; 21062306a36Sopenharmony_ci priv->drm = drm; 21162306a36Sopenharmony_ci priv->dev = dev; 21262306a36Sopenharmony_ci priv->compat = match->compat; 21362306a36Sopenharmony_ci priv->afbcd.ops = match->afbcd_ops; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci regs = devm_platform_ioremap_resource_byname(pdev, "vpu"); 21662306a36Sopenharmony_ci if (IS_ERR(regs)) { 21762306a36Sopenharmony_ci ret = PTR_ERR(regs); 21862306a36Sopenharmony_ci goto free_drm; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci priv->io_base = regs; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); 22462306a36Sopenharmony_ci if (!res) { 22562306a36Sopenharmony_ci ret = -EINVAL; 22662306a36Sopenharmony_ci goto free_drm; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci /* Simply ioremap since it may be a shared register zone */ 22962306a36Sopenharmony_ci regs = devm_ioremap(dev, res->start, resource_size(res)); 23062306a36Sopenharmony_ci if (!regs) { 23162306a36Sopenharmony_ci ret = -EADDRNOTAVAIL; 23262306a36Sopenharmony_ci goto free_drm; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci priv->hhi = devm_regmap_init_mmio(dev, regs, 23662306a36Sopenharmony_ci &meson_regmap_config); 23762306a36Sopenharmony_ci if (IS_ERR(priv->hhi)) { 23862306a36Sopenharmony_ci dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); 23962306a36Sopenharmony_ci ret = PTR_ERR(priv->hhi); 24062306a36Sopenharmony_ci goto free_drm; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci priv->canvas = meson_canvas_get(dev); 24462306a36Sopenharmony_ci if (IS_ERR(priv->canvas)) { 24562306a36Sopenharmony_ci ret = PTR_ERR(priv->canvas); 24662306a36Sopenharmony_ci goto free_drm; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); 25062306a36Sopenharmony_ci if (ret) 25162306a36Sopenharmony_ci goto free_drm; 25262306a36Sopenharmony_ci ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); 25362306a36Sopenharmony_ci if (ret) { 25462306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 25562306a36Sopenharmony_ci goto free_drm; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); 25862306a36Sopenharmony_ci if (ret) { 25962306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 26062306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 26162306a36Sopenharmony_ci goto free_drm; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); 26462306a36Sopenharmony_ci if (ret) { 26562306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 26662306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 26762306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 26862306a36Sopenharmony_ci goto free_drm; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci priv->vsync_irq = platform_get_irq(pdev, 0); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = drm_vblank_init(drm, 1); 27462306a36Sopenharmony_ci if (ret) 27562306a36Sopenharmony_ci goto free_drm; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Assign limits per soc revision/package */ 27862306a36Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) { 27962306a36Sopenharmony_ci if (soc_device_match(meson_drm_soc_attrs[i].attrs)) { 28062306a36Sopenharmony_ci priv->limits = &meson_drm_soc_attrs[i].limits; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* 28662306a36Sopenharmony_ci * Remove early framebuffers (ie. simplefb). The framebuffer can be 28762306a36Sopenharmony_ci * located anywhere in RAM 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci ret = drm_aperture_remove_framebuffers(&meson_driver); 29062306a36Sopenharmony_ci if (ret) 29162306a36Sopenharmony_ci goto free_drm; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ret = drmm_mode_config_init(drm); 29462306a36Sopenharmony_ci if (ret) 29562306a36Sopenharmony_ci goto free_drm; 29662306a36Sopenharmony_ci drm->mode_config.max_width = 3840; 29762306a36Sopenharmony_ci drm->mode_config.max_height = 2160; 29862306a36Sopenharmony_ci drm->mode_config.funcs = &meson_mode_config_funcs; 29962306a36Sopenharmony_ci drm->mode_config.helper_private = &meson_mode_config_helpers; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Hardware Initialization */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci meson_vpu_init(priv); 30462306a36Sopenharmony_ci meson_venc_init(priv); 30562306a36Sopenharmony_ci meson_vpp_init(priv); 30662306a36Sopenharmony_ci meson_viu_init(priv); 30762306a36Sopenharmony_ci if (priv->afbcd.ops) { 30862306a36Sopenharmony_ci ret = priv->afbcd.ops->init(priv); 30962306a36Sopenharmony_ci if (ret) 31062306a36Sopenharmony_ci goto free_drm; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Encoder Initialization */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = meson_encoder_cvbs_init(priv); 31662306a36Sopenharmony_ci if (ret) 31762306a36Sopenharmony_ci goto exit_afbcd; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (has_components) { 32062306a36Sopenharmony_ci ret = component_bind_all(dev, drm); 32162306a36Sopenharmony_ci if (ret) { 32262306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't bind all components\n"); 32362306a36Sopenharmony_ci /* Do not try to unbind */ 32462306a36Sopenharmony_ci has_components = false; 32562306a36Sopenharmony_ci goto exit_afbcd; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ret = meson_encoder_hdmi_init(priv); 33062306a36Sopenharmony_ci if (ret) 33162306a36Sopenharmony_ci goto exit_afbcd; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { 33462306a36Sopenharmony_ci ret = meson_encoder_dsi_init(priv); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci goto exit_afbcd; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ret = meson_plane_create(priv); 34062306a36Sopenharmony_ci if (ret) 34162306a36Sopenharmony_ci goto exit_afbcd; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = meson_overlay_create(priv); 34462306a36Sopenharmony_ci if (ret) 34562306a36Sopenharmony_ci goto exit_afbcd; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = meson_crtc_create(priv); 34862306a36Sopenharmony_ci if (ret) 34962306a36Sopenharmony_ci goto exit_afbcd; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm); 35262306a36Sopenharmony_ci if (ret) 35362306a36Sopenharmony_ci goto exit_afbcd; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci drm_mode_config_reset(drm); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci drm_kms_helper_poll_init(drm); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ret = drm_dev_register(drm, 0); 36262306a36Sopenharmony_ci if (ret) 36362306a36Sopenharmony_ci goto uninstall_irq; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci drm_fbdev_dma_setup(drm, 32); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciuninstall_irq: 37062306a36Sopenharmony_ci free_irq(priv->vsync_irq, drm); 37162306a36Sopenharmony_ciexit_afbcd: 37262306a36Sopenharmony_ci if (priv->afbcd.ops) 37362306a36Sopenharmony_ci priv->afbcd.ops->exit(priv); 37462306a36Sopenharmony_cifree_drm: 37562306a36Sopenharmony_ci drm_dev_put(drm); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci meson_encoder_dsi_remove(priv); 37862306a36Sopenharmony_ci meson_encoder_hdmi_remove(priv); 37962306a36Sopenharmony_ci meson_encoder_cvbs_remove(priv); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (has_components) 38262306a36Sopenharmony_ci component_unbind_all(dev, drm); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return ret; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int meson_drv_bind(struct device *dev) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci return meson_drv_bind_master(dev, true); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void meson_drv_unbind(struct device *dev) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct meson_drm *priv = dev_get_drvdata(dev); 39562306a36Sopenharmony_ci struct drm_device *drm = priv->drm; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (priv->canvas) { 39862306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 39962306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 40062306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 40162306a36Sopenharmony_ci meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci drm_dev_unregister(drm); 40562306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 40662306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 40762306a36Sopenharmony_ci free_irq(priv->vsync_irq, drm); 40862306a36Sopenharmony_ci drm_dev_put(drm); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci meson_encoder_dsi_remove(priv); 41162306a36Sopenharmony_ci meson_encoder_hdmi_remove(priv); 41262306a36Sopenharmony_ci meson_encoder_cvbs_remove(priv); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci component_unbind_all(dev, drm); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (priv->afbcd.ops) 41762306a36Sopenharmony_ci priv->afbcd.ops->exit(priv); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic const struct component_master_ops meson_drv_master_ops = { 42162306a36Sopenharmony_ci .bind = meson_drv_bind, 42262306a36Sopenharmony_ci .unbind = meson_drv_unbind, 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int __maybe_unused meson_drv_pm_suspend(struct device *dev) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct meson_drm *priv = dev_get_drvdata(dev); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!priv) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return drm_mode_config_helper_suspend(priv->drm); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int __maybe_unused meson_drv_pm_resume(struct device *dev) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct meson_drm *priv = dev_get_drvdata(dev); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!priv) 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci meson_vpu_init(priv); 44362306a36Sopenharmony_ci meson_venc_init(priv); 44462306a36Sopenharmony_ci meson_vpp_init(priv); 44562306a36Sopenharmony_ci meson_viu_init(priv); 44662306a36Sopenharmony_ci if (priv->afbcd.ops) 44762306a36Sopenharmony_ci priv->afbcd.ops->init(priv); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return drm_mode_config_helper_resume(priv->drm); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void meson_drv_shutdown(struct platform_device *pdev) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct meson_drm *priv = dev_get_drvdata(&pdev->dev); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!priv) 45762306a36Sopenharmony_ci return; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci drm_kms_helper_poll_fini(priv->drm); 46062306a36Sopenharmony_ci drm_atomic_helper_shutdown(priv->drm); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/* 46462306a36Sopenharmony_ci * Only devices to use as components 46562306a36Sopenharmony_ci * TOFIX: get rid of components when we can finally 46662306a36Sopenharmony_ci * get meson_dx_hdmi to stop using the meson_drm 46762306a36Sopenharmony_ci * private structure for HHI registers. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_cistatic const struct of_device_id components_dev_match[] = { 47062306a36Sopenharmony_ci { .compatible = "amlogic,meson-gxbb-dw-hdmi" }, 47162306a36Sopenharmony_ci { .compatible = "amlogic,meson-gxl-dw-hdmi" }, 47262306a36Sopenharmony_ci { .compatible = "amlogic,meson-gxm-dw-hdmi" }, 47362306a36Sopenharmony_ci { .compatible = "amlogic,meson-g12a-dw-hdmi" }, 47462306a36Sopenharmony_ci {} 47562306a36Sopenharmony_ci}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int meson_drv_probe(struct platform_device *pdev) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct component_match *match = NULL; 48062306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 48162306a36Sopenharmony_ci struct device_node *ep, *remote; 48262306a36Sopenharmony_ci int count = 0; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci for_each_endpoint_of_node(np, ep) { 48562306a36Sopenharmony_ci remote = of_graph_get_remote_port_parent(ep); 48662306a36Sopenharmony_ci if (!remote || !of_device_is_available(remote)) { 48762306a36Sopenharmony_ci of_node_put(remote); 48862306a36Sopenharmony_ci continue; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (of_match_node(components_dev_match, remote)) { 49262306a36Sopenharmony_ci component_match_add(&pdev->dev, &match, component_compare_of, remote); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n", 49562306a36Sopenharmony_ci np, remote, dev_name(&pdev->dev)); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci of_node_put(remote); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci ++count; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (count && !match) 50462306a36Sopenharmony_ci return meson_drv_bind_master(&pdev->dev, false); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* If some endpoints were found, initialize the nodes */ 50762306a36Sopenharmony_ci if (count) { 50862306a36Sopenharmony_ci dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return component_master_add_with_match(&pdev->dev, 51162306a36Sopenharmony_ci &meson_drv_master_ops, 51262306a36Sopenharmony_ci match); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* If no output endpoints were available, simply bail out */ 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void meson_drv_remove(struct platform_device *pdev) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci component_master_del(&pdev->dev, &meson_drv_master_ops); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic struct meson_drm_match_data meson_drm_gxbb_data = { 52562306a36Sopenharmony_ci .compat = VPU_COMPATIBLE_GXBB, 52662306a36Sopenharmony_ci}; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic struct meson_drm_match_data meson_drm_gxl_data = { 52962306a36Sopenharmony_ci .compat = VPU_COMPATIBLE_GXL, 53062306a36Sopenharmony_ci}; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic struct meson_drm_match_data meson_drm_gxm_data = { 53362306a36Sopenharmony_ci .compat = VPU_COMPATIBLE_GXM, 53462306a36Sopenharmony_ci .afbcd_ops = &meson_afbcd_gxm_ops, 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic struct meson_drm_match_data meson_drm_g12a_data = { 53862306a36Sopenharmony_ci .compat = VPU_COMPATIBLE_G12A, 53962306a36Sopenharmony_ci .afbcd_ops = &meson_afbcd_g12a_ops, 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic const struct of_device_id dt_match[] = { 54362306a36Sopenharmony_ci { .compatible = "amlogic,meson-gxbb-vpu", 54462306a36Sopenharmony_ci .data = (void *)&meson_drm_gxbb_data }, 54562306a36Sopenharmony_ci { .compatible = "amlogic,meson-gxl-vpu", 54662306a36Sopenharmony_ci .data = (void *)&meson_drm_gxl_data }, 54762306a36Sopenharmony_ci { .compatible = "amlogic,meson-gxm-vpu", 54862306a36Sopenharmony_ci .data = (void *)&meson_drm_gxm_data }, 54962306a36Sopenharmony_ci { .compatible = "amlogic,meson-g12a-vpu", 55062306a36Sopenharmony_ci .data = (void *)&meson_drm_g12a_data }, 55162306a36Sopenharmony_ci {} 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dt_match); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic const struct dev_pm_ops meson_drv_pm_ops = { 55662306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume) 55762306a36Sopenharmony_ci}; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic struct platform_driver meson_drm_platform_driver = { 56062306a36Sopenharmony_ci .probe = meson_drv_probe, 56162306a36Sopenharmony_ci .remove_new = meson_drv_remove, 56262306a36Sopenharmony_ci .shutdown = meson_drv_shutdown, 56362306a36Sopenharmony_ci .driver = { 56462306a36Sopenharmony_ci .name = "meson-drm", 56562306a36Sopenharmony_ci .of_match_table = dt_match, 56662306a36Sopenharmony_ci .pm = &meson_drv_pm_ops, 56762306a36Sopenharmony_ci }, 56862306a36Sopenharmony_ci}; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cidrm_module_platform_driver(meson_drm_platform_driver); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciMODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); 57362306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 57462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 57562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 576