summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c2
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h48
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy.c190
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c18
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c6
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c20
7 files changed, 281 insertions, 4 deletions
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 065ad4138799..6ad0f7e625e7 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -12,6 +12,7 @@ msm-y := \
hdmi/hdmi_connector.o \
hdmi/hdmi_hdcp.o \
hdmi/hdmi_i2c.o \
+ hdmi/hdmi_phy.o \
hdmi/hdmi_phy_8960.o \
hdmi/hdmi_phy_8x60.o \
hdmi/hdmi_phy_8x74.o \
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 68cc3cdefe92..0fe5411c5c3e 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -502,10 +502,12 @@ static struct platform_driver hdmi_driver = {
void __init hdmi_register(void)
{
+ hdmi_phy_driver_register();
platform_driver_register(&hdmi_driver);
}
void __exit hdmi_unregister(void)
{
platform_driver_unregister(&hdmi_driver);
+ hdmi_phy_driver_unregister();
}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index d71533219877..3404235bb24d 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -139,25 +139,65 @@ static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg)
}
/*
- * The phy appears to be different, for example between 8960 and 8x60,
- * so split the phy related functions out and load the correct one at
- * runtime:
+ * hdmi phy:
*/
-
struct hdmi_phy_funcs {
void (*destroy)(struct hdmi_phy *phy);
void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock);
void (*powerdown)(struct hdmi_phy *phy);
};
+enum hdmi_phy_type {
+ MSM_HDMI_PHY_8x60,
+ MSM_HDMI_PHY_8960,
+ MSM_HDMI_PHY_8x74,
+ MSM_HDMI_PHY_MAX,
+};
+
+struct hdmi_phy_cfg {
+ enum hdmi_phy_type type;
+ void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock);
+ void (*powerdown)(struct hdmi_phy *phy);
+ const char * const *reg_names;
+ int num_regs;
+ const char * const *clk_names;
+ int num_clks;
+};
+
+extern const struct hdmi_phy_cfg hdmi_phy_8x60_cfg;
+extern const struct hdmi_phy_cfg hdmi_phy_8960_cfg;
+extern const struct hdmi_phy_cfg hdmi_phy_8x74_cfg;
+
struct hdmi_phy {
+ struct platform_device *pdev;
+ void __iomem *mmio;
+ struct hdmi_phy_cfg *cfg;
const struct hdmi_phy_funcs *funcs;
+ struct regulator **regs;
+ struct clk **clks;
};
struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi);
struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);
struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi);
+static inline void hdmi_phy_write(struct hdmi_phy *phy, u32 reg, u32 data)
+{
+ msm_writel(data, phy->mmio + reg);
+}
+
+static inline u32 hdmi_phy_read(struct hdmi_phy *phy, u32 reg)
+{
+ return msm_readl(phy->mmio + reg);
+}
+
+int hdmi_phy_resource_enable(struct hdmi_phy *phy);
+void hdmi_phy_resource_disable(struct hdmi_phy *phy);
+void hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock);
+void hdmi_phy_powerdown(struct hdmi_phy *phy);
+void __init hdmi_phy_driver_register(void);
+void __exit hdmi_phy_driver_unregister(void);
+
/*
* audio:
*/
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
new file mode 100644
index 000000000000..de3f0f504ede
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of_device.h>
+
+#include "hdmi.h"
+
+static int hdmi_phy_resource_init(struct hdmi_phy *phy)
+{
+ struct hdmi_phy_cfg *cfg = phy->cfg;
+ struct device *dev = &phy->pdev->dev;
+ int i, ret;
+
+ phy->regs = devm_kzalloc(dev, sizeof(phy->regs[0]) * cfg->num_regs,
+ GFP_KERNEL);
+ if (!phy->regs)
+ return -ENOMEM;
+
+ phy->clks = devm_kzalloc(dev, sizeof(phy->clks[0]) * cfg->num_clks,
+ GFP_KERNEL);
+ if (!phy->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_regs; i++) {
+ struct regulator *reg;
+
+ reg = devm_regulator_get(dev, cfg->reg_names[i]);
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ dev_err(dev, "failed to get phy regulator: %s (%d)\n",
+ cfg->reg_names[i], ret);
+ return ret;
+ }
+
+ phy->regs[i] = reg;
+ }
+
+ for (i = 0; i < cfg->num_clks; i++) {
+ struct clk *clk;
+
+ clk = devm_clk_get(dev, cfg->clk_names[i]);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "failed to get phy clock: %s (%d)\n",
+ cfg->clk_names[i], ret);
+ return ret;
+ }
+
+ phy->clks[i] = clk;
+ }
+
+ return 0;
+}
+
+int hdmi_phy_resource_enable(struct hdmi_phy *phy)
+{
+ struct hdmi_phy_cfg *cfg = phy->cfg;
+ struct device *dev = &phy->pdev->dev;
+ int i, ret = 0;
+
+ pm_runtime_get_sync(dev);
+
+ for (i = 0; i < cfg->num_regs; i++) {
+ ret = regulator_enable(phy->regs[i]);
+ if (ret)
+ dev_err(dev, "failed to enable regulator: %s (%d)\n",
+ cfg->reg_names[i], ret);
+ }
+
+ for (i = 0; i < cfg->num_clks; i++) {
+ ret = clk_prepare_enable(phy->clks[i]);
+ if (ret)
+ dev_err(dev, "failed to enable clock: %s (%d)\n",
+ cfg->clk_names[i], ret);
+ }
+
+ return ret;
+}
+
+void hdmi_phy_resource_disable(struct hdmi_phy *phy)
+{
+ struct hdmi_phy_cfg *cfg = phy->cfg;
+ struct device *dev = &phy->pdev->dev;
+ int i;
+
+ for (i = cfg->num_clks - 1; i >= 0; i--)
+ clk_disable_unprepare(phy->clks[i]);
+
+ for (i = cfg->num_regs - 1; i >= 0; i--)
+ regulator_disable(phy->regs[i]);
+
+ pm_runtime_put_sync(dev);
+}
+
+void hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock)
+{
+ if (!phy || !phy->cfg->powerup)
+ return;
+
+ phy->cfg->powerup(phy, pixclock);
+}
+
+void hdmi_phy_powerdown(struct hdmi_phy *phy)
+{
+ if (!phy || !phy->cfg->powerdown)
+ return;
+
+ phy->cfg->powerdown(phy);
+}
+
+static int hdmi_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hdmi_phy *phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENODEV;
+
+ phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
+ if (!phy->cfg)
+ return -ENODEV;
+
+ phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY");
+ if (IS_ERR(phy->mmio)) {
+ dev_err(dev, "%s: failed to map phy base\n", __func__);
+ return -ENOMEM;
+ }
+
+ phy->pdev = pdev;
+
+ ret = hdmi_phy_resource_init(phy);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+
+ platform_set_drvdata(pdev, phy);
+
+ return 0;
+}
+
+static int hdmi_phy_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id hdmi_phy_dt_match[] = {
+ { .compatible = "qcom,hdmi-phy-8660",
+ .data = &hdmi_phy_8x60_cfg },
+ { .compatible = "qcom,hdmi-phy-8960",
+ .data = &hdmi_phy_8960_cfg },
+ { .compatible = "qcom,hdmi-phy-8974",
+ .data = &hdmi_phy_8x74_cfg },
+ { .compatible = "qcom,hdmi-phy-8084",
+ .data = &hdmi_phy_8x74_cfg },
+ {}
+};
+
+static struct platform_driver hdmi_phy_platform_driver = {
+ .probe = hdmi_phy_probe,
+ .remove = hdmi_phy_remove,
+ .driver = {
+ .name = "msm_hdmi_phy",
+ .of_match_table = hdmi_phy_dt_match,
+ },
+};
+
+void __init hdmi_phy_driver_register(void)
+{
+ platform_driver_register(&hdmi_phy_platform_driver);
+}
+
+void __exit hdmi_phy_driver_unregister(void)
+{
+ platform_driver_unregister(&hdmi_phy_platform_driver);
+}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
index 3a01cb5051e2..cbdd70085d12 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
@@ -464,6 +464,24 @@ static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
.powerdown = hdmi_phy_8960_powerdown,
};
+static const char * const hdmi_phy_8960_reg_names[] = {
+ "core-vdda",
+};
+
+static const char * const hdmi_phy_8960_clk_names[] = {
+ "slave_iface_clk",
+};
+
+const struct hdmi_phy_cfg hdmi_phy_8960_cfg = {
+ .type = MSM_HDMI_PHY_8960,
+ .powerup = hdmi_phy_8960_powerup,
+ .powerdown = hdmi_phy_8960_powerdown,
+ .reg_names = hdmi_phy_8960_reg_names,
+ .num_regs = ARRAY_SIZE(hdmi_phy_8960_reg_names),
+ .clk_names = hdmi_phy_8960_clk_names,
+ .num_clks = ARRAY_SIZE(hdmi_phy_8960_clk_names),
+};
+
struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
{
struct hdmi_phy_8960 *phy_8960;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c
index cb01421ae1e4..d529164f938d 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c
@@ -155,6 +155,12 @@ static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = {
.powerdown = hdmi_phy_8x60_powerdown,
};
+const struct hdmi_phy_cfg hdmi_phy_8x60_cfg = {
+ .type = MSM_HDMI_PHY_8x60,
+ .powerup = hdmi_phy_8x60_powerup,
+ .powerdown = hdmi_phy_8x60_powerdown,
+};
+
struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi)
{
struct hdmi_phy_8x60 *phy_8x60;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c
index 56ab8917ee9a..5e42d92f3699 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c
@@ -67,6 +67,26 @@ static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = {
.powerdown = hdmi_phy_8x74_powerdown,
};
+static const char * const hdmi_phy_8x74_reg_names[] = {
+ "core-vdda",
+ "vddio",
+};
+
+static const char * const hdmi_phy_8x74_clk_names[] = {
+ "iface_clk",
+ "alt_iface_clk"
+};
+
+const struct hdmi_phy_cfg hdmi_phy_8x74_cfg = {
+ .type = MSM_HDMI_PHY_8x74,
+ .powerup = hdmi_phy_8x74_powerup,
+ .powerdown = hdmi_phy_8x74_powerdown,
+ .reg_names = hdmi_phy_8x74_reg_names,
+ .num_regs = ARRAY_SIZE(hdmi_phy_8x74_reg_names),
+ .clk_names = hdmi_phy_8x74_clk_names,
+ .num_clks = ARRAY_SIZE(hdmi_phy_8x74_clk_names),
+};
+
struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi)
{
struct hdmi_phy_8x74 *phy_8x74;