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> 20c8afe684SRob Clark #include "hdmi.h" 21c8afe684SRob Clark 22c8afe684SRob Clark void hdmi_set_mode(struct hdmi *hdmi, bool power_on) 23c8afe684SRob Clark { 24c8afe684SRob Clark uint32_t ctrl = 0; 25c6a57a50Sjilai wang unsigned long flags; 26c8afe684SRob Clark 27c6a57a50Sjilai wang spin_lock_irqsave(&hdmi->reg_lock, flags); 28c8afe684SRob Clark if (power_on) { 29c8afe684SRob Clark ctrl |= HDMI_CTRL_ENABLE; 30c8afe684SRob Clark if (!hdmi->hdmi_mode) { 31c8afe684SRob Clark ctrl |= HDMI_CTRL_HDMI; 32c8afe684SRob Clark hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 33c8afe684SRob Clark ctrl &= ~HDMI_CTRL_HDMI; 34c8afe684SRob Clark } else { 35c8afe684SRob Clark ctrl |= HDMI_CTRL_HDMI; 36c8afe684SRob Clark } 37c8afe684SRob Clark } else { 38c8afe684SRob Clark ctrl = HDMI_CTRL_HDMI; 39c8afe684SRob Clark } 40c8afe684SRob Clark 41c8afe684SRob Clark hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); 42c6a57a50Sjilai wang spin_unlock_irqrestore(&hdmi->reg_lock, flags); 43c8afe684SRob Clark DBG("HDMI Core: %s, HDMI_CTRL=0x%08x", 44c8afe684SRob Clark power_on ? "Enable" : "Disable", ctrl); 45c8afe684SRob Clark } 46c8afe684SRob Clark 47f6a8eacaSRob Clark static irqreturn_t hdmi_irq(int irq, void *dev_id) 48c8afe684SRob Clark { 49c8afe684SRob Clark struct hdmi *hdmi = dev_id; 50c8afe684SRob Clark 51c8afe684SRob Clark /* Process HPD: */ 52c8afe684SRob Clark hdmi_connector_irq(hdmi->connector); 53c8afe684SRob Clark 54c8afe684SRob Clark /* Process DDC: */ 55c8afe684SRob Clark hdmi_i2c_irq(hdmi->i2c); 56c8afe684SRob Clark 57c6a57a50Sjilai wang /* Process HDCP: */ 58c6a57a50Sjilai wang if (hdmi->hdcp_ctrl) 59c6a57a50Sjilai wang hdmi_hdcp_irq(hdmi->hdcp_ctrl); 60c6a57a50Sjilai wang 61c8afe684SRob Clark /* TODO audio.. */ 62c8afe684SRob Clark 63c8afe684SRob Clark return IRQ_HANDLED; 64c8afe684SRob Clark } 65c8afe684SRob Clark 66d1a717bdSRob Clark static void hdmi_destroy(struct hdmi *hdmi) 67c8afe684SRob Clark { 68c8afe684SRob Clark struct hdmi_phy *phy = hdmi->phy; 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 if (phy) 80c8afe684SRob Clark phy->funcs->destroy(phy); 81c8afe684SRob Clark 82c8afe684SRob Clark if (hdmi->i2c) 83c8afe684SRob Clark hdmi_i2c_destroy(hdmi->i2c); 84c8afe684SRob Clark 85c0c0d9eeSRob Clark platform_set_drvdata(hdmi->pdev, NULL); 86c8afe684SRob Clark } 87c8afe684SRob Clark 88067fef37SRob Clark /* construct hdmi at bind/probe time, grab all the resources. If 89067fef37SRob Clark * we are to EPROBE_DEFER we want to do it here, rather than later 90067fef37SRob Clark * at modeset_init() time 91067fef37SRob Clark */ 92067fef37SRob Clark static struct hdmi *hdmi_init(struct platform_device *pdev) 93c8afe684SRob Clark { 94067fef37SRob Clark struct hdmi_platform_config *config = pdev->dev.platform_data; 95a3376e3eSRob Clark struct hdmi *hdmi = NULL; 96c6a57a50Sjilai wang struct resource *res; 97dada25bdSRob Clark int i, ret; 98c8afe684SRob Clark 99067fef37SRob Clark hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); 100a3376e3eSRob Clark if (!hdmi) { 101a3376e3eSRob Clark ret = -ENOMEM; 102a3376e3eSRob Clark goto fail; 103a3376e3eSRob Clark } 104a3376e3eSRob Clark 105c8afe684SRob Clark hdmi->pdev = pdev; 106dada25bdSRob Clark hdmi->config = config; 107c6a57a50Sjilai wang spin_lock_init(&hdmi->reg_lock); 108c0c0d9eeSRob Clark 109c8afe684SRob Clark /* not sure about which phy maps to which msm.. probably I miss some */ 1103a84f846SStephane Viau if (config->phy_init) { 111c8afe684SRob Clark hdmi->phy = config->phy_init(hdmi); 112c8afe684SRob Clark 113c8afe684SRob Clark if (IS_ERR(hdmi->phy)) { 114c8afe684SRob Clark ret = PTR_ERR(hdmi->phy); 115067fef37SRob Clark dev_err(&pdev->dev, "failed to load phy: %d\n", ret); 116c8afe684SRob Clark hdmi->phy = NULL; 117c8afe684SRob Clark goto fail; 118c8afe684SRob Clark } 1193a84f846SStephane Viau } 120c8afe684SRob Clark 121dada25bdSRob Clark hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); 122c8afe684SRob Clark if (IS_ERR(hdmi->mmio)) { 123c8afe684SRob Clark ret = PTR_ERR(hdmi->mmio); 124c8afe684SRob Clark goto fail; 125c8afe684SRob Clark } 126c8afe684SRob Clark 127c6a57a50Sjilai wang /* HDCP needs physical address of hdmi register */ 128c6a57a50Sjilai wang res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 129c6a57a50Sjilai wang config->mmio_name); 130c6a57a50Sjilai wang hdmi->mmio_phy_addr = res->start; 131c6a57a50Sjilai wang 132c6a57a50Sjilai wang hdmi->qfprom_mmio = msm_ioremap(pdev, 133c6a57a50Sjilai wang config->qfprom_mmio_name, "HDMI_QFPROM"); 134c6a57a50Sjilai wang if (IS_ERR(hdmi->qfprom_mmio)) { 135c6a57a50Sjilai wang dev_info(&pdev->dev, "can't find qfprom resource\n"); 136c6a57a50Sjilai wang hdmi->qfprom_mmio = NULL; 137c6a57a50Sjilai wang } 138c6a57a50Sjilai wang 139447fa529SStephane Viau hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) * 140447fa529SStephane Viau config->hpd_reg_cnt, GFP_KERNEL); 141447fa529SStephane Viau if (!hdmi->hpd_regs) { 142447fa529SStephane Viau ret = -ENOMEM; 143447fa529SStephane Viau goto fail; 144447fa529SStephane Viau } 145dada25bdSRob Clark for (i = 0; i < config->hpd_reg_cnt; i++) { 146dada25bdSRob Clark struct regulator *reg; 147dada25bdSRob Clark 1483e87599bSRob Clark reg = devm_regulator_get(&pdev->dev, 14941e69778SRob Clark config->hpd_reg_names[i]); 150dada25bdSRob Clark if (IS_ERR(reg)) { 151dada25bdSRob Clark ret = PTR_ERR(reg); 152067fef37SRob Clark dev_err(&pdev->dev, "failed to get hpd regulator: %s (%d)\n", 153dada25bdSRob Clark config->hpd_reg_names[i], ret); 154c8afe684SRob Clark goto fail; 155c8afe684SRob Clark } 156c8afe684SRob Clark 157dada25bdSRob Clark hdmi->hpd_regs[i] = reg; 158dada25bdSRob Clark } 159c8afe684SRob Clark 160447fa529SStephane Viau hdmi->pwr_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_regs[0]) * 161447fa529SStephane Viau config->pwr_reg_cnt, GFP_KERNEL); 162447fa529SStephane Viau if (!hdmi->pwr_regs) { 163447fa529SStephane Viau ret = -ENOMEM; 164447fa529SStephane Viau goto fail; 165447fa529SStephane Viau } 166dada25bdSRob Clark for (i = 0; i < config->pwr_reg_cnt; i++) { 167dada25bdSRob Clark struct regulator *reg; 168dada25bdSRob Clark 1693e87599bSRob Clark reg = devm_regulator_get(&pdev->dev, 17041e69778SRob Clark config->pwr_reg_names[i]); 171dada25bdSRob Clark if (IS_ERR(reg)) { 172dada25bdSRob Clark ret = PTR_ERR(reg); 173067fef37SRob Clark dev_err(&pdev->dev, "failed to get pwr regulator: %s (%d)\n", 174dada25bdSRob Clark config->pwr_reg_names[i], ret); 175c8afe684SRob Clark goto fail; 176c8afe684SRob Clark } 177c8afe684SRob Clark 178dada25bdSRob Clark hdmi->pwr_regs[i] = reg; 179dada25bdSRob Clark } 180dada25bdSRob Clark 181447fa529SStephane Viau hdmi->hpd_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_clks[0]) * 182447fa529SStephane Viau config->hpd_clk_cnt, GFP_KERNEL); 183447fa529SStephane Viau if (!hdmi->hpd_clks) { 184447fa529SStephane Viau ret = -ENOMEM; 185447fa529SStephane Viau goto fail; 186447fa529SStephane Viau } 187dada25bdSRob Clark for (i = 0; i < config->hpd_clk_cnt; i++) { 188dada25bdSRob Clark struct clk *clk; 189dada25bdSRob Clark 190dada25bdSRob Clark clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]); 191dada25bdSRob Clark if (IS_ERR(clk)) { 192dada25bdSRob Clark ret = PTR_ERR(clk); 193067fef37SRob Clark dev_err(&pdev->dev, "failed to get hpd clk: %s (%d)\n", 194dada25bdSRob Clark config->hpd_clk_names[i], ret); 195c8afe684SRob Clark goto fail; 196c8afe684SRob Clark } 197c8afe684SRob Clark 198dada25bdSRob Clark hdmi->hpd_clks[i] = clk; 199dada25bdSRob Clark } 200dada25bdSRob Clark 201447fa529SStephane Viau hdmi->pwr_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_clks[0]) * 202447fa529SStephane Viau config->pwr_clk_cnt, GFP_KERNEL); 203447fa529SStephane Viau if (!hdmi->pwr_clks) { 204447fa529SStephane Viau ret = -ENOMEM; 205447fa529SStephane Viau goto fail; 206447fa529SStephane Viau } 207dada25bdSRob Clark for (i = 0; i < config->pwr_clk_cnt; i++) { 208dada25bdSRob Clark struct clk *clk; 209dada25bdSRob Clark 210dada25bdSRob Clark clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]); 211dada25bdSRob Clark if (IS_ERR(clk)) { 212dada25bdSRob Clark ret = PTR_ERR(clk); 213067fef37SRob Clark dev_err(&pdev->dev, "failed to get pwr clk: %s (%d)\n", 214dada25bdSRob Clark config->pwr_clk_names[i], ret); 215c8afe684SRob Clark goto fail; 216c8afe684SRob Clark } 217c8afe684SRob Clark 218dada25bdSRob Clark hdmi->pwr_clks[i] = clk; 219dada25bdSRob Clark } 220dada25bdSRob Clark 221c6a57a50Sjilai wang hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0); 222c6a57a50Sjilai wang 223c8afe684SRob Clark hdmi->i2c = hdmi_i2c_init(hdmi); 224c8afe684SRob Clark if (IS_ERR(hdmi->i2c)) { 225c8afe684SRob Clark ret = PTR_ERR(hdmi->i2c); 226067fef37SRob Clark dev_err(&pdev->dev, "failed to get i2c: %d\n", ret); 227c8afe684SRob Clark hdmi->i2c = NULL; 228c8afe684SRob Clark goto fail; 229c8afe684SRob Clark } 230c8afe684SRob Clark 231c6a57a50Sjilai wang hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi); 232c6a57a50Sjilai wang if (IS_ERR(hdmi->hdcp_ctrl)) { 233c6a57a50Sjilai wang dev_warn(&pdev->dev, "failed to init hdcp: disabled\n"); 234c6a57a50Sjilai wang hdmi->hdcp_ctrl = NULL; 235c6a57a50Sjilai wang } 236c6a57a50Sjilai wang 237067fef37SRob Clark return hdmi; 238067fef37SRob Clark 239067fef37SRob Clark fail: 240067fef37SRob Clark if (hdmi) 241d1a717bdSRob Clark hdmi_destroy(hdmi); 242067fef37SRob Clark 243067fef37SRob Clark return ERR_PTR(ret); 244067fef37SRob Clark } 245067fef37SRob Clark 246067fef37SRob Clark /* Second part of initialization, the drm/kms level modeset_init, 247067fef37SRob Clark * constructs/initializes mode objects, etc, is called from master 248067fef37SRob Clark * driver (not hdmi sub-device's probe/bind!) 249067fef37SRob Clark * 250067fef37SRob Clark * Any resource (regulator/clk/etc) which could be missing at boot 251067fef37SRob Clark * should be handled in hdmi_init() so that failure happens from 252067fef37SRob Clark * hdmi sub-device's probe. 253067fef37SRob Clark */ 254067fef37SRob Clark int hdmi_modeset_init(struct hdmi *hdmi, 255067fef37SRob Clark struct drm_device *dev, struct drm_encoder *encoder) 256067fef37SRob Clark { 257067fef37SRob Clark struct msm_drm_private *priv = dev->dev_private; 258067fef37SRob Clark struct platform_device *pdev = hdmi->pdev; 259067fef37SRob Clark int ret; 260067fef37SRob Clark 261067fef37SRob Clark hdmi->dev = dev; 262067fef37SRob Clark hdmi->encoder = encoder; 263067fef37SRob Clark 264067fef37SRob Clark hdmi_audio_infoframe_init(&hdmi->audio.infoframe); 265067fef37SRob Clark 266a3376e3eSRob Clark hdmi->bridge = hdmi_bridge_init(hdmi); 267a3376e3eSRob Clark if (IS_ERR(hdmi->bridge)) { 268a3376e3eSRob Clark ret = PTR_ERR(hdmi->bridge); 269a3376e3eSRob Clark dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret); 270a3376e3eSRob Clark hdmi->bridge = NULL; 271a3376e3eSRob Clark goto fail; 272a3376e3eSRob Clark } 273a3376e3eSRob Clark 274a3376e3eSRob Clark hdmi->connector = hdmi_connector_init(hdmi); 275a3376e3eSRob Clark if (IS_ERR(hdmi->connector)) { 276a3376e3eSRob Clark ret = PTR_ERR(hdmi->connector); 277a3376e3eSRob Clark dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret); 278a3376e3eSRob Clark hdmi->connector = NULL; 279a3376e3eSRob Clark goto fail; 280a3376e3eSRob Clark } 281a3376e3eSRob Clark 282f6a8eacaSRob Clark hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 283c8afe684SRob Clark if (hdmi->irq < 0) { 284c8afe684SRob Clark ret = hdmi->irq; 285c8afe684SRob Clark dev_err(dev->dev, "failed to get irq: %d\n", ret); 286c8afe684SRob Clark goto fail; 287c8afe684SRob Clark } 288c8afe684SRob Clark 289f6a8eacaSRob Clark ret = devm_request_irq(&pdev->dev, hdmi->irq, 290f6a8eacaSRob Clark hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 291c8afe684SRob Clark "hdmi_isr", hdmi); 292c8afe684SRob Clark if (ret < 0) { 293c8afe684SRob Clark dev_err(dev->dev, "failed to request IRQ%u: %d\n", 294c8afe684SRob Clark hdmi->irq, ret); 295c8afe684SRob Clark goto fail; 296c8afe684SRob Clark } 297c8afe684SRob Clark 298a3376e3eSRob Clark encoder->bridge = hdmi->bridge; 299a3376e3eSRob Clark 300a3376e3eSRob Clark priv->bridges[priv->num_bridges++] = hdmi->bridge; 301a3376e3eSRob Clark priv->connectors[priv->num_connectors++] = hdmi->connector; 302a3376e3eSRob Clark 303c0c0d9eeSRob Clark platform_set_drvdata(pdev, hdmi); 304c0c0d9eeSRob Clark 305067fef37SRob Clark return 0; 306c8afe684SRob Clark 307c8afe684SRob Clark fail: 3083d3f8b1fSAjay Kumar /* bridge is normally destroyed by drm: */ 309067fef37SRob Clark if (hdmi->bridge) { 3103d3f8b1fSAjay Kumar hdmi_bridge_destroy(hdmi->bridge); 311067fef37SRob Clark hdmi->bridge = NULL; 312067fef37SRob Clark } 313067fef37SRob Clark if (hdmi->connector) { 314a3376e3eSRob Clark hdmi->connector->funcs->destroy(hdmi->connector); 315067fef37SRob Clark hdmi->connector = NULL; 316a3376e3eSRob Clark } 317c8afe684SRob Clark 318067fef37SRob Clark return ret; 319c8afe684SRob Clark } 320c8afe684SRob Clark 321c8afe684SRob Clark /* 322c8afe684SRob Clark * The hdmi device: 323c8afe684SRob Clark */ 324c8afe684SRob Clark 325dada25bdSRob Clark #include <linux/of_gpio.h> 326dada25bdSRob Clark 3275eba5d87SStephane Viau #define HDMI_CFG(item, entry) \ 3285eba5d87SStephane Viau .item ## _names = item ##_names_ ## entry, \ 3295eba5d87SStephane Viau .item ## _cnt = ARRAY_SIZE(item ## _names_ ## entry) 3305eba5d87SStephane Viau 3310afbe59eSStephane Viau static const char *pwr_reg_names_none[] = {}; 3320afbe59eSStephane Viau static const char *hpd_reg_names_none[] = {}; 3330afbe59eSStephane Viau 3345eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8660_config = { 3355eba5d87SStephane Viau .phy_init = hdmi_phy_8x60_init, 3365eba5d87SStephane Viau }; 3375eba5d87SStephane Viau 3385eba5d87SStephane Viau static const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"}; 3395eba5d87SStephane Viau static const char *hpd_clk_names_8960[] = {"core_clk", "master_iface_clk", "slave_iface_clk"}; 3405eba5d87SStephane Viau 3415eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8960_config = { 3425eba5d87SStephane Viau .phy_init = hdmi_phy_8960_init, 3435eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8960), 3445eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8960), 3455eba5d87SStephane Viau }; 3465eba5d87SStephane Viau 3475eba5d87SStephane Viau static const char *pwr_reg_names_8x74[] = {"core-vdda", "core-vcc"}; 3485eba5d87SStephane Viau static const char *hpd_reg_names_8x74[] = {"hpd-gdsc", "hpd-5v"}; 3495eba5d87SStephane Viau static const char *pwr_clk_names_8x74[] = {"extp_clk", "alt_iface_clk"}; 3505eba5d87SStephane Viau static const char *hpd_clk_names_8x74[] = {"iface_clk", "core_clk", "mdp_core_clk"}; 3515eba5d87SStephane Viau static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; 3525eba5d87SStephane Viau 3535cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8974_config = { 3545eba5d87SStephane Viau .phy_init = hdmi_phy_8x74_init, 3555eba5d87SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3565eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8x74), 3575eba5d87SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3585eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3595eba5d87SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3605eba5d87SStephane Viau }; 3615eba5d87SStephane Viau 3625eba5d87SStephane Viau static const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"}; 3635eba5d87SStephane Viau 3645eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8084_config = { 3655eba5d87SStephane Viau .phy_init = hdmi_phy_8x74_init, 3665eba5d87SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3675eba5d87SStephane Viau HDMI_CFG(hpd_reg, 8084), 3685eba5d87SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3695eba5d87SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3705eba5d87SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3715eba5d87SStephane Viau }; 3725eba5d87SStephane Viau 3735cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8994_config = { 3743a84f846SStephane Viau .phy_init = NULL, /* nothing to do for this HDMI PHY 20nm */ 3753a84f846SStephane Viau HDMI_CFG(pwr_reg, 8x74), 3760afbe59eSStephane Viau HDMI_CFG(hpd_reg, none), 3770afbe59eSStephane Viau HDMI_CFG(pwr_clk, 8x74), 3780afbe59eSStephane Viau HDMI_CFG(hpd_clk, 8x74), 3790afbe59eSStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3800afbe59eSStephane Viau }; 3810afbe59eSStephane Viau 3820afbe59eSStephane Viau static struct hdmi_platform_config hdmi_tx_8996_config = { 3830afbe59eSStephane Viau .phy_init = NULL, 3840afbe59eSStephane Viau HDMI_CFG(pwr_reg, none), 3850afbe59eSStephane Viau HDMI_CFG(hpd_reg, none), 3863a84f846SStephane Viau HDMI_CFG(pwr_clk, 8x74), 3873a84f846SStephane Viau HDMI_CFG(hpd_clk, 8x74), 3883a84f846SStephane Viau .hpd_freq = hpd_clk_freq_8x74, 3893a84f846SStephane Viau }; 3903a84f846SStephane Viau 3915eba5d87SStephane Viau static const struct of_device_id dt_match[] = { 3920afbe59eSStephane Viau { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config }, 3935cf3a455SRob Clark { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config }, 3945eba5d87SStephane Viau { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config }, 3955cf3a455SRob Clark { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config }, 3965eba5d87SStephane Viau { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config }, 3975eba5d87SStephane Viau { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config }, 3985eba5d87SStephane Viau {} 3995eba5d87SStephane Viau }; 4005eba5d87SStephane Viau 401c8afe684SRob Clark #ifdef CONFIG_OF 402fc886107SMark Charlebois static int get_gpio(struct device *dev, struct device_node *of_node, const char *name) 403dada25bdSRob Clark { 404dada25bdSRob Clark int gpio = of_get_named_gpio(of_node, name, 0); 405dada25bdSRob Clark if (gpio < 0) { 40641e69778SRob Clark char name2[32]; 40741e69778SRob Clark snprintf(name2, sizeof(name2), "%s-gpio", name); 40841e69778SRob Clark gpio = of_get_named_gpio(of_node, name2, 0); 40941e69778SRob Clark if (gpio < 0) { 4103a84f846SStephane Viau DBG("failed to get gpio: %s (%d)", name, gpio); 411dada25bdSRob Clark gpio = -1; 412dada25bdSRob Clark } 41341e69778SRob Clark } 414dada25bdSRob Clark return gpio; 415dada25bdSRob Clark } 416fc886107SMark Charlebois #endif 417fc886107SMark Charlebois 418fc886107SMark Charlebois static int hdmi_bind(struct device *dev, struct device *master, void *data) 419fc886107SMark Charlebois { 420d1a717bdSRob Clark struct drm_device *drm = dev_get_drvdata(master); 421d1a717bdSRob Clark struct msm_drm_private *priv = drm->dev_private; 4225eba5d87SStephane Viau static struct hdmi_platform_config *hdmi_cfg; 423067fef37SRob Clark struct hdmi *hdmi; 424fc886107SMark Charlebois #ifdef CONFIG_OF 425fc886107SMark Charlebois struct device_node *of_node = dev->of_node; 4265eba5d87SStephane Viau const struct of_device_id *match; 427dada25bdSRob Clark 4285eba5d87SStephane Viau match = of_match_node(dt_match, of_node); 4295eba5d87SStephane Viau if (match && match->data) { 4305eba5d87SStephane Viau hdmi_cfg = (struct hdmi_platform_config *)match->data; 4315eba5d87SStephane Viau DBG("hdmi phy: %s", match->compatible); 43241e69778SRob Clark } else { 43341e69778SRob Clark dev_err(dev, "unknown phy: %s\n", of_node->name); 4345eba5d87SStephane Viau return -ENXIO; 43541e69778SRob Clark } 43641e69778SRob Clark 4375eba5d87SStephane Viau hdmi_cfg->mmio_name = "core_physical"; 438c6a57a50Sjilai wang hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; 4395eba5d87SStephane Viau hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk"); 4405eba5d87SStephane Viau hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data"); 4415eba5d87SStephane Viau hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd"); 4425eba5d87SStephane Viau hdmi_cfg->mux_en_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-en"); 4435eba5d87SStephane Viau hdmi_cfg->mux_sel_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-sel"); 4445eba5d87SStephane Viau hdmi_cfg->mux_lpm_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-lpm"); 445dada25bdSRob Clark 446c8afe684SRob Clark #else 4475eba5d87SStephane Viau static struct hdmi_platform_config config = {}; 448dada25bdSRob Clark static const char *hpd_clk_names[] = { 449dada25bdSRob Clark "core_clk", "master_iface_clk", "slave_iface_clk", 450dada25bdSRob Clark }; 451c8afe684SRob Clark if (cpu_is_apq8064()) { 452dada25bdSRob Clark static const char *hpd_reg_names[] = {"8921_hdmi_mvs"}; 453c8afe684SRob Clark config.phy_init = hdmi_phy_8960_init; 454dada25bdSRob Clark config.hpd_reg_names = hpd_reg_names; 455dada25bdSRob Clark config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names); 456dada25bdSRob Clark config.hpd_clk_names = hpd_clk_names; 457dada25bdSRob Clark config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names); 458c8afe684SRob Clark config.ddc_clk_gpio = 70; 459c8afe684SRob Clark config.ddc_data_gpio = 71; 460c8afe684SRob Clark config.hpd_gpio = 72; 461dada25bdSRob Clark config.mux_en_gpio = -1; 462c0c0d9eeSRob Clark config.mux_sel_gpio = -1; 463e529c7e6SRob Clark } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) { 464dada25bdSRob Clark static const char *hpd_reg_names[] = {"8921_hdmi_mvs"}; 465c8afe684SRob Clark config.phy_init = hdmi_phy_8960_init; 466dada25bdSRob Clark config.hpd_reg_names = hpd_reg_names; 467dada25bdSRob Clark config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names); 468dada25bdSRob Clark config.hpd_clk_names = hpd_clk_names; 469dada25bdSRob Clark config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names); 470c8afe684SRob Clark config.ddc_clk_gpio = 100; 471c8afe684SRob Clark config.ddc_data_gpio = 101; 472c8afe684SRob Clark config.hpd_gpio = 102; 473dada25bdSRob Clark config.mux_en_gpio = -1; 474dada25bdSRob Clark config.mux_sel_gpio = -1; 475c8afe684SRob Clark } else if (cpu_is_msm8x60()) { 476dada25bdSRob Clark static const char *hpd_reg_names[] = { 477dada25bdSRob Clark "8901_hdmi_mvs", "8901_mpp0" 478dada25bdSRob Clark }; 479c8afe684SRob Clark config.phy_init = hdmi_phy_8x60_init; 480dada25bdSRob Clark config.hpd_reg_names = hpd_reg_names; 481dada25bdSRob Clark config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names); 482dada25bdSRob Clark config.hpd_clk_names = hpd_clk_names; 483dada25bdSRob Clark config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names); 484c8afe684SRob Clark config.ddc_clk_gpio = 170; 485c8afe684SRob Clark config.ddc_data_gpio = 171; 486c8afe684SRob Clark config.hpd_gpio = 172; 487dada25bdSRob Clark config.mux_en_gpio = -1; 488dada25bdSRob Clark config.mux_sel_gpio = -1; 489c8afe684SRob Clark } 490c6a57a50Sjilai wang config.mmio_name = "hdmi_msm_hdmi_addr"; 491c6a57a50Sjilai wang config.qfprom_mmio_name = "hdmi_msm_qfprom_addr"; 492c6a57a50Sjilai wang 4935eba5d87SStephane Viau hdmi_cfg = &config; 494c8afe684SRob Clark #endif 4955eba5d87SStephane Viau dev->platform_data = hdmi_cfg; 4965eba5d87SStephane Viau 497067fef37SRob Clark hdmi = hdmi_init(to_platform_device(dev)); 498067fef37SRob Clark if (IS_ERR(hdmi)) 499067fef37SRob Clark return PTR_ERR(hdmi); 500d1a717bdSRob Clark priv->hdmi = hdmi; 5015eba5d87SStephane Viau 502c8afe684SRob Clark return 0; 503c8afe684SRob Clark } 504c8afe684SRob Clark 505060530f1SRob Clark static void hdmi_unbind(struct device *dev, struct device *master, 506060530f1SRob Clark void *data) 507060530f1SRob Clark { 508d1a717bdSRob Clark struct drm_device *drm = dev_get_drvdata(master); 509d1a717bdSRob Clark struct msm_drm_private *priv = drm->dev_private; 510d1a717bdSRob Clark if (priv->hdmi) { 511d1a717bdSRob Clark hdmi_destroy(priv->hdmi); 512d1a717bdSRob Clark priv->hdmi = NULL; 513d1a717bdSRob Clark } 514060530f1SRob Clark } 515060530f1SRob Clark 516060530f1SRob Clark static const struct component_ops hdmi_ops = { 517060530f1SRob Clark .bind = hdmi_bind, 518060530f1SRob Clark .unbind = hdmi_unbind, 519060530f1SRob Clark }; 520060530f1SRob Clark 521060530f1SRob Clark static int hdmi_dev_probe(struct platform_device *pdev) 522060530f1SRob Clark { 523060530f1SRob Clark return component_add(&pdev->dev, &hdmi_ops); 524060530f1SRob Clark } 525060530f1SRob Clark 526c8afe684SRob Clark static int hdmi_dev_remove(struct platform_device *pdev) 527c8afe684SRob Clark { 528060530f1SRob Clark component_del(&pdev->dev, &hdmi_ops); 529c8afe684SRob Clark return 0; 530c8afe684SRob Clark } 531c8afe684SRob Clark 532c8afe684SRob Clark static struct platform_driver hdmi_driver = { 533c8afe684SRob Clark .probe = hdmi_dev_probe, 534c8afe684SRob Clark .remove = hdmi_dev_remove, 535dada25bdSRob Clark .driver = { 536dada25bdSRob Clark .name = "hdmi_msm", 537dada25bdSRob Clark .of_match_table = dt_match, 538dada25bdSRob Clark }, 539c8afe684SRob Clark }; 540c8afe684SRob Clark 541c8afe684SRob Clark void __init hdmi_register(void) 542c8afe684SRob Clark { 543c8afe684SRob Clark platform_driver_register(&hdmi_driver); 544c8afe684SRob Clark } 545c8afe684SRob Clark 546c8afe684SRob Clark void __exit hdmi_unregister(void) 547c8afe684SRob Clark { 548c8afe684SRob Clark platform_driver_unregister(&hdmi_driver); 549c8afe684SRob Clark } 550