18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 Red Hat 58c2ecf20Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 98c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <drm/drm_bridge_connector.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <sound/hdmi-codec.h> 148c2ecf20Sopenharmony_ci#include "hdmi.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_civoid msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci uint32_t ctrl = 0; 198c2ecf20Sopenharmony_ci unsigned long flags; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 228c2ecf20Sopenharmony_ci if (power_on) { 238c2ecf20Sopenharmony_ci ctrl |= HDMI_CTRL_ENABLE; 248c2ecf20Sopenharmony_ci if (!hdmi->hdmi_mode) { 258c2ecf20Sopenharmony_ci ctrl |= HDMI_CTRL_HDMI; 268c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 278c2ecf20Sopenharmony_ci ctrl &= ~HDMI_CTRL_HDMI; 288c2ecf20Sopenharmony_ci } else { 298c2ecf20Sopenharmony_ci ctrl |= HDMI_CTRL_HDMI; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci } else { 328c2ecf20Sopenharmony_ci ctrl = HDMI_CTRL_HDMI; 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 378c2ecf20Sopenharmony_ci DBG("HDMI Core: %s, HDMI_CTRL=0x%08x", 388c2ecf20Sopenharmony_ci power_on ? "Enable" : "Disable", ctrl); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic irqreturn_t msm_hdmi_irq(int irq, void *dev_id) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct hdmi *hdmi = dev_id; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Process HPD: */ 468c2ecf20Sopenharmony_ci msm_hdmi_hpd_irq(hdmi->bridge); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Process DDC: */ 498c2ecf20Sopenharmony_ci msm_hdmi_i2c_irq(hdmi->i2c); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Process HDCP: */ 528c2ecf20Sopenharmony_ci if (hdmi->hdcp_ctrl) 538c2ecf20Sopenharmony_ci msm_hdmi_hdcp_irq(hdmi->hdcp_ctrl); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* TODO audio.. */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void msm_hdmi_destroy(struct hdmi *hdmi) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * at this point, hpd has been disabled, 648c2ecf20Sopenharmony_ci * after flush workq, it's safe to deinit hdcp 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci if (hdmi->workq) { 678c2ecf20Sopenharmony_ci flush_workqueue(hdmi->workq); 688c2ecf20Sopenharmony_ci destroy_workqueue(hdmi->workq); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci msm_hdmi_hdcp_destroy(hdmi); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (hdmi->phy_dev) { 738c2ecf20Sopenharmony_ci put_device(hdmi->phy_dev); 748c2ecf20Sopenharmony_ci hdmi->phy = NULL; 758c2ecf20Sopenharmony_ci hdmi->phy_dev = NULL; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (hdmi->i2c) 798c2ecf20Sopenharmony_ci msm_hdmi_i2c_destroy(hdmi->i2c); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci platform_set_drvdata(hdmi->pdev, NULL); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int msm_hdmi_get_phy(struct hdmi *hdmi) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct platform_device *pdev = hdmi->pdev; 878c2ecf20Sopenharmony_ci struct platform_device *phy_pdev; 888c2ecf20Sopenharmony_ci struct device_node *phy_node; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0); 918c2ecf20Sopenharmony_ci if (!phy_node) { 928c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n"); 938c2ecf20Sopenharmony_ci return -ENXIO; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci phy_pdev = of_find_device_by_node(phy_node); 978c2ecf20Sopenharmony_ci if (phy_pdev) 988c2ecf20Sopenharmony_ci hdmi->phy = platform_get_drvdata(phy_pdev); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci of_node_put(phy_node); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!phy_pdev) { 1038c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "phy driver is not ready\n"); 1048c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci if (!hdmi->phy) { 1078c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "phy driver is not ready\n"); 1088c2ecf20Sopenharmony_ci put_device(&phy_pdev->dev); 1098c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci hdmi->phy_dev = get_device(&phy_pdev->dev); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* construct hdmi at bind/probe time, grab all the resources. If 1188c2ecf20Sopenharmony_ci * we are to EPROBE_DEFER we want to do it here, rather than later 1198c2ecf20Sopenharmony_ci * at modeset_init() time 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic struct hdmi *msm_hdmi_init(struct platform_device *pdev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct hdmi_platform_config *config = pdev->dev.platform_data; 1248c2ecf20Sopenharmony_ci struct hdmi *hdmi = NULL; 1258c2ecf20Sopenharmony_ci struct resource *res; 1268c2ecf20Sopenharmony_ci int i, ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!hdmi) { 1308c2ecf20Sopenharmony_ci ret = -ENOMEM; 1318c2ecf20Sopenharmony_ci goto fail; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci hdmi->pdev = pdev; 1358c2ecf20Sopenharmony_ci hdmi->config = config; 1368c2ecf20Sopenharmony_ci spin_lock_init(&hdmi->reg_lock); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); 1398c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->mmio)) { 1408c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->mmio); 1418c2ecf20Sopenharmony_ci goto fail; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* HDCP needs physical address of hdmi register */ 1458c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 1468c2ecf20Sopenharmony_ci config->mmio_name); 1478c2ecf20Sopenharmony_ci if (!res) { 1488c2ecf20Sopenharmony_ci ret = -EINVAL; 1498c2ecf20Sopenharmony_ci goto fail; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci hdmi->mmio_phy_addr = res->start; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci hdmi->qfprom_mmio = msm_ioremap(pdev, 1548c2ecf20Sopenharmony_ci config->qfprom_mmio_name, "HDMI_QFPROM"); 1558c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->qfprom_mmio)) { 1568c2ecf20Sopenharmony_ci DRM_DEV_INFO(&pdev->dev, "can't find qfprom resource\n"); 1578c2ecf20Sopenharmony_ci hdmi->qfprom_mmio = NULL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci hdmi->hpd_regs = devm_kcalloc(&pdev->dev, 1618c2ecf20Sopenharmony_ci config->hpd_reg_cnt, 1628c2ecf20Sopenharmony_ci sizeof(hdmi->hpd_regs[0]), 1638c2ecf20Sopenharmony_ci GFP_KERNEL); 1648c2ecf20Sopenharmony_ci if (!hdmi->hpd_regs) { 1658c2ecf20Sopenharmony_ci ret = -ENOMEM; 1668c2ecf20Sopenharmony_ci goto fail; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci for (i = 0; i < config->hpd_reg_cnt; i++) { 1698c2ecf20Sopenharmony_ci struct regulator *reg; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci reg = devm_regulator_get(&pdev->dev, 1728c2ecf20Sopenharmony_ci config->hpd_reg_names[i]); 1738c2ecf20Sopenharmony_ci if (IS_ERR(reg)) { 1748c2ecf20Sopenharmony_ci ret = PTR_ERR(reg); 1758c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get hpd regulator: %s (%d)\n", 1768c2ecf20Sopenharmony_ci config->hpd_reg_names[i], ret); 1778c2ecf20Sopenharmony_ci goto fail; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci hdmi->hpd_regs[i] = reg; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci hdmi->pwr_regs = devm_kcalloc(&pdev->dev, 1848c2ecf20Sopenharmony_ci config->pwr_reg_cnt, 1858c2ecf20Sopenharmony_ci sizeof(hdmi->pwr_regs[0]), 1868c2ecf20Sopenharmony_ci GFP_KERNEL); 1878c2ecf20Sopenharmony_ci if (!hdmi->pwr_regs) { 1888c2ecf20Sopenharmony_ci ret = -ENOMEM; 1898c2ecf20Sopenharmony_ci goto fail; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci for (i = 0; i < config->pwr_reg_cnt; i++) { 1928c2ecf20Sopenharmony_ci struct regulator *reg; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci reg = devm_regulator_get(&pdev->dev, 1958c2ecf20Sopenharmony_ci config->pwr_reg_names[i]); 1968c2ecf20Sopenharmony_ci if (IS_ERR(reg)) { 1978c2ecf20Sopenharmony_ci ret = PTR_ERR(reg); 1988c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get pwr regulator: %s (%d)\n", 1998c2ecf20Sopenharmony_ci config->pwr_reg_names[i], ret); 2008c2ecf20Sopenharmony_ci goto fail; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci hdmi->pwr_regs[i] = reg; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci hdmi->hpd_clks = devm_kcalloc(&pdev->dev, 2078c2ecf20Sopenharmony_ci config->hpd_clk_cnt, 2088c2ecf20Sopenharmony_ci sizeof(hdmi->hpd_clks[0]), 2098c2ecf20Sopenharmony_ci GFP_KERNEL); 2108c2ecf20Sopenharmony_ci if (!hdmi->hpd_clks) { 2118c2ecf20Sopenharmony_ci ret = -ENOMEM; 2128c2ecf20Sopenharmony_ci goto fail; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci for (i = 0; i < config->hpd_clk_cnt; i++) { 2158c2ecf20Sopenharmony_ci struct clk *clk; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci clk = msm_clk_get(pdev, config->hpd_clk_names[i]); 2188c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 2198c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 2208c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get hpd clk: %s (%d)\n", 2218c2ecf20Sopenharmony_ci config->hpd_clk_names[i], ret); 2228c2ecf20Sopenharmony_ci goto fail; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci hdmi->hpd_clks[i] = clk; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci hdmi->pwr_clks = devm_kcalloc(&pdev->dev, 2298c2ecf20Sopenharmony_ci config->pwr_clk_cnt, 2308c2ecf20Sopenharmony_ci sizeof(hdmi->pwr_clks[0]), 2318c2ecf20Sopenharmony_ci GFP_KERNEL); 2328c2ecf20Sopenharmony_ci if (!hdmi->pwr_clks) { 2338c2ecf20Sopenharmony_ci ret = -ENOMEM; 2348c2ecf20Sopenharmony_ci goto fail; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci for (i = 0; i < config->pwr_clk_cnt; i++) { 2378c2ecf20Sopenharmony_ci struct clk *clk; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci clk = msm_clk_get(pdev, config->pwr_clk_names[i]); 2408c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 2418c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 2428c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get pwr clk: %s (%d)\n", 2438c2ecf20Sopenharmony_ci config->pwr_clk_names[i], ret); 2448c2ecf20Sopenharmony_ci goto fail; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci hdmi->pwr_clks[i] = clk; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci hdmi->hpd_gpiod = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN); 2518c2ecf20Sopenharmony_ci /* This will catch e.g. -EPROBE_DEFER */ 2528c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->hpd_gpiod)) { 2538c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->hpd_gpiod); 2548c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get hpd gpio: (%d)\n", ret); 2558c2ecf20Sopenharmony_ci goto fail; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!hdmi->hpd_gpiod) 2598c2ecf20Sopenharmony_ci DBG("failed to get HPD gpio"); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (hdmi->hpd_gpiod) 2628c2ecf20Sopenharmony_ci gpiod_set_consumer_name(hdmi->hpd_gpiod, "HDMI_HPD"); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0); 2678c2ecf20Sopenharmony_ci if (!hdmi->workq) { 2688c2ecf20Sopenharmony_ci ret = -ENOMEM; 2698c2ecf20Sopenharmony_ci goto fail; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci hdmi->i2c = msm_hdmi_i2c_init(hdmi); 2738c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->i2c)) { 2748c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->i2c); 2758c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get i2c: %d\n", ret); 2768c2ecf20Sopenharmony_ci hdmi->i2c = NULL; 2778c2ecf20Sopenharmony_ci goto fail; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ret = msm_hdmi_get_phy(hdmi); 2818c2ecf20Sopenharmony_ci if (ret) { 2828c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "failed to get phy\n"); 2838c2ecf20Sopenharmony_ci goto fail; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci hdmi->hdcp_ctrl = msm_hdmi_hdcp_init(hdmi); 2878c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->hdcp_ctrl)) { 2888c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "failed to init hdcp: disabled\n"); 2898c2ecf20Sopenharmony_ci hdmi->hdcp_ctrl = NULL; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return hdmi; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cifail: 2958c2ecf20Sopenharmony_ci if (hdmi) 2968c2ecf20Sopenharmony_ci msm_hdmi_destroy(hdmi); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* Second part of initialization, the drm/kms level modeset_init, 3028c2ecf20Sopenharmony_ci * constructs/initializes mode objects, etc, is called from master 3038c2ecf20Sopenharmony_ci * driver (not hdmi sub-device's probe/bind!) 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * Any resource (regulator/clk/etc) which could be missing at boot 3068c2ecf20Sopenharmony_ci * should be handled in msm_hdmi_init() so that failure happens from 3078c2ecf20Sopenharmony_ci * hdmi sub-device's probe. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ciint msm_hdmi_modeset_init(struct hdmi *hdmi, 3108c2ecf20Sopenharmony_ci struct drm_device *dev, struct drm_encoder *encoder) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 3138c2ecf20Sopenharmony_ci struct platform_device *pdev = hdmi->pdev; 3148c2ecf20Sopenharmony_ci int ret; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (priv->num_bridges == ARRAY_SIZE(priv->bridges)) { 3178c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "too many bridges\n"); 3188c2ecf20Sopenharmony_ci return -ENOSPC; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci hdmi->dev = dev; 3228c2ecf20Sopenharmony_ci hdmi->encoder = encoder; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci hdmi_audio_infoframe_init(&hdmi->audio.infoframe); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci hdmi->bridge = msm_hdmi_bridge_init(hdmi); 3278c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->bridge)) { 3288c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->bridge); 3298c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to create HDMI bridge: %d\n", ret); 3308c2ecf20Sopenharmony_ci hdmi->bridge = NULL; 3318c2ecf20Sopenharmony_ci goto fail; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci hdmi->connector = drm_bridge_connector_init(hdmi->dev, encoder); 3358c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->connector)) { 3368c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->connector); 3378c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to create HDMI connector: %d\n", ret); 3388c2ecf20Sopenharmony_ci hdmi->connector = NULL; 3398c2ecf20Sopenharmony_ci goto fail; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci drm_connector_attach_encoder(hdmi->connector, hdmi->encoder); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 3458c2ecf20Sopenharmony_ci if (!hdmi->irq) { 3468c2ecf20Sopenharmony_ci ret = -EINVAL; 3478c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to get irq\n"); 3488c2ecf20Sopenharmony_ci goto fail; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = devm_request_irq(dev->dev, hdmi->irq, 3528c2ecf20Sopenharmony_ci msm_hdmi_irq, IRQF_TRIGGER_HIGH, 3538c2ecf20Sopenharmony_ci "hdmi_isr", hdmi); 3548c2ecf20Sopenharmony_ci if (ret < 0) { 3558c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to request IRQ%u: %d\n", 3568c2ecf20Sopenharmony_ci hdmi->irq, ret); 3578c2ecf20Sopenharmony_ci goto fail; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci drm_bridge_connector_enable_hpd(hdmi->connector); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = msm_hdmi_hpd_enable(hdmi->bridge); 3638c2ecf20Sopenharmony_ci if (ret < 0) { 3648c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&hdmi->pdev->dev, "failed to enable HPD: %d\n", ret); 3658c2ecf20Sopenharmony_ci goto fail; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci priv->bridges[priv->num_bridges++] = hdmi->bridge; 3698c2ecf20Sopenharmony_ci priv->connectors[priv->num_connectors++] = hdmi->connector; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, hdmi); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cifail: 3768c2ecf20Sopenharmony_ci /* bridge is normally destroyed by drm: */ 3778c2ecf20Sopenharmony_ci if (hdmi->bridge) { 3788c2ecf20Sopenharmony_ci msm_hdmi_bridge_destroy(hdmi->bridge); 3798c2ecf20Sopenharmony_ci hdmi->bridge = NULL; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci if (hdmi->connector) { 3828c2ecf20Sopenharmony_ci hdmi->connector->funcs->destroy(hdmi->connector); 3838c2ecf20Sopenharmony_ci hdmi->connector = NULL; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * The hdmi device: 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci#define HDMI_CFG(item, entry) \ 3948c2ecf20Sopenharmony_ci .item ## _names = item ##_names_ ## entry, \ 3958c2ecf20Sopenharmony_ci .item ## _cnt = ARRAY_SIZE(item ## _names_ ## entry) 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic const char *pwr_reg_names_none[] = {}; 3988c2ecf20Sopenharmony_cistatic const char *hpd_reg_names_none[] = {}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic struct hdmi_platform_config hdmi_tx_8660_config; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"}; 4038c2ecf20Sopenharmony_cistatic const char *hpd_clk_names_8960[] = {"core", "master_iface", "slave_iface"}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic struct hdmi_platform_config hdmi_tx_8960_config = { 4068c2ecf20Sopenharmony_ci HDMI_CFG(hpd_reg, 8960), 4078c2ecf20Sopenharmony_ci HDMI_CFG(hpd_clk, 8960), 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic const char *pwr_reg_names_8x74[] = {"core-vdda", "core-vcc"}; 4118c2ecf20Sopenharmony_cistatic const char *hpd_reg_names_8x74[] = {"hpd-gdsc", "hpd-5v"}; 4128c2ecf20Sopenharmony_cistatic const char *pwr_clk_names_8x74[] = {"extp", "alt_iface"}; 4138c2ecf20Sopenharmony_cistatic const char *hpd_clk_names_8x74[] = {"iface", "core", "mdp_core"}; 4148c2ecf20Sopenharmony_cistatic unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic struct hdmi_platform_config hdmi_tx_8974_config = { 4178c2ecf20Sopenharmony_ci HDMI_CFG(pwr_reg, 8x74), 4188c2ecf20Sopenharmony_ci HDMI_CFG(hpd_reg, 8x74), 4198c2ecf20Sopenharmony_ci HDMI_CFG(pwr_clk, 8x74), 4208c2ecf20Sopenharmony_ci HDMI_CFG(hpd_clk, 8x74), 4218c2ecf20Sopenharmony_ci .hpd_freq = hpd_clk_freq_8x74, 4228c2ecf20Sopenharmony_ci}; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic struct hdmi_platform_config hdmi_tx_8084_config = { 4278c2ecf20Sopenharmony_ci HDMI_CFG(pwr_reg, 8x74), 4288c2ecf20Sopenharmony_ci HDMI_CFG(hpd_reg, 8084), 4298c2ecf20Sopenharmony_ci HDMI_CFG(pwr_clk, 8x74), 4308c2ecf20Sopenharmony_ci HDMI_CFG(hpd_clk, 8x74), 4318c2ecf20Sopenharmony_ci .hpd_freq = hpd_clk_freq_8x74, 4328c2ecf20Sopenharmony_ci}; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic struct hdmi_platform_config hdmi_tx_8994_config = { 4358c2ecf20Sopenharmony_ci HDMI_CFG(pwr_reg, 8x74), 4368c2ecf20Sopenharmony_ci HDMI_CFG(hpd_reg, none), 4378c2ecf20Sopenharmony_ci HDMI_CFG(pwr_clk, 8x74), 4388c2ecf20Sopenharmony_ci HDMI_CFG(hpd_clk, 8x74), 4398c2ecf20Sopenharmony_ci .hpd_freq = hpd_clk_freq_8x74, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic struct hdmi_platform_config hdmi_tx_8996_config = { 4438c2ecf20Sopenharmony_ci HDMI_CFG(pwr_reg, none), 4448c2ecf20Sopenharmony_ci HDMI_CFG(hpd_reg, none), 4458c2ecf20Sopenharmony_ci HDMI_CFG(pwr_clk, 8x74), 4468c2ecf20Sopenharmony_ci HDMI_CFG(hpd_clk, 8x74), 4478c2ecf20Sopenharmony_ci .hpd_freq = hpd_clk_freq_8x74, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* 4518c2ecf20Sopenharmony_ci * HDMI audio codec callbacks 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_cistatic int msm_hdmi_audio_hw_params(struct device *dev, void *data, 4548c2ecf20Sopenharmony_ci struct hdmi_codec_daifmt *daifmt, 4558c2ecf20Sopenharmony_ci struct hdmi_codec_params *params) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct hdmi *hdmi = dev_get_drvdata(dev); 4588c2ecf20Sopenharmony_ci unsigned int chan; 4598c2ecf20Sopenharmony_ci unsigned int channel_allocation = 0; 4608c2ecf20Sopenharmony_ci unsigned int rate; 4618c2ecf20Sopenharmony_ci unsigned int level_shift = 0; /* 0dB */ 4628c2ecf20Sopenharmony_ci bool down_mix = false; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, 4658c2ecf20Sopenharmony_ci params->sample_width, params->cea.channels); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci switch (params->cea.channels) { 4688c2ecf20Sopenharmony_ci case 2: 4698c2ecf20Sopenharmony_ci /* FR and FL speakers */ 4708c2ecf20Sopenharmony_ci channel_allocation = 0; 4718c2ecf20Sopenharmony_ci chan = MSM_HDMI_AUDIO_CHANNEL_2; 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci case 4: 4748c2ecf20Sopenharmony_ci /* FC, LFE, FR and FL speakers */ 4758c2ecf20Sopenharmony_ci channel_allocation = 0x3; 4768c2ecf20Sopenharmony_ci chan = MSM_HDMI_AUDIO_CHANNEL_4; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci case 6: 4798c2ecf20Sopenharmony_ci /* RR, RL, FC, LFE, FR and FL speakers */ 4808c2ecf20Sopenharmony_ci channel_allocation = 0x0B; 4818c2ecf20Sopenharmony_ci chan = MSM_HDMI_AUDIO_CHANNEL_6; 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci case 8: 4848c2ecf20Sopenharmony_ci /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */ 4858c2ecf20Sopenharmony_ci channel_allocation = 0x1F; 4868c2ecf20Sopenharmony_ci chan = MSM_HDMI_AUDIO_CHANNEL_8; 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci default: 4898c2ecf20Sopenharmony_ci return -EINVAL; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci switch (params->sample_rate) { 4938c2ecf20Sopenharmony_ci case 32000: 4948c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_32KHZ; 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci case 44100: 4978c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_44_1KHZ; 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case 48000: 5008c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_48KHZ; 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case 88200: 5038c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_88_2KHZ; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case 96000: 5068c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_96KHZ; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case 176400: 5098c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_176_4KHZ; 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci case 192000: 5128c2ecf20Sopenharmony_ci rate = HDMI_SAMPLE_RATE_192KHZ; 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci default: 5158c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "rate[%d] not supported!\n", 5168c2ecf20Sopenharmony_ci params->sample_rate); 5178c2ecf20Sopenharmony_ci return -EINVAL; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci msm_hdmi_audio_set_sample_rate(hdmi, rate); 5218c2ecf20Sopenharmony_ci msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation, 5228c2ecf20Sopenharmony_ci level_shift, down_mix); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void msm_hdmi_audio_shutdown(struct device *dev, void *data) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct hdmi *hdmi = dev_get_drvdata(dev); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { 5358c2ecf20Sopenharmony_ci .hw_params = msm_hdmi_audio_hw_params, 5368c2ecf20Sopenharmony_ci .audio_shutdown = msm_hdmi_audio_shutdown, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic struct hdmi_codec_pdata codec_data = { 5408c2ecf20Sopenharmony_ci .ops = &msm_hdmi_audio_codec_ops, 5418c2ecf20Sopenharmony_ci .max_i2s_channels = 8, 5428c2ecf20Sopenharmony_ci .i2s = 1, 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci hdmi->audio_pdev = platform_device_register_data(dev, 5488c2ecf20Sopenharmony_ci HDMI_CODEC_DRV_NAME, 5498c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO, 5508c2ecf20Sopenharmony_ci &codec_data, 5518c2ecf20Sopenharmony_ci sizeof(codec_data)); 5528c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hdmi->audio_pdev); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int msm_hdmi_bind(struct device *dev, struct device *master, void *data) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 5588c2ecf20Sopenharmony_ci struct msm_drm_private *priv = drm->dev_private; 5598c2ecf20Sopenharmony_ci struct hdmi_platform_config *hdmi_cfg; 5608c2ecf20Sopenharmony_ci struct hdmi *hdmi; 5618c2ecf20Sopenharmony_ci struct device_node *of_node = dev->of_node; 5628c2ecf20Sopenharmony_ci int err; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci hdmi_cfg = (struct hdmi_platform_config *) 5658c2ecf20Sopenharmony_ci of_device_get_match_data(dev); 5668c2ecf20Sopenharmony_ci if (!hdmi_cfg) { 5678c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "unknown hdmi_cfg: %pOFn\n", of_node); 5688c2ecf20Sopenharmony_ci return -ENXIO; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci hdmi_cfg->mmio_name = "core_physical"; 5728c2ecf20Sopenharmony_ci hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci dev->platform_data = hdmi_cfg; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci hdmi = msm_hdmi_init(to_platform_device(dev)); 5778c2ecf20Sopenharmony_ci if (IS_ERR(hdmi)) 5788c2ecf20Sopenharmony_ci return PTR_ERR(hdmi); 5798c2ecf20Sopenharmony_ci priv->hdmi = hdmi; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci err = msm_hdmi_register_audio_driver(hdmi, dev); 5828c2ecf20Sopenharmony_ci if (err) { 5838c2ecf20Sopenharmony_ci DRM_ERROR("Failed to attach an audio codec %d\n", err); 5848c2ecf20Sopenharmony_ci hdmi->audio_pdev = NULL; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic void msm_hdmi_unbind(struct device *dev, struct device *master, 5918c2ecf20Sopenharmony_ci void *data) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 5948c2ecf20Sopenharmony_ci struct msm_drm_private *priv = drm->dev_private; 5958c2ecf20Sopenharmony_ci if (priv->hdmi) { 5968c2ecf20Sopenharmony_ci if (priv->hdmi->audio_pdev) 5978c2ecf20Sopenharmony_ci platform_device_unregister(priv->hdmi->audio_pdev); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci msm_hdmi_destroy(priv->hdmi); 6008c2ecf20Sopenharmony_ci priv->hdmi = NULL; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic const struct component_ops msm_hdmi_ops = { 6058c2ecf20Sopenharmony_ci .bind = msm_hdmi_bind, 6068c2ecf20Sopenharmony_ci .unbind = msm_hdmi_unbind, 6078c2ecf20Sopenharmony_ci}; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int msm_hdmi_dev_probe(struct platform_device *pdev) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &msm_hdmi_ops); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int msm_hdmi_dev_remove(struct platform_device *pdev) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci component_del(&pdev->dev, &msm_hdmi_ops); 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic const struct of_device_id msm_hdmi_dt_match[] = { 6218c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config }, 6228c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config }, 6238c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config }, 6248c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config }, 6258c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config }, 6268c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config }, 6278c2ecf20Sopenharmony_ci {} 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic struct platform_driver msm_hdmi_driver = { 6318c2ecf20Sopenharmony_ci .probe = msm_hdmi_dev_probe, 6328c2ecf20Sopenharmony_ci .remove = msm_hdmi_dev_remove, 6338c2ecf20Sopenharmony_ci .driver = { 6348c2ecf20Sopenharmony_ci .name = "hdmi_msm", 6358c2ecf20Sopenharmony_ci .of_match_table = msm_hdmi_dt_match, 6368c2ecf20Sopenharmony_ci }, 6378c2ecf20Sopenharmony_ci}; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_civoid __init msm_hdmi_register(void) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci msm_hdmi_phy_driver_register(); 6428c2ecf20Sopenharmony_ci platform_driver_register(&msm_hdmi_driver); 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_civoid __exit msm_hdmi_unregister(void) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci platform_driver_unregister(&msm_hdmi_driver); 6488c2ecf20Sopenharmony_ci msm_hdmi_phy_driver_unregister(); 6498c2ecf20Sopenharmony_ci} 650