drm/msm: add hdmi support for apq8x74/mdp5
[deliverable/linux.git] / drivers / gpu / drm / msm / hdmi / hdmi.c
index 32f26f85505059e8c5af56eedceb95e14107af68..6f1588aa9071f8db8642dcb8b32e46642381b38d 100644 (file)
@@ -41,7 +41,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
                        power_on ? "Enable" : "Disable", ctrl);
 }
 
-static irqreturn_t hdmi_irq(int irq, void *dev_id)
+irqreturn_t hdmi_irq(int irq, void *dev_id)
 {
        struct hdmi *hdmi = dev_id;
 
@@ -71,13 +71,13 @@ void hdmi_destroy(struct kref *kref)
 }
 
 /* initialize connector */
-int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
+struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
 {
        struct hdmi *hdmi = NULL;
        struct msm_drm_private *priv = dev->dev_private;
        struct platform_device *pdev = hdmi_pdev;
        struct hdmi_platform_config *config;
-       int ret;
+       int i, ret;
 
        if (!pdev) {
                dev_err(dev->dev, "no hdmi device\n");
@@ -99,6 +99,7 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
 
        hdmi->dev = dev;
        hdmi->pdev = pdev;
+       hdmi->config = config;
        hdmi->encoder = encoder;
 
        /* not sure about which phy maps to which msm.. probably I miss some */
@@ -114,44 +115,70 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
                goto fail;
        }
 
-       hdmi->mmio = msm_ioremap(pdev, "hdmi_msm_hdmi_addr", "HDMI");
+       hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
        if (IS_ERR(hdmi->mmio)) {
                ret = PTR_ERR(hdmi->mmio);
                goto fail;
        }
 
-       hdmi->mvs = devm_regulator_get(&pdev->dev, "8901_hdmi_mvs");
-       if (IS_ERR(hdmi->mvs))
-               hdmi->mvs = devm_regulator_get(&pdev->dev, "8921_hdmi_mvs");
-       if (IS_ERR(hdmi->mvs)) {
-               ret = PTR_ERR(hdmi->mvs);
-               dev_err(dev->dev, "failed to get mvs regulator: %d\n", ret);
-               goto fail;
+       BUG_ON(config->hpd_reg_cnt > ARRAY_SIZE(hdmi->hpd_regs));
+       for (i = 0; i < config->hpd_reg_cnt; i++) {
+               struct regulator *reg;
+
+               reg = devm_regulator_get(&pdev->dev, config->hpd_reg_names[i]);
+               if (IS_ERR(reg)) {
+                       ret = PTR_ERR(reg);
+                       dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n",
+                                       config->hpd_reg_names[i], ret);
+                       goto fail;
+               }
+
+               hdmi->hpd_regs[i] = reg;
        }
 
-       hdmi->mpp0 = devm_regulator_get(&pdev->dev, "8901_mpp0");
-       if (IS_ERR(hdmi->mpp0))
-               hdmi->mpp0 = NULL;
+       BUG_ON(config->pwr_reg_cnt > ARRAY_SIZE(hdmi->pwr_regs));
+       for (i = 0; i < config->pwr_reg_cnt; i++) {
+               struct regulator *reg;
 
-       hdmi->clk = devm_clk_get(&pdev->dev, "core_clk");
-       if (IS_ERR(hdmi->clk)) {
-               ret = PTR_ERR(hdmi->clk);
-               dev_err(dev->dev, "failed to get 'clk': %d\n", ret);
-               goto fail;
+               reg = devm_regulator_get(&pdev->dev, config->pwr_reg_names[i]);
+               if (IS_ERR(reg)) {
+                       ret = PTR_ERR(reg);
+                       dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n",
+                                       config->pwr_reg_names[i], ret);
+                       goto fail;
+               }
+
+               hdmi->pwr_regs[i] = reg;
        }
 
-       hdmi->m_pclk = devm_clk_get(&pdev->dev, "master_iface_clk");
-       if (IS_ERR(hdmi->m_pclk)) {
-               ret = PTR_ERR(hdmi->m_pclk);
-               dev_err(dev->dev, "failed to get 'm_pclk': %d\n", ret);
-               goto fail;
+       BUG_ON(config->hpd_clk_cnt > ARRAY_SIZE(hdmi->hpd_clks));
+       for (i = 0; i < config->hpd_clk_cnt; i++) {
+               struct clk *clk;
+
+               clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       dev_err(dev->dev, "failed to get hpd clk: %s (%d)\n",
+                                       config->hpd_clk_names[i], ret);
+                       goto fail;
+               }
+
+               hdmi->hpd_clks[i] = clk;
        }
 
-       hdmi->s_pclk = devm_clk_get(&pdev->dev, "slave_iface_clk");
-       if (IS_ERR(hdmi->s_pclk)) {
-               ret = PTR_ERR(hdmi->s_pclk);
-               dev_err(dev->dev, "failed to get 's_pclk': %d\n", ret);
-               goto fail;
+       BUG_ON(config->pwr_clk_cnt > ARRAY_SIZE(hdmi->pwr_clks));
+       for (i = 0; i < config->pwr_clk_cnt; i++) {
+               struct clk *clk;
+
+               clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       dev_err(dev->dev, "failed to get pwr clk: %s (%d)\n",
+                                       config->pwr_clk_names[i], ret);
+                       goto fail;
+               }
+
+               hdmi->pwr_clks[i] = clk;
        }
 
        hdmi->i2c = hdmi_i2c_init(hdmi);
@@ -178,20 +205,22 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
                goto fail;
        }
 
-       hdmi->irq = platform_get_irq(pdev, 0);
-       if (hdmi->irq < 0) {
-               ret = hdmi->irq;
-               dev_err(dev->dev, "failed to get irq: %d\n", ret);
-               goto fail;
-       }
+       if (!config->shared_irq) {
+               hdmi->irq = platform_get_irq(pdev, 0);
+               if (hdmi->irq < 0) {
+                       ret = hdmi->irq;
+                       dev_err(dev->dev, "failed to get irq: %d\n", ret);
+                       goto fail;
+               }
 
-       ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq,
-                       NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
-                       "hdmi_isr", hdmi);
-       if (ret < 0) {
-               dev_err(dev->dev, "failed to request IRQ%u: %d\n",
-                               hdmi->irq, ret);
-               goto fail;
+               ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq,
+                               NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                               "hdmi_isr", hdmi);
+               if (ret < 0) {
+                       dev_err(dev->dev, "failed to request IRQ%u: %d\n",
+                                       hdmi->irq, ret);
+                       goto fail;
+               }
        }
 
        encoder->bridge = hdmi->bridge;
@@ -199,7 +228,7 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
        priv->bridges[priv->num_bridges++]       = hdmi->bridge;
        priv->connectors[priv->num_connectors++] = hdmi->connector;
 
-       return 0;
+       return hdmi;
 
 fail:
        if (hdmi) {
@@ -211,37 +240,100 @@ fail:
                hdmi_destroy(&hdmi->refcount);
        }
 
-       return ret;
+       return ERR_PTR(ret);
 }
 
 /*
  * The hdmi device:
  */
 
+#include <linux/of_gpio.h>
+
 static int hdmi_dev_probe(struct platform_device *pdev)
 {
        static struct hdmi_platform_config config = {};
 #ifdef CONFIG_OF
-       /* TODO */
+       struct device_node *of_node = pdev->dev.of_node;
+
+       int get_gpio(const char *name)
+       {
+               int gpio = of_get_named_gpio(of_node, name, 0);
+               if (gpio < 0) {
+                       dev_err(&pdev->dev, "failed to get gpio: %s (%d)\n",
+                                       name, gpio);
+                       gpio = -1;
+               }
+               return gpio;
+       }
+
+       /* TODO actually use DT.. */
+       static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"};
+       static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"};
+       static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"};
+       static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
+
+       config.phy_init      = hdmi_phy_8x74_init;
+       config.mmio_name     = "core_physical";
+       config.hpd_reg_names = hpd_reg_names;
+       config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
+       config.pwr_reg_names = pwr_reg_names;
+       config.pwr_reg_cnt   = ARRAY_SIZE(pwr_reg_names);
+       config.hpd_clk_names = hpd_clk_names;
+       config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
+       config.pwr_clk_names = pwr_clk_names;
+       config.pwr_clk_cnt   = ARRAY_SIZE(pwr_clk_names);
+       config.ddc_clk_gpio  = get_gpio("qcom,hdmi-tx-ddc-clk");
+       config.ddc_data_gpio = get_gpio("qcom,hdmi-tx-ddc-data");
+       config.hpd_gpio      = get_gpio("qcom,hdmi-tx-hpd");
+       config.mux_en_gpio   = get_gpio("qcom,hdmi-tx-mux-en");
+       config.mux_sel_gpio  = get_gpio("qcom,hdmi-tx-mux-sel");
+       config.shared_irq    = true;
+
 #else
+       static const char *hpd_clk_names[] = {
+                       "core_clk", "master_iface_clk", "slave_iface_clk",
+       };
        if (cpu_is_apq8064()) {
+               static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
                config.phy_init      = hdmi_phy_8960_init;
+               config.mmio_name     = "hdmi_msm_hdmi_addr";
+               config.hpd_reg_names = hpd_reg_names;
+               config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
+               config.hpd_clk_names = hpd_clk_names;
+               config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
                config.ddc_clk_gpio  = 70;
                config.ddc_data_gpio = 71;
                config.hpd_gpio      = 72;
-               config.pmic_gpio     = 13 + NR_GPIO_IRQS;
+               config.mux_en_gpio   = -1;
+               config.mux_sel_gpio  = 13 + NR_GPIO_IRQS;
        } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
+               static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
                config.phy_init      = hdmi_phy_8960_init;
+               config.mmio_name     = "hdmi_msm_hdmi_addr";
+               config.hpd_reg_names = hpd_reg_names;
+               config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
+               config.hpd_clk_names = hpd_clk_names;
+               config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
                config.ddc_clk_gpio  = 100;
                config.ddc_data_gpio = 101;
                config.hpd_gpio      = 102;
-               config.pmic_gpio     = -1;
+               config.mux_en_gpio   = -1;
+               config.mux_sel_gpio  = -1;
        } else if (cpu_is_msm8x60()) {
+               static const char *hpd_reg_names[] = {
+                               "8901_hdmi_mvs", "8901_mpp0"
+               };
                config.phy_init      = hdmi_phy_8x60_init;
+               config.mmio_name     = "hdmi_msm_hdmi_addr";
+               config.hpd_reg_names = hpd_reg_names;
+               config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
+               config.hpd_clk_names = hpd_clk_names;
+               config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
                config.ddc_clk_gpio  = 170;
                config.ddc_data_gpio = 171;
                config.hpd_gpio      = 172;
-               config.pmic_gpio     = -1;
+               config.mux_en_gpio   = -1;
+               config.mux_sel_gpio  = -1;
        }
 #endif
        pdev->dev.platform_data = &config;
@@ -255,10 +347,19 @@ static int hdmi_dev_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id dt_match[] = {
+       { .compatible = "qcom,hdmi-tx" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
 static struct platform_driver hdmi_driver = {
        .probe = hdmi_dev_probe,
        .remove = hdmi_dev_remove,
-       .driver.name = "hdmi_msm",
+       .driver = {
+               .name = "hdmi_msm",
+               .of_match_table = dt_match,
+       },
 };
 
 void __init hdmi_register(void)
This page took 0.034382 seconds and 5 git commands to generate.