1c8afe684SRob Clark /* 25eba5d87SStephane Viau * Copyright (c) 2014 The Linux Foundation. All rights reserved. 3c8afe684SRob Clark * Copyright (C) 2013 Red Hat 4c8afe684SRob Clark * Author: Rob Clark <robdclark@gmail.com> 5c8afe684SRob Clark * 6c8afe684SRob Clark * This program is free software; you can redistribute it and/or modify it 7c8afe684SRob Clark * under the terms of the GNU General Public License version 2 as published by 8c8afe684SRob Clark * the Free Software Foundation. 9c8afe684SRob Clark * 10c8afe684SRob Clark * This program is distributed in the hope that it will be useful, but WITHOUT 11c8afe684SRob Clark * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12c8afe684SRob Clark * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13c8afe684SRob Clark * more details. 14c8afe684SRob Clark * 15c8afe684SRob Clark * You should have received a copy of the GNU General Public License along with 16c8afe684SRob Clark * this program. If not, see <http://www.gnu.org/licenses/>. 17c8afe684SRob Clark */ 18c8afe684SRob Clark 19f6a8eacaSRob Clark #include <linux/of_irq.h> 201fd6a441SArchit Taneja #include <linux/of_gpio.h> 211fd6a441SArchit Taneja 22c8afe684SRob Clark #include "hdmi.h" 23c8afe684SRob Clark 24c8afe684SRob Clark void hdmi_set_mode(struct hdmi *hdmi, bool power_on) 25c8afe684SRob Clark { 26c8afe684SRob Clark uint32_t ctrl = 0; 27c6a57a50Sjilai wang unsigned long flags; 28c8afe684SRob Clark 29c6a57a50Sjilai wang spin_lock_irqsave(&hdmi->reg_lock, flags); 30c8afe684SRob Clark if (power_on) { 31c8afe684SRob Clark ctrl |= HDMI_CTRL_ENABLE; 32c8afe684SRob Clark if (!hdmi->hdmi_mode) { 33c8afe684SRob Clark ctrl |= HDMI_CTRL_HDMI; 34c8afe684SRob Clark hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 35c8afe684SRob Clark ctrl &= ~HDMI_CTRL_HDMI; 36c8afe684SRob Clark } else { 37c8afe684SRob Clark ctrl |= HDMI_CTRL_HDMI; 38c8afe684SRob Clark } 39c8afe684SRob Clark } else { 40c8afe684SRob Clark ctrl = HDMI_CTRL_HDMI; 41c8afe684SRob Clark } 42c8afe684SRob Clark 43c8afe684SRob Clark hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 44c6a57a50Sjilai wang spin_unlock_irqrestore(&hdmi->reg_lock, flags); 45c8afe684SRob Clark DBG("HDMI Core: %s, HDMI_CTRL=0x%08x", 46c8afe684SRob Clark power_on ? "Enable" : "Disable", ctrl); 47c8afe684SRob Clark } 48c8afe684SRob Clark 49f6a8eacaSRob Clark static irqreturn_t hdmi_irq(int irq, void *dev_id) 50c8afe684SRob Clark { 51c8afe684SRob Clark struct hdmi *hdmi = dev_id; 52c8afe684SRob Clark 53c8afe684SRob Clark /* Process HPD: */ 54c8afe684SRob Clark hdmi_connector_irq(hdmi->connector); 55c8afe684SRob Clark 56c8afe684SRob Clark /* Process DDC: */ 57c8afe684SRob Clark hdmi_i2c_irq(hdmi->i2c); 58c8afe684SRob Clark 59c6a57a50Sjilai wang /* Process HDCP: */ 60c6a57a50Sjilai wang if (hdmi->hdcp_ctrl) 61c6a57a50Sjilai wang hdmi_hdcp_irq(hdmi->hdcp_ctrl); 62c6a57a50Sjilai wang 63c8afe684SRob Clark /* TODO audio.. */ 64c8afe684SRob Clark 65c8afe684SRob Clark return IRQ_HANDLED; 66c8afe684SRob Clark } 67c8afe684SRob Clark 68d1a717bdSRob Clark static void hdmi_destroy(struct hdmi *hdmi) 69c8afe684SRob Clark { 70c6a57a50Sjilai wang /* 71c6a57a50Sjilai wang * at this point, hpd has been disabled, 72c6a57a50Sjilai wang * after flush workq, it's safe to deinit hdcp 73c6a57a50Sjilai wang */ 74c6a57a50Sjilai wang if (hdmi->workq) { 75c6a57a50Sjilai wang flush_workqueue(hdmi->workq); 76c6a57a50Sjilai wang destroy_workqueue(hdmi->workq); 77c6a57a50Sjilai wang } 78c6a57a50Sjilai wang hdmi_hdcp_destroy(hdmi); 79c8afe684SRob Clark 80e00012b2SArchit Taneja if (hdmi->phy_dev) { 81e00012b2SArchit Taneja put_device(hdmi->phy_dev); 82e00012b2SArchit Taneja hdmi->phy = NULL; 83e00012b2SArchit Taneja hdmi->phy_dev = NULL; 84e00012b2SArchit Taneja } 85e00012b2SArchit Taneja 86c8afe684SRob Clark if (hdmi->i2c) 87c8afe684SRob Clark hdmi_i2c_destroy(hdmi->i2c); 88c8afe684SRob Clark 89c0c0d9eeSRob Clark platform_set_drvdata(hdmi->pdev, NULL); 90c8afe684SRob Clark } 91c8afe684SRob Clark 92e00012b2SArchit Taneja static int hdmi_get_phy(struct hdmi *hdmi) 93e00012b2SArchit Taneja { 94e00012b2SArchit Taneja struct platform_device *pdev = hdmi->pdev; 95e00012b2SArchit Taneja struct platform_device *phy_pdev; 96e00012b2SArchit Taneja struct device_node *phy_node; 97e00012b2SArchit Taneja 98e00012b2SArchit Taneja phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0); 99e00012b2SArchit Taneja if (!phy_node) { 100e00012b2SArchit Taneja dev_err(&pdev->dev, "cannot find phy device\n"); 101e00012b2SArchit Taneja return -ENXIO; 102e00012b2SArchit Taneja } 103e00012b2SArchit Taneja 104e00012b2SArchit Taneja phy_pdev = of_find_device_by_node(phy_node); 105e00012b2SArchit Taneja if (phy_pdev) 106e00012b2SArchit Taneja hdmi->phy = platform_get_drvdata(phy_pdev); 107e00012b2SArchit Taneja 108e00012b2SArchit Taneja of_node_put(phy_node); 109e00012b2SArchit Taneja 110e00012b2SArchit Taneja if (!phy_pdev || !hdmi->phy) { 111e00012b2SArchit Taneja dev_err(&pdev->dev, "phy driver is not ready\n"); 112e00012b2SArchit Taneja return -EPROBE_DEFER; 113e00012b2SArchit Taneja } 114e00012b2SArchit Taneja 115e00012b2SArchit Taneja hdmi->phy_dev = get_device(&phy_pdev->dev); 116e00012b2SArchit Taneja 117e00012b2SArchit Taneja return 0; 118e00012b2SArchit Taneja } 119e00012b2SArchit Taneja 120067fef37SRob Clark /* construct hdmi at bind/probe time, grab all the resources. If 121067fef37SRob Clark * we are to EPROBE_DEFER we want to do it here, rather than later 122067fef37SRob Clark * at modeset_init() time 123067fef37SRob Clark */ 124067fef37SRob Clark static struct hdmi *hdmi_init(struct platform_device *pdev) 125c8afe684SRob Clark { 126067fef37SRob Clark struct hdmi_platform_config *config = pdev->dev.platform_data; 127a3376e3eSRob Clark struct hdmi *hdmi = NULL; 128c6a57a50Sjilai wang struct resource *res; 129dada25bdSRob Clark int i, ret; 130c8afe684SRob Clark 131067fef37SRob Clark hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); 132a3376e3eSRob Clark if (!hdmi) { 133a3376e3eSRob Clark ret = -ENOMEM; 134a3376e3eSRob Clark goto fail; 135a3376e3eSRob Clark } 136a3376e3eSRob Clark 137c8afe684SRob Clark hdmi->pdev = pdev; 138dada25bdSRob Clark hdmi->config = config; 139c6a57a50Sjilai wang spin_lock_init(&hdmi->reg_lock); 140c0c0d9eeSRob Clark 141dada25bdSRob Clark hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); 142c8afe684SRob Clark if (IS_ERR(hdmi->mmio)) { 143c8afe684SRob Clark ret = PTR_ERR(hdmi->mmio); 144c8afe684SRob Clark goto fail; 145c8afe684SRob Clark } 146c8afe684SRob Clark 147c6a57a50Sjilai wang /* HDCP needs physical address of hdmi register */ 148c6a57a50Sjilai wang res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 149c6a57a50Sjilai wang config->mmio_name); 150c6a57a50Sjilai wang hdmi->mmio_phy_addr = res->start; 151c6a57a50Sjilai wang 152c6a57a50Sjilai wang hdmi->qfprom_mmio = msm_ioremap(pdev, 153c6a57a50Sjilai wang config->qfprom_mmio_name, "HDMI_QFPROM"); 154c6a57a50Sjilai wang if (IS_ERR(hdmi->qfprom_mmio)) { 155c6a57a50Sjilai wang dev_info(&pdev->dev, "can't find qfprom resource\n"); 156c6a57a50Sjilai wang hdmi->qfprom_mmio = NULL; 157c6a57a50Sjilai wang } 158c6a57a50Sjilai wang 159447fa529SStephane Viau hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) * 160447fa529SStephane Viau config->hpd_reg_cnt, GFP_KERNEL); 161447fa529SStephane Viau if (!hdmi->hpd_regs) { 162447fa529SStephane Viau ret = -ENOMEM; 163447fa529SStephane Viau goto fail; 164447fa529SStephane Viau } 165dada25bdSRob Clark for (i = 0; i < config->hpd_reg_cnt; i++) { 166dada25bdSRob Clark struct regulator *reg; 167dada25bdSRob Clark 1683e87599bSRob Clark reg = devm_regulator_get(&pdev->dev, 16941e69778SRob Clark config->hpd_reg_names[i]); 170dada25bdSRob Clark if (IS_ERR(reg)) { 171dada25bdSRob Clark ret = PTR_ERR(reg); 172067fef37SRob Clark dev_err(&pdev->dev, "failed to get hpd regulator: %s (%d)\n", 173dada25bdSRob Clark config->hpd_reg_names[i], ret); 174c8afe684SRob Clark goto fail; 175c8afe684SRob Clark } 176c8afe684SRob Clark 177dada25bdSRob Clark hdmi->hpd_regs[i] = reg; 178dada25bdSRob Clark } 179c8afe684SRob Clark 180447fa529SStephane Viau hdmi->pwr_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_regs[0]) * 181447fa529SStephane Viau config->pwr_reg_cnt, GFP_KERNEL); 182447fa529SStephane Viau if (!hdmi->pwr_regs) { 183447fa529SStephane Viau ret = -ENOMEM; 184447fa529SStephane Viau goto fail; 185447fa529SStephane Viau } 186dada25bdSRob Clark for (i = 0; i < config->pwr_reg_cnt; i++) { 187dada25bdSRob Clark struct regulator *reg; 188dada25bdSRob Clark 1893e87599bSRob Clark reg = devm_regulator_get(&pdev->dev, 19041e69778SRob Clark config->pwr_reg_names[i]); 191dada25bdSRob Clark if (IS_ERR(reg)) { 192dada25bdSRob Clark ret = PTR_ERR(reg); 193067fef37SRob Clark dev_err(&pdev->dev, "failed to get pwr regulator: %s (%d)\n", 194dada25bdSRob Clark config->pwr_reg_names[i], ret); 195c8afe684SRob Clark goto fail; 196c8afe684SRob Clark } 197c8afe684SRob Clark 198dada25bdSRob Clark hdmi->pwr_regs[i] = reg; 199dada25bdSRob Clark } 200dada25bdSRob Clark 201447fa529SStephane Viau hdmi->hpd_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_clks[0]) * 202447fa529SStephane Viau config->hpd_clk_cnt, GFP_KERNEL); 203447fa529SStephane Viau if (!hdmi->hpd_clks) { 204447fa529SStephane Viau ret = -ENOMEM; 205447fa529SStephane Viau goto fail; 206447fa529SStephane Viau } 207dada25bdSRob Clark for (i = 0; i < config->hpd_clk_cnt; i++) { 208dada25bdSRob Clark struct clk *clk; 209dada25bdSRob Clark 210dada25bdSRob Clark clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]); 211dada25bdSRob Clark if (IS_ERR(clk)) { 212dada25bdSRob Clark ret = PTR_ERR(clk); 213067fef37SRob Clark dev_err(&pdev->dev, "failed to get hpd clk: %s (%d)\n", 214dada25bdSRob Clark config->hpd_clk_names[i], ret); 215c8afe684SRob Clark goto fail; 216c8afe684SRob Clark } 217c8afe684SRob Clark 218dada25bdSRob Clark hdmi->hpd_clks[i] = clk; 219dada25bdSRob Clark } 220dada25bdSRob Clark 221447fa529SStephane Viau hdmi->pwr_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_clks[0]) * 222447fa529SStephane Viau config->pwr_clk_cnt, GFP_KERNEL); 223447fa529SStephane Viau if (!hdmi->pwr_clks) { 224447fa529SStephane Viau ret = -ENOMEM; 225447fa529SStephane Viau goto fail; 226447fa529SStephane Viau } 227dada25bdSRob Clark for (i = 0; i < config->pwr_clk_cnt; i++) { 228dada25bdSRob Clark struct clk *clk; 229dada25bdSRob Clark 230dada25bdSRob Clark clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]); 231dada25bdSRob Clark if (IS_ERR(clk)) { 232dada25bdSRob Clark ret = PTR_ERR(clk); 233067fef37SRob Clark dev_err(&pdev->dev, "failed to get pwr clk: %s (%d)\n", 234dada25bdSRob Clark config->pwr_clk_names[i], ret); 235c8afe684SRob Clark goto fail; 236c8afe684SRob Clark } 237c8afe684SRob Clark 238dada25bdSRob Clark hdmi->pwr_clks[i] = clk; 239dada25bdSRob Clark } 240dada25bdSRob Clark 241c6a57a50Sjilai wang hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0); 242c6a57a50Sjilai wang 243c8afe684SRob Clark hdmi->i2c = hdmi_i2c_init(hdmi); 244c8afe684SRob Clark if (IS_ERR(hdmi->i2c)) { 245c8afe684SRob Clark ret = PTR_ERR(hdmi->i2c); 246067fef37SRob Clark dev_err(&pdev->dev, "failed to get i2c: %d\n", ret); 247c8afe684SRob Clark hdmi->i2c = NULL; 248c8afe684SRob Clark goto fail; 249c8afe684SRob Clark } 250c8afe684SRob Clark 251e00012b2SArchit Taneja ret = hdmi_get_phy(hdmi); 252e00012b2SArchit Taneja if (ret) { 253e00012b2SArchit Taneja dev_err(&pdev->dev, "failed to get phy\n"); 254e00012b2SArchit Taneja goto fail; 255e00012b2SArchit Taneja } 256e00012b2SArchit Taneja 257c6a57a50Sjilai wang hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi); 258c6a57a50Sjilai wang if (IS_ERR(hdmi->hdcp_ctrl)) { 259c6a57a50Sjilai wang dev_warn(&pdev->dev, "failed to init hdcp: disabled\n"); 260c6a57a50Sjilai wang hdmi->hdcp_ctrl = NULL; 261c6a57a50Sjilai wang } 262c6a57a50Sjilai wang 263067fef37SRob Clark return hdmi; 264067fef37SRob Clark 265067fef37SRob Clark fail: 266067fef37SRob Clark if (hdmi) 267d1a717bdSRob Clark hdmi_destroy(hdmi); 268067fef37SRob Clark 269067fef37SRob Clark return ERR_PTR(ret); 270067fef37SRob Clark } 271067fef37SRob Clark 272067fef37SRob Clark /* Second part of initialization, the drm/kms level modeset_init, 273067fef37SRob Clark * constructs/initializes mode objects, etc, is called from master 274067fef37SRob Clark * driver (not hdmi sub-device's probe/bind!) 275067fef37SRob Clark * 276067fef37SRob Clark * Any resource (regulator/clk/etc) which could be missing at boot 277067fef37SRob Clark * should be handled in hdmi_init() so that failure happens from 278067fef37SRob Clark * hdmi sub-device's probe. 279067fef37SRob Clark */ 280067fef37SRob Clark int hdmi_modeset_init(struct hdmi *hdmi, 281067fef37SRob Clark struct drm_device *dev, struct drm_encoder *encoder) 282067fef37SRob Clark { 283067fef37SRob Clark struct msm_drm_private *priv = dev->dev_private; 284067fef37SRob Clark struct platform_device *pdev = hdmi->pdev; 285067fef37SRob Clark int ret; 286067fef37SRob Clark 287067fef37SRob Clark hdmi->dev = dev; 288067fef37SRob Clark hdmi->encoder = encoder; 289067fef37SRob Clark 290067fef37SRob Clark hdmi_audio_infoframe_init(&hdmi->audio.infoframe); 291067fef37SRob Clark 292a3376e3eSRob Clark hdmi->bridge = hdmi_bridge_init(hdmi); 293a3376e3eSRob Clark if (IS_ERR(hdmi->bridge)) { 294a3376e3eSRob Clark ret = PTR_ERR(hdmi->bridge); 295a3376e3eSRob Clark dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret); 296a3376e3eSRob Clark hdmi->bridge = NULL; 297a3376e3eSRob Clark goto fail; 298a3376e3eSRob Clark } 299a3376e3eSRob Clark 300a3376e3eSRob Clark hdmi->connector = hdmi_connector_init(hdmi); 301a3376e3eSRob Clark if (IS_ERR(hdmi->connector)) { 302a3376e3eSRob Clark ret = PTR_ERR(hdmi->connector); 303a3376e3eSRob Clark dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret); 304a3376e3eSRob Clark hdmi->connector = NULL; 305a3376e3eSRob Clark goto fail; 306a3376e3eSRob Clark } 307a3376e3eSRob Clark 308f6a8eacaSRob Clark hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 309c8afe684SRob Clark if (hdmi->irq < 0) { 310c8afe684SRob Clark ret = hdmi->irq; 311c8afe684SRob Clark dev_err(dev->dev, "failed to get irq: %d\n", ret); 312c8afe684SRob Clark goto fail; 313c8afe684SRob Clark } 314c8afe684SRob Clark 315f6a8eacaSRob Clark ret = devm_request_irq(&pdev->dev, hdmi->irq, 316f6a8eacaSRob Clark hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 317c8afe684SRob Clark "hdmi_isr", hdmi); 318c8afe684SRob Clark if (ret < 0) { 319c8afe684SRob Clark dev_err(dev->dev, "failed to request IRQ%u: %d\n", 320c8afe684SRob Clark hdmi->irq, ret); 321c8afe684SRob Clark goto fail; 322c8afe684SRob Clark } 323c8afe684SRob Clark 324a3376e3eSRob Clark encoder->bridge = hdmi->bridge; 325a3376e3eSRob Clark 326a3376e3eSRob Clark priv->bridges[priv->num_bridges++] = hdmi->bridge; 327a3376e3eSRob Clark priv->connectors[priv->num_connectors++] = hdmi->connector; 328a3376e3eSRob Clark 329c0c0d9eeSRob Clark platform_set_drvdata(pdev, hdmi); 330c0c0d9eeSRob Clark 331067fef37SRob Clark return 0; 332c8afe684SRob Clark 333c8afe684SRob Clark fail: 3343d3f8b1fSAjay Kumar /* bridge is normally destroyed by drm: */ 335067fef37SRob Clark if (hdmi->bridge) { 3363d3f8b1fSAjay Kumar hdmi_bridge_destroy(hdmi->bridge); 337067fef37SRob Clark hdmi->bridge = NULL; 338067fef37SRob Clark } 339067fef37SRob Clark if (hdmi->connector) { 340a3376e3eSRob Clark hdmi->connector->funcs->destroy(hdmi->connector); 341067fef37SRob Clark hdmi->connector = NULL; 342a3376e3eSRob Clark } 343c8afe684SRob Clark 344067fef37SRob Clark return ret; 345c8afe684SRob Clark } 346c8afe684SRob Clark 347c8afe684SRob Clark /* 348c8afe684SRob Clark * The hdmi device: 349c8afe684SRob Clark */ 350c8afe684SRob Clark 3515eba5d87SStephane Viau #define HDMI_CFG(item, entry) \ 3525eba5d87SStephane Viau .item ## _names = item ##_names_ ## entry, \ 3535eba5d87SStephane Viau .item ## _cnt = ARRAY_SIZE(item ## _names_ ## entry) 3545eba5d87SStephane Viau 3550afbe59eSStephane Viau static const char *pwr_reg_names_none[] = {}; 3560afbe59eSStephane Viau static const char *hpd_reg_names_none[] = {}; 3570afbe59eSStephane Viau 358ba3d7bf3SArchit Taneja static struct hdmi_platform_config hdmi_tx_8660_config; 3595eba5d87SStephane Viau 3605eba5d87SStephane Viau static const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"}; 3615eba5d87SStephane Viau static const char *hpd_clk_names_8960[] = {"core_clk", "master_iface_clk", "slave_iface_clk"}; 3625eba5d87SStephane Viau 3635eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8960_config = { 3645eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8960), 3655eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8960), 3665eba5d87SStephane Viau }; 3675eba5d87SStephane Viau 3685eba5d87SStephane Viau static const char *pwr_reg_names_8x74[] = {"core-vdda", "core-vcc"}; 3695eba5d87SStephane Viau static const char *hpd_reg_names_8x74[] = {"hpd-gdsc", "hpd-5v"}; 3705eba5d87SStephane Viau static const char *pwr_clk_names_8x74[] = {"extp_clk", "alt_iface_clk"}; 3715eba5d87SStephane Viau static const char *hpd_clk_names_8x74[] = {"iface_clk", "core_clk", "mdp_core_clk"}; 3725eba5d87SStephane Viau static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; 3735eba5d87SStephane Viau 3745cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8974_config = { 3755eba5d87SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3765eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8x74), 3775eba5d87SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3785eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3795eba5d87SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3805eba5d87SStephane Viau }; 3815eba5d87SStephane Viau 3825eba5d87SStephane Viau static const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"}; 3835eba5d87SStephane Viau 3845eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8084_config = { 3855eba5d87SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3865eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8084), 3875eba5d87SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3885eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3895eba5d87SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3905eba5d87SStephane Viau }; 3915eba5d87SStephane Viau 3925cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8994_config = { 3933a84f846SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3940afbe59eSStephane Viau HDMI_CFG(hpd_reg, none), 3950afbe59eSStephane Viau HDMI_CFG(pwr_clk, 8x74), 3960afbe59eSStephane Viau HDMI_CFG(hpd_clk, 8x74), 3970afbe59eSStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3980afbe59eSStephane Viau }; 3990afbe59eSStephane Viau 4000afbe59eSStephane Viau static struct hdmi_platform_config hdmi_tx_8996_config = { 4010afbe59eSStephane Viau HDMI_CFG(pwr_reg, none), 4020afbe59eSStephane Viau HDMI_CFG(hpd_reg, none), 4033a84f846SStephane Viau HDMI_CFG(pwr_clk, 8x74), 4043a84f846SStephane Viau HDMI_CFG(hpd_clk, 8x74), 4053a84f846SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 4063a84f846SStephane Viau }; 4073a84f846SStephane Viau 408dc50f782SArchit Taneja static const struct { 409dc50f782SArchit Taneja const char *name; 410dc50f782SArchit Taneja const bool output; 411dc50f782SArchit Taneja const int value; 412dc50f782SArchit Taneja const char *label; 413dc50f782SArchit Taneja } hdmi_gpio_pdata[] = { 414dc50f782SArchit Taneja { "qcom,hdmi-tx-ddc-clk", true, 1, "HDMI_DDC_CLK" }, 415dc50f782SArchit Taneja { "qcom,hdmi-tx-ddc-data", true, 1, "HDMI_DDC_DATA" }, 416dc50f782SArchit Taneja { "qcom,hdmi-tx-hpd", false, 1, "HDMI_HPD" }, 417dc50f782SArchit Taneja { "qcom,hdmi-tx-mux-en", true, 1, "HDMI_MUX_EN" }, 418dc50f782SArchit Taneja { "qcom,hdmi-tx-mux-sel", true, 0, "HDMI_MUX_SEL" }, 419dc50f782SArchit Taneja { "qcom,hdmi-tx-mux-lpm", true, 1, "HDMI_MUX_LPM" }, 420dc50f782SArchit Taneja }; 421dc50f782SArchit Taneja 422dc50f782SArchit Taneja static int get_gpio(struct device_node *of_node, const char *name) 423dada25bdSRob Clark { 424dada25bdSRob Clark int gpio = of_get_named_gpio(of_node, name, 0); 425dada25bdSRob Clark if (gpio < 0) { 42641e69778SRob Clark char name2[32]; 42741e69778SRob Clark snprintf(name2, sizeof(name2), "%s-gpio", name); 42841e69778SRob Clark gpio = of_get_named_gpio(of_node, name2, 0); 42941e69778SRob Clark if (gpio < 0) { 4303a84f846SStephane Viau DBG("failed to get gpio: %s (%d)", name, gpio); 431dada25bdSRob Clark gpio = -1; 432dada25bdSRob Clark } 43341e69778SRob Clark } 434dada25bdSRob Clark return gpio; 435dada25bdSRob Clark } 436fc886107SMark Charlebois 437fc886107SMark Charlebois static int hdmi_bind(struct device *dev, struct device *master, void *data) 438fc886107SMark Charlebois { 439d1a717bdSRob Clark struct drm_device *drm = dev_get_drvdata(master); 440d1a717bdSRob Clark struct msm_drm_private *priv = drm->dev_private; 4415eba5d87SStephane Viau static struct hdmi_platform_config *hdmi_cfg; 442067fef37SRob Clark struct hdmi *hdmi; 443fc886107SMark Charlebois struct device_node *of_node = dev->of_node; 444dc50f782SArchit Taneja int i; 445dada25bdSRob Clark 4461fd6a441SArchit Taneja hdmi_cfg = (struct hdmi_platform_config *) 4471fd6a441SArchit Taneja of_device_get_match_data(dev); 4481fd6a441SArchit Taneja if (!hdmi_cfg) { 4491fd6a441SArchit Taneja dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name); 4505eba5d87SStephane Viau return -ENXIO; 45141e69778SRob Clark } 45241e69778SRob Clark 4535eba5d87SStephane Viau hdmi_cfg->mmio_name = "core_physical"; 454c6a57a50Sjilai wang hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; 455dc50f782SArchit Taneja 456dc50f782SArchit Taneja for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) { 457dc50f782SArchit Taneja hdmi_cfg->gpios[i].num = get_gpio(of_node, 458dc50f782SArchit Taneja hdmi_gpio_pdata[i].name); 459dc50f782SArchit Taneja hdmi_cfg->gpios[i].output = hdmi_gpio_pdata[i].output; 460dc50f782SArchit Taneja hdmi_cfg->gpios[i].value = hdmi_gpio_pdata[i].value; 461dc50f782SArchit Taneja hdmi_cfg->gpios[i].label = hdmi_gpio_pdata[i].label; 462dc50f782SArchit Taneja } 463dada25bdSRob Clark 4645eba5d87SStephane Viau dev->platform_data = hdmi_cfg; 4655eba5d87SStephane Viau 466067fef37SRob Clark hdmi = hdmi_init(to_platform_device(dev)); 467067fef37SRob Clark if (IS_ERR(hdmi)) 468067fef37SRob Clark return PTR_ERR(hdmi); 469d1a717bdSRob Clark priv->hdmi = hdmi; 4705eba5d87SStephane Viau 471c8afe684SRob Clark return 0; 472c8afe684SRob Clark } 473c8afe684SRob Clark 474060530f1SRob Clark static void hdmi_unbind(struct device *dev, struct device *master, 475060530f1SRob Clark void *data) 476060530f1SRob Clark { 477d1a717bdSRob Clark struct drm_device *drm = dev_get_drvdata(master); 478d1a717bdSRob Clark struct msm_drm_private *priv = drm->dev_private; 479d1a717bdSRob Clark if (priv->hdmi) { 480d1a717bdSRob Clark hdmi_destroy(priv->hdmi); 481d1a717bdSRob Clark priv->hdmi = NULL; 482d1a717bdSRob Clark } 483060530f1SRob Clark } 484060530f1SRob Clark 485060530f1SRob Clark static const struct component_ops hdmi_ops = { 486060530f1SRob Clark .bind = hdmi_bind, 487060530f1SRob Clark .unbind = hdmi_unbind, 488060530f1SRob Clark }; 489060530f1SRob Clark 490060530f1SRob Clark static int hdmi_dev_probe(struct platform_device *pdev) 491060530f1SRob Clark { 492060530f1SRob Clark return component_add(&pdev->dev, &hdmi_ops); 493060530f1SRob Clark } 494060530f1SRob Clark 495c8afe684SRob Clark static int hdmi_dev_remove(struct platform_device *pdev) 496c8afe684SRob Clark { 497060530f1SRob Clark component_del(&pdev->dev, &hdmi_ops); 498c8afe684SRob Clark return 0; 499c8afe684SRob Clark } 500c8afe684SRob Clark 5011fd6a441SArchit Taneja static const struct of_device_id dt_match[] = { 5021fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config }, 5031fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config }, 5041fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config }, 5051fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config }, 5061fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config }, 5071fd6a441SArchit Taneja { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config }, 5081fd6a441SArchit Taneja {} 5091fd6a441SArchit Taneja }; 5101fd6a441SArchit Taneja 511c8afe684SRob Clark static struct platform_driver hdmi_driver = { 512c8afe684SRob Clark .probe = hdmi_dev_probe, 513c8afe684SRob Clark .remove = hdmi_dev_remove, 514dada25bdSRob Clark .driver = { 515dada25bdSRob Clark .name = "hdmi_msm", 516dada25bdSRob Clark .of_match_table = dt_match, 517dada25bdSRob Clark }, 518c8afe684SRob Clark }; 519c8afe684SRob Clark 520c8afe684SRob Clark void __init hdmi_register(void) 521c8afe684SRob Clark { 52215b4a452SArchit Taneja hdmi_phy_driver_register(); 523c8afe684SRob Clark platform_driver_register(&hdmi_driver); 524c8afe684SRob Clark } 525c8afe684SRob Clark 526c8afe684SRob Clark void __exit hdmi_unregister(void) 527c8afe684SRob Clark { 528c8afe684SRob Clark platform_driver_unregister(&hdmi_driver); 52915b4a452SArchit Taneja hdmi_phy_driver_unregister(); 530c8afe684SRob Clark } 531