1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2c8afe684SRob Clark /* 35eba5d87SStephane Viau * Copyright (c) 2014 The Linux Foundation. All rights reserved. 4c8afe684SRob Clark * Copyright (C) 2013 Red Hat 5c8afe684SRob Clark * Author: Rob Clark <robdclark@gmail.com> 6c8afe684SRob Clark */ 7c8afe684SRob Clark 8f6a8eacaSRob Clark #include <linux/of_irq.h> 91fd6a441SArchit Taneja #include <linux/of_gpio.h> 101fd6a441SArchit Taneja 11*caa24223SDmitry Baryshkov #include <drm/drm_bridge_connector.h> 12*caa24223SDmitry Baryshkov 13f1427016SSrinivas Kandagatla #include <sound/hdmi-codec.h> 14c8afe684SRob Clark #include "hdmi.h" 15c8afe684SRob Clark 16fcda50c8SArnd Bergmann void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) 17c8afe684SRob Clark { 18c8afe684SRob Clark uint32_t ctrl = 0; 19c6a57a50Sjilai wang unsigned long flags; 20c8afe684SRob Clark 21c6a57a50Sjilai wang spin_lock_irqsave(&hdmi->reg_lock, flags); 22c8afe684SRob Clark if (power_on) { 23c8afe684SRob Clark ctrl |= HDMI_CTRL_ENABLE; 24c8afe684SRob Clark if (!hdmi->hdmi_mode) { 25c8afe684SRob Clark ctrl |= HDMI_CTRL_HDMI; 26c8afe684SRob Clark hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 27c8afe684SRob Clark ctrl &= ~HDMI_CTRL_HDMI; 28c8afe684SRob Clark } else { 29c8afe684SRob Clark ctrl |= HDMI_CTRL_HDMI; 30c8afe684SRob Clark } 31c8afe684SRob Clark } else { 32c8afe684SRob Clark ctrl = HDMI_CTRL_HDMI; 33c8afe684SRob Clark } 34c8afe684SRob Clark 35c8afe684SRob Clark hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 36c6a57a50Sjilai wang spin_unlock_irqrestore(&hdmi->reg_lock, flags); 37c8afe684SRob Clark DBG("HDMI Core: %s, HDMI_CTRL=0x%08x", 38c8afe684SRob Clark power_on ? "Enable" : "Disable", ctrl); 39c8afe684SRob Clark } 40c8afe684SRob Clark 41fcda50c8SArnd Bergmann static irqreturn_t msm_hdmi_irq(int irq, void *dev_id) 42c8afe684SRob Clark { 43c8afe684SRob Clark struct hdmi *hdmi = dev_id; 44c8afe684SRob Clark 45c8afe684SRob Clark /* Process HPD: */ 46*caa24223SDmitry Baryshkov msm_hdmi_hpd_irq(hdmi->bridge); 47c8afe684SRob Clark 48c8afe684SRob Clark /* Process DDC: */ 49fcda50c8SArnd Bergmann msm_hdmi_i2c_irq(hdmi->i2c); 50c8afe684SRob Clark 51c6a57a50Sjilai wang /* Process HDCP: */ 52c6a57a50Sjilai wang if (hdmi->hdcp_ctrl) 53fcda50c8SArnd Bergmann msm_hdmi_hdcp_irq(hdmi->hdcp_ctrl); 54c6a57a50Sjilai wang 55c8afe684SRob Clark /* TODO audio.. */ 56c8afe684SRob Clark 57c8afe684SRob Clark return IRQ_HANDLED; 58c8afe684SRob Clark } 59c8afe684SRob Clark 60fcda50c8SArnd Bergmann static void msm_hdmi_destroy(struct hdmi *hdmi) 61c8afe684SRob Clark { 62c6a57a50Sjilai wang /* 63c6a57a50Sjilai wang * at this point, hpd has been disabled, 64c6a57a50Sjilai wang * after flush workq, it's safe to deinit hdcp 65c6a57a50Sjilai wang */ 66f8e7bce3SChristophe JAILLET if (hdmi->workq) 67c6a57a50Sjilai wang destroy_workqueue(hdmi->workq); 68fcda50c8SArnd Bergmann msm_hdmi_hdcp_destroy(hdmi); 69c8afe684SRob Clark 70e00012b2SArchit Taneja if (hdmi->phy_dev) { 71e00012b2SArchit Taneja put_device(hdmi->phy_dev); 72e00012b2SArchit Taneja hdmi->phy = NULL; 73e00012b2SArchit Taneja hdmi->phy_dev = NULL; 74e00012b2SArchit Taneja } 75e00012b2SArchit Taneja 76c8afe684SRob Clark if (hdmi->i2c) 77fcda50c8SArnd Bergmann msm_hdmi_i2c_destroy(hdmi->i2c); 78c8afe684SRob Clark 79c0c0d9eeSRob Clark platform_set_drvdata(hdmi->pdev, NULL); 80c8afe684SRob Clark } 81c8afe684SRob Clark 82fcda50c8SArnd Bergmann static int msm_hdmi_get_phy(struct hdmi *hdmi) 83e00012b2SArchit Taneja { 84e00012b2SArchit Taneja struct platform_device *pdev = hdmi->pdev; 85e00012b2SArchit Taneja struct platform_device *phy_pdev; 86e00012b2SArchit Taneja struct device_node *phy_node; 87e00012b2SArchit Taneja 88e00012b2SArchit Taneja phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0); 89e00012b2SArchit Taneja if (!phy_node) { 906a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n"); 91e00012b2SArchit Taneja return -ENXIO; 92e00012b2SArchit Taneja } 93e00012b2SArchit Taneja 94e00012b2SArchit Taneja phy_pdev = of_find_device_by_node(phy_node); 95e00012b2SArchit Taneja if (phy_pdev) 96e00012b2SArchit Taneja hdmi->phy = platform_get_drvdata(phy_pdev); 97e00012b2SArchit Taneja 98e00012b2SArchit Taneja of_node_put(phy_node); 99e00012b2SArchit Taneja 100e00012b2SArchit Taneja if (!phy_pdev || !hdmi->phy) { 1016a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "phy driver is not ready\n"); 102e00012b2SArchit Taneja return -EPROBE_DEFER; 103e00012b2SArchit Taneja } 104e00012b2SArchit Taneja 105e00012b2SArchit Taneja hdmi->phy_dev = get_device(&phy_pdev->dev); 106e00012b2SArchit Taneja 107e00012b2SArchit Taneja return 0; 108e00012b2SArchit Taneja } 109e00012b2SArchit Taneja 110067fef37SRob Clark /* construct hdmi at bind/probe time, grab all the resources. If 111067fef37SRob Clark * we are to EPROBE_DEFER we want to do it here, rather than later 112067fef37SRob Clark * at modeset_init() time 113067fef37SRob Clark */ 114fcda50c8SArnd Bergmann static struct hdmi *msm_hdmi_init(struct platform_device *pdev) 115c8afe684SRob Clark { 116067fef37SRob Clark struct hdmi_platform_config *config = pdev->dev.platform_data; 117a3376e3eSRob Clark struct hdmi *hdmi = NULL; 118c6a57a50Sjilai wang struct resource *res; 119dada25bdSRob Clark int i, ret; 120c8afe684SRob Clark 121067fef37SRob Clark hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); 122a3376e3eSRob Clark if (!hdmi) { 123a3376e3eSRob Clark ret = -ENOMEM; 124a3376e3eSRob Clark goto fail; 125a3376e3eSRob Clark } 126a3376e3eSRob Clark 127c8afe684SRob Clark hdmi->pdev = pdev; 128dada25bdSRob Clark hdmi->config = config; 129c6a57a50Sjilai wang spin_lock_init(&hdmi->reg_lock); 130c0c0d9eeSRob Clark 131dada25bdSRob Clark hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); 132c8afe684SRob Clark if (IS_ERR(hdmi->mmio)) { 133c8afe684SRob Clark ret = PTR_ERR(hdmi->mmio); 134c8afe684SRob Clark goto fail; 135c8afe684SRob Clark } 136c8afe684SRob Clark 137c6a57a50Sjilai wang /* HDCP needs physical address of hdmi register */ 138c6a57a50Sjilai wang res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 139c6a57a50Sjilai wang config->mmio_name); 140c6a57a50Sjilai wang hdmi->mmio_phy_addr = res->start; 141c6a57a50Sjilai wang 142c6a57a50Sjilai wang hdmi->qfprom_mmio = msm_ioremap(pdev, 143c6a57a50Sjilai wang config->qfprom_mmio_name, "HDMI_QFPROM"); 144c6a57a50Sjilai wang if (IS_ERR(hdmi->qfprom_mmio)) { 1456a41da17SMamta Shukla DRM_DEV_INFO(&pdev->dev, "can't find qfprom resource\n"); 146c6a57a50Sjilai wang hdmi->qfprom_mmio = NULL; 147c6a57a50Sjilai wang } 148c6a57a50Sjilai wang 149a86854d0SKees Cook hdmi->hpd_regs = devm_kcalloc(&pdev->dev, 150a86854d0SKees Cook config->hpd_reg_cnt, 151a86854d0SKees Cook sizeof(hdmi->hpd_regs[0]), 152a86854d0SKees Cook GFP_KERNEL); 153447fa529SStephane Viau if (!hdmi->hpd_regs) { 154447fa529SStephane Viau ret = -ENOMEM; 155447fa529SStephane Viau goto fail; 156447fa529SStephane Viau } 15731b3b1f5SDmitry Baryshkov for (i = 0; i < config->hpd_reg_cnt; i++) 15831b3b1f5SDmitry Baryshkov hdmi->hpd_regs[i].supply = config->hpd_reg_names[i]; 159dada25bdSRob Clark 16031b3b1f5SDmitry Baryshkov ret = devm_regulator_bulk_get(&pdev->dev, config->hpd_reg_cnt, hdmi->hpd_regs); 16131b3b1f5SDmitry Baryshkov if (ret) { 16231b3b1f5SDmitry Baryshkov DRM_DEV_ERROR(&pdev->dev, "failed to get hpd regulator: %d\n", ret); 163c8afe684SRob Clark goto fail; 164c8afe684SRob Clark } 165c8afe684SRob Clark 166a86854d0SKees Cook hdmi->pwr_regs = devm_kcalloc(&pdev->dev, 167a86854d0SKees Cook config->pwr_reg_cnt, 168a86854d0SKees Cook sizeof(hdmi->pwr_regs[0]), 169a86854d0SKees Cook GFP_KERNEL); 170447fa529SStephane Viau if (!hdmi->pwr_regs) { 171447fa529SStephane Viau ret = -ENOMEM; 172447fa529SStephane Viau goto fail; 173447fa529SStephane Viau } 174dada25bdSRob Clark 17531b3b1f5SDmitry Baryshkov ret = devm_regulator_bulk_get(&pdev->dev, config->pwr_reg_cnt, hdmi->pwr_regs); 17631b3b1f5SDmitry Baryshkov if (ret) { 17731b3b1f5SDmitry Baryshkov DRM_DEV_ERROR(&pdev->dev, "failed to get pwr regulator: %d\n", ret); 178c8afe684SRob Clark goto fail; 179c8afe684SRob Clark } 180c8afe684SRob Clark 181a86854d0SKees Cook hdmi->hpd_clks = devm_kcalloc(&pdev->dev, 182a86854d0SKees Cook config->hpd_clk_cnt, 183a86854d0SKees Cook sizeof(hdmi->hpd_clks[0]), 184a86854d0SKees Cook GFP_KERNEL); 185447fa529SStephane Viau if (!hdmi->hpd_clks) { 186447fa529SStephane Viau ret = -ENOMEM; 187447fa529SStephane Viau goto fail; 188447fa529SStephane Viau } 189dada25bdSRob Clark for (i = 0; i < config->hpd_clk_cnt; i++) { 190dada25bdSRob Clark struct clk *clk; 191dada25bdSRob Clark 192aede1e9eSRob Clark clk = msm_clk_get(pdev, config->hpd_clk_names[i]); 193dada25bdSRob Clark if (IS_ERR(clk)) { 194dada25bdSRob Clark ret = PTR_ERR(clk); 1956a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "failed to get hpd clk: %s (%d)\n", 196dada25bdSRob Clark config->hpd_clk_names[i], ret); 197c8afe684SRob Clark goto fail; 198c8afe684SRob Clark } 199c8afe684SRob Clark 200dada25bdSRob Clark hdmi->hpd_clks[i] = clk; 201dada25bdSRob Clark } 202dada25bdSRob Clark 203a86854d0SKees Cook hdmi->pwr_clks = devm_kcalloc(&pdev->dev, 204a86854d0SKees Cook config->pwr_clk_cnt, 205a86854d0SKees Cook sizeof(hdmi->pwr_clks[0]), 206a86854d0SKees Cook GFP_KERNEL); 207447fa529SStephane Viau if (!hdmi->pwr_clks) { 208447fa529SStephane Viau ret = -ENOMEM; 209447fa529SStephane Viau goto fail; 210447fa529SStephane Viau } 211dada25bdSRob Clark for (i = 0; i < config->pwr_clk_cnt; i++) { 212dada25bdSRob Clark struct clk *clk; 213dada25bdSRob Clark 214aede1e9eSRob Clark clk = msm_clk_get(pdev, config->pwr_clk_names[i]); 215dada25bdSRob Clark if (IS_ERR(clk)) { 216dada25bdSRob Clark ret = PTR_ERR(clk); 2176a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "failed to get pwr clk: %s (%d)\n", 218dada25bdSRob Clark config->pwr_clk_names[i], ret); 219c8afe684SRob Clark goto fail; 220c8afe684SRob Clark } 221c8afe684SRob Clark 222dada25bdSRob Clark hdmi->pwr_clks[i] = clk; 223dada25bdSRob Clark } 224dada25bdSRob Clark 2256ed9ed48SArchit Taneja pm_runtime_enable(&pdev->dev); 2266ed9ed48SArchit Taneja 227c6a57a50Sjilai wang hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0); 228c6a57a50Sjilai wang 229fcda50c8SArnd Bergmann hdmi->i2c = msm_hdmi_i2c_init(hdmi); 230c8afe684SRob Clark if (IS_ERR(hdmi->i2c)) { 231c8afe684SRob Clark ret = PTR_ERR(hdmi->i2c); 2326a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "failed to get i2c: %d\n", ret); 233c8afe684SRob Clark hdmi->i2c = NULL; 234c8afe684SRob Clark goto fail; 235c8afe684SRob Clark } 236c8afe684SRob Clark 237fcda50c8SArnd Bergmann ret = msm_hdmi_get_phy(hdmi); 238e00012b2SArchit Taneja if (ret) { 2396a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "failed to get phy\n"); 240e00012b2SArchit Taneja goto fail; 241e00012b2SArchit Taneja } 242e00012b2SArchit Taneja 243fcda50c8SArnd Bergmann hdmi->hdcp_ctrl = msm_hdmi_hdcp_init(hdmi); 244c6a57a50Sjilai wang if (IS_ERR(hdmi->hdcp_ctrl)) { 245c6a57a50Sjilai wang dev_warn(&pdev->dev, "failed to init hdcp: disabled\n"); 246c6a57a50Sjilai wang hdmi->hdcp_ctrl = NULL; 247c6a57a50Sjilai wang } 248c6a57a50Sjilai wang 249067fef37SRob Clark return hdmi; 250067fef37SRob Clark 251067fef37SRob Clark fail: 252067fef37SRob Clark if (hdmi) 253fcda50c8SArnd Bergmann msm_hdmi_destroy(hdmi); 254067fef37SRob Clark 255067fef37SRob Clark return ERR_PTR(ret); 256067fef37SRob Clark } 257067fef37SRob Clark 258067fef37SRob Clark /* Second part of initialization, the drm/kms level modeset_init, 259067fef37SRob Clark * constructs/initializes mode objects, etc, is called from master 260067fef37SRob Clark * driver (not hdmi sub-device's probe/bind!) 261067fef37SRob Clark * 262067fef37SRob Clark * Any resource (regulator/clk/etc) which could be missing at boot 263fcda50c8SArnd Bergmann * should be handled in msm_hdmi_init() so that failure happens from 264067fef37SRob Clark * hdmi sub-device's probe. 265067fef37SRob Clark */ 266fcda50c8SArnd Bergmann int msm_hdmi_modeset_init(struct hdmi *hdmi, 267067fef37SRob Clark struct drm_device *dev, struct drm_encoder *encoder) 268067fef37SRob Clark { 269067fef37SRob Clark struct msm_drm_private *priv = dev->dev_private; 270067fef37SRob Clark struct platform_device *pdev = hdmi->pdev; 271067fef37SRob Clark int ret; 272067fef37SRob Clark 273067fef37SRob Clark hdmi->dev = dev; 274067fef37SRob Clark hdmi->encoder = encoder; 275067fef37SRob Clark 276067fef37SRob Clark hdmi_audio_infoframe_init(&hdmi->audio.infoframe); 277067fef37SRob Clark 278fcda50c8SArnd Bergmann hdmi->bridge = msm_hdmi_bridge_init(hdmi); 279a3376e3eSRob Clark if (IS_ERR(hdmi->bridge)) { 280a3376e3eSRob Clark ret = PTR_ERR(hdmi->bridge); 2816a41da17SMamta Shukla DRM_DEV_ERROR(dev->dev, "failed to create HDMI bridge: %d\n", ret); 282a3376e3eSRob Clark hdmi->bridge = NULL; 283a3376e3eSRob Clark goto fail; 284a3376e3eSRob Clark } 285a3376e3eSRob Clark 286*caa24223SDmitry Baryshkov hdmi->connector = drm_bridge_connector_init(hdmi->dev, encoder); 287a3376e3eSRob Clark if (IS_ERR(hdmi->connector)) { 288a3376e3eSRob Clark ret = PTR_ERR(hdmi->connector); 2896a41da17SMamta Shukla DRM_DEV_ERROR(dev->dev, "failed to create HDMI connector: %d\n", ret); 290a3376e3eSRob Clark hdmi->connector = NULL; 291a3376e3eSRob Clark goto fail; 292a3376e3eSRob Clark } 293a3376e3eSRob Clark 294*caa24223SDmitry Baryshkov drm_connector_attach_encoder(hdmi->connector, hdmi->encoder); 295*caa24223SDmitry Baryshkov 296f6a8eacaSRob Clark hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 297c8afe684SRob Clark if (hdmi->irq < 0) { 298c8afe684SRob Clark ret = hdmi->irq; 2996a41da17SMamta Shukla DRM_DEV_ERROR(dev->dev, "failed to get irq: %d\n", ret); 300c8afe684SRob Clark goto fail; 301c8afe684SRob Clark } 302c8afe684SRob Clark 303f6a8eacaSRob Clark ret = devm_request_irq(&pdev->dev, hdmi->irq, 304fcda50c8SArnd Bergmann msm_hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 305c8afe684SRob Clark "hdmi_isr", hdmi); 306c8afe684SRob Clark if (ret < 0) { 3076a41da17SMamta Shukla DRM_DEV_ERROR(dev->dev, "failed to request IRQ%u: %d\n", 308c8afe684SRob Clark hdmi->irq, ret); 309c8afe684SRob Clark goto fail; 310c8afe684SRob Clark } 311c8afe684SRob Clark 312*caa24223SDmitry Baryshkov drm_bridge_connector_enable_hpd(hdmi->connector); 313*caa24223SDmitry Baryshkov 314*caa24223SDmitry Baryshkov ret = msm_hdmi_hpd_enable(hdmi->bridge); 315ee445635STodor Tomov if (ret < 0) { 316ee445635STodor Tomov DRM_DEV_ERROR(&hdmi->pdev->dev, "failed to enable HPD: %d\n", ret); 317ee445635STodor Tomov goto fail; 318ee445635STodor Tomov } 319ee445635STodor Tomov 320a3376e3eSRob Clark priv->bridges[priv->num_bridges++] = hdmi->bridge; 321a3376e3eSRob Clark priv->connectors[priv->num_connectors++] = hdmi->connector; 322a3376e3eSRob Clark 323c0c0d9eeSRob Clark platform_set_drvdata(pdev, hdmi); 324c0c0d9eeSRob Clark 325067fef37SRob Clark return 0; 326c8afe684SRob Clark 327c8afe684SRob Clark fail: 3283d3f8b1fSAjay Kumar /* bridge is normally destroyed by drm: */ 329067fef37SRob Clark if (hdmi->bridge) { 330fcda50c8SArnd Bergmann msm_hdmi_bridge_destroy(hdmi->bridge); 331067fef37SRob Clark hdmi->bridge = NULL; 332067fef37SRob Clark } 333067fef37SRob Clark if (hdmi->connector) { 334a3376e3eSRob Clark hdmi->connector->funcs->destroy(hdmi->connector); 335067fef37SRob Clark hdmi->connector = NULL; 336a3376e3eSRob Clark } 337c8afe684SRob Clark 338067fef37SRob Clark return ret; 339c8afe684SRob Clark } 340c8afe684SRob Clark 341c8afe684SRob Clark /* 342c8afe684SRob Clark * The hdmi device: 343c8afe684SRob Clark */ 344c8afe684SRob Clark 3455eba5d87SStephane Viau #define HDMI_CFG(item, entry) \ 3465eba5d87SStephane Viau .item ## _names = item ##_names_ ## entry, \ 3475eba5d87SStephane Viau .item ## _cnt = ARRAY_SIZE(item ## _names_ ## entry) 3485eba5d87SStephane Viau 3490afbe59eSStephane Viau static const char *pwr_reg_names_none[] = {}; 3500afbe59eSStephane Viau static const char *hpd_reg_names_none[] = {}; 3510afbe59eSStephane Viau 352ba3d7bf3SArchit Taneja static struct hdmi_platform_config hdmi_tx_8660_config; 3535eba5d87SStephane Viau 3545eba5d87SStephane Viau static const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"}; 355aede1e9eSRob Clark static const char *hpd_clk_names_8960[] = {"core", "master_iface", "slave_iface"}; 3565eba5d87SStephane Viau 3575eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8960_config = { 3585eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8960), 3595eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8960), 3605eba5d87SStephane Viau }; 3615eba5d87SStephane Viau 3625eba5d87SStephane Viau static const char *pwr_reg_names_8x74[] = {"core-vdda", "core-vcc"}; 3635eba5d87SStephane Viau static const char *hpd_reg_names_8x74[] = {"hpd-gdsc", "hpd-5v"}; 364aede1e9eSRob Clark static const char *pwr_clk_names_8x74[] = {"extp", "alt_iface"}; 365aede1e9eSRob Clark static const char *hpd_clk_names_8x74[] = {"iface", "core", "mdp_core"}; 3665eba5d87SStephane Viau static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; 3675eba5d87SStephane Viau 3685cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8974_config = { 3695eba5d87SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3705eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8x74), 3715eba5d87SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3725eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3735eba5d87SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3745eba5d87SStephane Viau }; 3755eba5d87SStephane Viau 3765eba5d87SStephane Viau static const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"}; 3775eba5d87SStephane Viau 3785eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8084_config = { 3795eba5d87SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3805eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8084), 3815eba5d87SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3825eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3835eba5d87SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3845eba5d87SStephane Viau }; 3855eba5d87SStephane Viau 3865cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8994_config = { 3873a84f846SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3880afbe59eSStephane Viau HDMI_CFG(hpd_reg, none), 3890afbe59eSStephane Viau HDMI_CFG(pwr_clk, 8x74), 3900afbe59eSStephane Viau HDMI_CFG(hpd_clk, 8x74), 3910afbe59eSStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3920afbe59eSStephane Viau }; 3930afbe59eSStephane Viau 3940afbe59eSStephane Viau static struct hdmi_platform_config hdmi_tx_8996_config = { 3950afbe59eSStephane Viau HDMI_CFG(pwr_reg, none), 3960afbe59eSStephane Viau HDMI_CFG(hpd_reg, none), 3973a84f846SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3983a84f846SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3993a84f846SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 4003a84f846SStephane Viau }; 4013a84f846SStephane Viau 402dc50f782SArchit Taneja static const struct { 403dc50f782SArchit Taneja const char *name; 404dc50f782SArchit Taneja const bool output; 405dc50f782SArchit Taneja const int value; 406dc50f782SArchit Taneja const char *label; 407fcda50c8SArnd Bergmann } msm_hdmi_gpio_pdata[] = { 408dc50f782SArchit Taneja { "qcom,hdmi-tx-ddc-clk", true, 1, "HDMI_DDC_CLK" }, 409dc50f782SArchit Taneja { "qcom,hdmi-tx-ddc-data", true, 1, "HDMI_DDC_DATA" }, 410dc50f782SArchit Taneja { "qcom,hdmi-tx-hpd", false, 1, "HDMI_HPD" }, 411dc50f782SArchit Taneja { "qcom,hdmi-tx-mux-en", true, 1, "HDMI_MUX_EN" }, 412dc50f782SArchit Taneja { "qcom,hdmi-tx-mux-sel", true, 0, "HDMI_MUX_SEL" }, 413dc50f782SArchit Taneja { "qcom,hdmi-tx-mux-lpm", true, 1, "HDMI_MUX_LPM" }, 414dc50f782SArchit Taneja }; 415dc50f782SArchit Taneja 416f1427016SSrinivas Kandagatla /* 417f1427016SSrinivas Kandagatla * HDMI audio codec callbacks 418f1427016SSrinivas Kandagatla */ 419f1427016SSrinivas Kandagatla static int msm_hdmi_audio_hw_params(struct device *dev, void *data, 420f1427016SSrinivas Kandagatla struct hdmi_codec_daifmt *daifmt, 421f1427016SSrinivas Kandagatla struct hdmi_codec_params *params) 422f1427016SSrinivas Kandagatla { 423f1427016SSrinivas Kandagatla struct hdmi *hdmi = dev_get_drvdata(dev); 424f1427016SSrinivas Kandagatla unsigned int chan; 425f1427016SSrinivas Kandagatla unsigned int channel_allocation = 0; 426f1427016SSrinivas Kandagatla unsigned int rate; 427f1427016SSrinivas Kandagatla unsigned int level_shift = 0; /* 0dB */ 428f1427016SSrinivas Kandagatla bool down_mix = false; 429f1427016SSrinivas Kandagatla 4306a41da17SMamta Shukla DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, 431f1427016SSrinivas Kandagatla params->sample_width, params->cea.channels); 432f1427016SSrinivas Kandagatla 433f1427016SSrinivas Kandagatla switch (params->cea.channels) { 434f1427016SSrinivas Kandagatla case 2: 435f1427016SSrinivas Kandagatla /* FR and FL speakers */ 436f1427016SSrinivas Kandagatla channel_allocation = 0; 437f1427016SSrinivas Kandagatla chan = MSM_HDMI_AUDIO_CHANNEL_2; 438f1427016SSrinivas Kandagatla break; 439f1427016SSrinivas Kandagatla case 4: 440f1427016SSrinivas Kandagatla /* FC, LFE, FR and FL speakers */ 441f1427016SSrinivas Kandagatla channel_allocation = 0x3; 442f1427016SSrinivas Kandagatla chan = MSM_HDMI_AUDIO_CHANNEL_4; 443f1427016SSrinivas Kandagatla break; 444f1427016SSrinivas Kandagatla case 6: 445f1427016SSrinivas Kandagatla /* RR, RL, FC, LFE, FR and FL speakers */ 446f1427016SSrinivas Kandagatla channel_allocation = 0x0B; 447f1427016SSrinivas Kandagatla chan = MSM_HDMI_AUDIO_CHANNEL_6; 448f1427016SSrinivas Kandagatla break; 449f1427016SSrinivas Kandagatla case 8: 450f1427016SSrinivas Kandagatla /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */ 451f1427016SSrinivas Kandagatla channel_allocation = 0x1F; 452f1427016SSrinivas Kandagatla chan = MSM_HDMI_AUDIO_CHANNEL_8; 453f1427016SSrinivas Kandagatla break; 454f1427016SSrinivas Kandagatla default: 455f1427016SSrinivas Kandagatla return -EINVAL; 456f1427016SSrinivas Kandagatla } 457f1427016SSrinivas Kandagatla 458f1427016SSrinivas Kandagatla switch (params->sample_rate) { 459f1427016SSrinivas Kandagatla case 32000: 460f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_32KHZ; 461f1427016SSrinivas Kandagatla break; 462f1427016SSrinivas Kandagatla case 44100: 463f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_44_1KHZ; 464f1427016SSrinivas Kandagatla break; 465f1427016SSrinivas Kandagatla case 48000: 466f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_48KHZ; 467f1427016SSrinivas Kandagatla break; 468f1427016SSrinivas Kandagatla case 88200: 469f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_88_2KHZ; 470f1427016SSrinivas Kandagatla break; 471f1427016SSrinivas Kandagatla case 96000: 472f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_96KHZ; 473f1427016SSrinivas Kandagatla break; 474f1427016SSrinivas Kandagatla case 176400: 475f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_176_4KHZ; 476f1427016SSrinivas Kandagatla break; 477f1427016SSrinivas Kandagatla case 192000: 478f1427016SSrinivas Kandagatla rate = HDMI_SAMPLE_RATE_192KHZ; 479f1427016SSrinivas Kandagatla break; 480f1427016SSrinivas Kandagatla default: 4816a41da17SMamta Shukla DRM_DEV_ERROR(dev, "rate[%d] not supported!\n", 482f1427016SSrinivas Kandagatla params->sample_rate); 483f1427016SSrinivas Kandagatla return -EINVAL; 484f1427016SSrinivas Kandagatla } 485f1427016SSrinivas Kandagatla 486f1427016SSrinivas Kandagatla msm_hdmi_audio_set_sample_rate(hdmi, rate); 487f1427016SSrinivas Kandagatla msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation, 488f1427016SSrinivas Kandagatla level_shift, down_mix); 489f1427016SSrinivas Kandagatla 490f1427016SSrinivas Kandagatla return 0; 491f1427016SSrinivas Kandagatla } 492f1427016SSrinivas Kandagatla 493f1427016SSrinivas Kandagatla static void msm_hdmi_audio_shutdown(struct device *dev, void *data) 494f1427016SSrinivas Kandagatla { 495f1427016SSrinivas Kandagatla struct hdmi *hdmi = dev_get_drvdata(dev); 496f1427016SSrinivas Kandagatla 497f1427016SSrinivas Kandagatla msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); 498f1427016SSrinivas Kandagatla } 499f1427016SSrinivas Kandagatla 500f1427016SSrinivas Kandagatla static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { 501f1427016SSrinivas Kandagatla .hw_params = msm_hdmi_audio_hw_params, 502f1427016SSrinivas Kandagatla .audio_shutdown = msm_hdmi_audio_shutdown, 503f1427016SSrinivas Kandagatla }; 504f1427016SSrinivas Kandagatla 505f1427016SSrinivas Kandagatla static struct hdmi_codec_pdata codec_data = { 506f1427016SSrinivas Kandagatla .ops = &msm_hdmi_audio_codec_ops, 507f1427016SSrinivas Kandagatla .max_i2s_channels = 8, 508f1427016SSrinivas Kandagatla .i2s = 1, 509f1427016SSrinivas Kandagatla }; 510f1427016SSrinivas Kandagatla 511f1427016SSrinivas Kandagatla static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) 512f1427016SSrinivas Kandagatla { 513f1427016SSrinivas Kandagatla hdmi->audio_pdev = platform_device_register_data(dev, 514f1427016SSrinivas Kandagatla HDMI_CODEC_DRV_NAME, 515f1427016SSrinivas Kandagatla PLATFORM_DEVID_AUTO, 516f1427016SSrinivas Kandagatla &codec_data, 517f1427016SSrinivas Kandagatla sizeof(codec_data)); 51806f32172SWei Yongjun return PTR_ERR_OR_ZERO(hdmi->audio_pdev); 519f1427016SSrinivas Kandagatla } 520f1427016SSrinivas Kandagatla 521fcda50c8SArnd Bergmann static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) 522fc886107SMark Charlebois { 523ec919e6eSAngeloGioacchino Del Regno struct msm_drm_private *priv = dev_get_drvdata(master); 52467146519SYueHaibing struct hdmi_platform_config *hdmi_cfg; 525067fef37SRob Clark struct hdmi *hdmi; 526fc886107SMark Charlebois struct device_node *of_node = dev->of_node; 527f1427016SSrinivas Kandagatla int i, err; 528dada25bdSRob Clark 5291fd6a441SArchit Taneja hdmi_cfg = (struct hdmi_platform_config *) 5301fd6a441SArchit Taneja of_device_get_match_data(dev); 5311fd6a441SArchit Taneja if (!hdmi_cfg) { 5326a41da17SMamta Shukla DRM_DEV_ERROR(dev, "unknown hdmi_cfg: %pOFn\n", of_node); 5335eba5d87SStephane Viau return -ENXIO; 53441e69778SRob Clark } 53541e69778SRob Clark 5365eba5d87SStephane Viau hdmi_cfg->mmio_name = "core_physical"; 537c6a57a50Sjilai wang hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; 538dc50f782SArchit Taneja 539dc50f782SArchit Taneja for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) { 540a44769b4SLinus Walleij const char *name = msm_hdmi_gpio_pdata[i].name; 541a44769b4SLinus Walleij struct gpio_desc *gpiod; 542a44769b4SLinus Walleij 543a44769b4SLinus Walleij /* 544a44769b4SLinus Walleij * We are fetching the GPIO lines "as is" since the connector 545a44769b4SLinus Walleij * code is enabling and disabling the lines. Until that point 546a44769b4SLinus Walleij * the power-on default value will be kept. 547a44769b4SLinus Walleij */ 548a44769b4SLinus Walleij gpiod = devm_gpiod_get_optional(dev, name, GPIOD_ASIS); 549a44769b4SLinus Walleij /* This will catch e.g. -PROBE_DEFER */ 550a44769b4SLinus Walleij if (IS_ERR(gpiod)) 551a44769b4SLinus Walleij return PTR_ERR(gpiod); 552a44769b4SLinus Walleij if (!gpiod) { 553a44769b4SLinus Walleij /* Try a second time, stripping down the name */ 554a44769b4SLinus Walleij char name3[32]; 555a44769b4SLinus Walleij 556a44769b4SLinus Walleij /* 557a44769b4SLinus Walleij * Try again after stripping out the "qcom,hdmi-tx" 558a44769b4SLinus Walleij * prefix. This is mainly to match "hpd-gpios" used 559a44769b4SLinus Walleij * in the upstream bindings. 560a44769b4SLinus Walleij */ 561a44769b4SLinus Walleij if (sscanf(name, "qcom,hdmi-tx-%s", name3)) 562a44769b4SLinus Walleij gpiod = devm_gpiod_get_optional(dev, name3, GPIOD_ASIS); 563a44769b4SLinus Walleij if (IS_ERR(gpiod)) 564a44769b4SLinus Walleij return PTR_ERR(gpiod); 565a44769b4SLinus Walleij if (!gpiod) 566a44769b4SLinus Walleij DBG("failed to get gpio: %s", name); 567a44769b4SLinus Walleij } 568a44769b4SLinus Walleij hdmi_cfg->gpios[i].gpiod = gpiod; 569a44769b4SLinus Walleij if (gpiod) 570a44769b4SLinus Walleij gpiod_set_consumer_name(gpiod, msm_hdmi_gpio_pdata[i].label); 571fcda50c8SArnd Bergmann hdmi_cfg->gpios[i].output = msm_hdmi_gpio_pdata[i].output; 572fcda50c8SArnd Bergmann hdmi_cfg->gpios[i].value = msm_hdmi_gpio_pdata[i].value; 573dc50f782SArchit Taneja } 574dada25bdSRob Clark 5755eba5d87SStephane Viau dev->platform_data = hdmi_cfg; 5765eba5d87SStephane Viau 577fcda50c8SArnd Bergmann hdmi = msm_hdmi_init(to_platform_device(dev)); 578067fef37SRob Clark if (IS_ERR(hdmi)) 579067fef37SRob Clark return PTR_ERR(hdmi); 580d1a717bdSRob Clark priv->hdmi = hdmi; 5815eba5d87SStephane Viau 582f1427016SSrinivas Kandagatla err = msm_hdmi_register_audio_driver(hdmi, dev); 583f1427016SSrinivas Kandagatla if (err) { 584f1427016SSrinivas Kandagatla DRM_ERROR("Failed to attach an audio codec %d\n", err); 585f1427016SSrinivas Kandagatla hdmi->audio_pdev = NULL; 586f1427016SSrinivas Kandagatla } 587f1427016SSrinivas Kandagatla 588c8afe684SRob Clark return 0; 589c8afe684SRob Clark } 590c8afe684SRob Clark 591fcda50c8SArnd Bergmann static void msm_hdmi_unbind(struct device *dev, struct device *master, 592060530f1SRob Clark void *data) 593060530f1SRob Clark { 594ec919e6eSAngeloGioacchino Del Regno struct msm_drm_private *priv = dev_get_drvdata(master); 595ec919e6eSAngeloGioacchino Del Regno 596d1a717bdSRob Clark if (priv->hdmi) { 597f1427016SSrinivas Kandagatla if (priv->hdmi->audio_pdev) 598f1427016SSrinivas Kandagatla platform_device_unregister(priv->hdmi->audio_pdev); 599f1427016SSrinivas Kandagatla 600fcda50c8SArnd Bergmann msm_hdmi_destroy(priv->hdmi); 601d1a717bdSRob Clark priv->hdmi = NULL; 602d1a717bdSRob Clark } 603060530f1SRob Clark } 604060530f1SRob Clark 605fcda50c8SArnd Bergmann static const struct component_ops msm_hdmi_ops = { 606fcda50c8SArnd Bergmann .bind = msm_hdmi_bind, 607fcda50c8SArnd Bergmann .unbind = msm_hdmi_unbind, 608060530f1SRob Clark }; 609060530f1SRob Clark 610fcda50c8SArnd Bergmann static int msm_hdmi_dev_probe(struct platform_device *pdev) 611060530f1SRob Clark { 612fcda50c8SArnd Bergmann return component_add(&pdev->dev, &msm_hdmi_ops); 613060530f1SRob Clark } 614060530f1SRob Clark 615fcda50c8SArnd Bergmann static int msm_hdmi_dev_remove(struct platform_device *pdev) 616c8afe684SRob Clark { 617fcda50c8SArnd Bergmann component_del(&pdev->dev, &msm_hdmi_ops); 618c8afe684SRob Clark return 0; 619c8afe684SRob Clark } 620c8afe684SRob Clark 621fcda50c8SArnd Bergmann static const struct of_device_id msm_hdmi_dt_match[] = { 6221fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config }, 6231fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config }, 6241fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config }, 6251fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config }, 6261fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config }, 6271fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config }, 6281fd6a441SArchit Taneja {} 6291fd6a441SArchit Taneja }; 6301fd6a441SArchit Taneja 631fcda50c8SArnd Bergmann static struct platform_driver msm_hdmi_driver = { 632fcda50c8SArnd Bergmann .probe = msm_hdmi_dev_probe, 633fcda50c8SArnd Bergmann .remove = msm_hdmi_dev_remove, 634dada25bdSRob Clark .driver = { 635dada25bdSRob Clark .name = "hdmi_msm", 636fcda50c8SArnd Bergmann .of_match_table = msm_hdmi_dt_match, 637dada25bdSRob Clark }, 638c8afe684SRob Clark }; 639c8afe684SRob Clark 640fcda50c8SArnd Bergmann void __init msm_hdmi_register(void) 641c8afe684SRob Clark { 642fcda50c8SArnd Bergmann msm_hdmi_phy_driver_register(); 643fcda50c8SArnd Bergmann platform_driver_register(&msm_hdmi_driver); 644c8afe684SRob Clark } 645c8afe684SRob Clark 646fcda50c8SArnd Bergmann void __exit msm_hdmi_unregister(void) 647c8afe684SRob Clark { 648fcda50c8SArnd Bergmann platform_driver_unregister(&msm_hdmi_driver); 649fcda50c8SArnd Bergmann msm_hdmi_phy_driver_unregister(); 650c8afe684SRob Clark } 651