drm/msm/dsi: Separate PHY to another platform device
There are different types of PHY from one chipset to another, while
the DSI host controller is relatively consistent across platforms.
Also, the PLL inside PHY is providing the source of DSI byte and
pixel clocks, which are used by DSI host controller. Separated devices
for clock provider and clock consumer make DSI driver better fit into
common clock framework.
Signed-off-by: Hai Li <hali@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c
index 2b1c8fd..2d3b33c 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c
@@ -11,12 +11,27 @@
* GNU General Public License for more details.
*/
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
#include "dsi.h"
#include "dsi.xml.h"
#define dsi_phy_read(offset) msm_readl((offset))
#define dsi_phy_write(offset, data) msm_writel((data), (offset))
+struct dsi_phy_ops {
+ int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
+ const unsigned long bit_rate, const unsigned long esc_rate);
+ int (*disable)(struct msm_dsi_phy *phy);
+};
+
+struct dsi_phy_cfg {
+ enum msm_dsi_phy_type type;
+ struct dsi_reg_config reg_cfg;
+ struct dsi_phy_ops ops;
+};
+
struct dsi_dphy_timing {
u32 clk_pre;
u32 clk_post;
@@ -40,17 +55,100 @@
int id;
struct clk *ahb_clk;
+ struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
struct dsi_dphy_timing timing;
- enum msm_dsi_phy_type type;
+ const struct dsi_phy_cfg *cfg;
struct msm_dsi_pll *pll;
-
- int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
- const unsigned long bit_rate, const unsigned long esc_rate);
- int (*disable)(struct msm_dsi_phy *phy);
};
+static int dsi_phy_regulator_init(struct msm_dsi_phy *phy)
+{
+ struct regulator_bulk_data *s = phy->supplies;
+ const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+ struct device *dev = &phy->pdev->dev;
+ int num = phy->cfg->reg_cfg.num;
+ int i, ret;
+
+ for (i = 0; i < num; i++)
+ s[i].supply = regs[i].name;
+
+ ret = devm_regulator_bulk_get(&phy->pdev->dev, num, s);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to init regulator, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) {
+ ret = regulator_set_voltage(s[i].consumer,
+ regs[i].min_voltage, regs[i].max_voltage);
+ if (ret < 0) {
+ dev_err(dev,
+ "regulator %d set voltage failed, %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy)
+{
+ struct regulator_bulk_data *s = phy->supplies;
+ const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+ int num = phy->cfg->reg_cfg.num;
+ int i;
+
+ DBG("");
+ for (i = num - 1; i >= 0; i--)
+ if (regs[i].disable_load >= 0)
+ regulator_set_load(s[i].consumer,
+ regs[i].disable_load);
+
+ regulator_bulk_disable(num, s);
+}
+
+static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy)
+{
+ struct regulator_bulk_data *s = phy->supplies;
+ const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+ struct device *dev = &phy->pdev->dev;
+ int num = phy->cfg->reg_cfg.num;
+ int ret, i;
+
+ DBG("");
+ for (i = 0; i < num; i++) {
+ if (regs[i].enable_load >= 0) {
+ ret = regulator_set_load(s[i].consumer,
+ regs[i].enable_load);
+ if (ret < 0) {
+ dev_err(dev,
+ "regulator %d set op mode failed, %d\n",
+ i, ret);
+ goto fail;
+ }
+ }
+ }
+
+ ret = regulator_bulk_enable(num, s);
+ if (ret < 0) {
+ dev_err(dev, "regulator enable failed, %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ for (i--; i >= 0; i--)
+ regulator_set_load(s[i].consumer, regs[i].disable_load);
+ return ret;
+}
+
#define S_DIV_ROUND_UP(n, d) \
(((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d)))
@@ -313,51 +411,94 @@
pm_runtime_put_sync(&phy->pdev->dev);
}
-#define dsi_phy_func_init(name) \
- do { \
- phy->enable = dsi_##name##_phy_enable; \
- phy->disable = dsi_##name##_phy_disable; \
- } while (0)
+static const struct dsi_phy_cfg dsi_phy_cfgs[MSM_DSI_PHY_MAX] = {
+ [MSM_DSI_PHY_28NM_HPM] = {
+ .type = MSM_DSI_PHY_28NM_HPM,
+ .reg_cfg = {
+ .num = 1,
+ .regs = {
+ {"vddio", 1800000, 1800000, 100000, 100},
+ },
+ },
+ .ops = {
+ .enable = dsi_28nm_phy_enable,
+ .disable = dsi_28nm_phy_disable,
+ }
+ },
+ [MSM_DSI_PHY_28NM_LP] = {
+ .type = MSM_DSI_PHY_28NM_LP,
+ .reg_cfg = {
+ .num = 1,
+ .regs = {
+ {"vddio", 1800000, 1800000, 100000, 100},
+ },
+ },
+ .ops = {
+ .enable = dsi_28nm_phy_enable,
+ .disable = dsi_28nm_phy_disable,
+ }
+ },
+};
-struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
- enum msm_dsi_phy_type type, int id)
+static const struct of_device_id dsi_phy_dt_match[] = {
+ { .compatible = "qcom,dsi-phy-28nm-hpm",
+ .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_HPM],},
+ { .compatible = "qcom,dsi-phy-28nm-lp",
+ .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_LP],},
+ {}
+};
+
+static int dsi_phy_driver_probe(struct platform_device *pdev)
{
struct msm_dsi_phy *phy;
+ const struct of_device_id *match;
int ret;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
- return NULL;
+ return -ENOMEM;
+
+ match = of_match_node(dsi_phy_dt_match, pdev->dev.of_node);
+ if (!match)
+ return -ENODEV;
+
+ phy->cfg = match->data;
+ phy->pdev = pdev;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,dsi-phy-index", &phy->id);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: PHY index not specified, ret=%d\n",
+ __func__, ret);
+ goto fail;
+ }
phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR(phy->base)) {
- pr_err("%s: failed to map phy base\n", __func__);
- return NULL;
+ dev_err(&pdev->dev, "%s: failed to map phy base\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
}
phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG");
if (IS_ERR(phy->reg_base)) {
- pr_err("%s: failed to map phy regulator base\n", __func__);
- return NULL;
+ dev_err(&pdev->dev,
+ "%s: failed to map phy regulator base\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
}
- switch (type) {
- case MSM_DSI_PHY_28NM_HPM:
- case MSM_DSI_PHY_28NM_LP:
- dsi_phy_func_init(28nm);
- break;
- default:
- pr_err("%s: unsupported type, %d\n", __func__, type);
- return NULL;
+ ret = dsi_phy_regulator_init(phy);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: failed to init regulator\n", __func__);
+ goto fail;
}
- phy->type = type;
- phy->id = id;
- phy->pdev = pdev;
-
phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(phy->ahb_clk)) {
pr_err("%s: Unable to get ahb clk\n", __func__);
- return NULL;
+ ret = PTR_ERR(phy->ahb_clk);
+ goto fail;
}
/* PLL init will call into clk_register which requires
@@ -365,39 +506,84 @@
*/
ret = dsi_phy_enable_resource(phy);
if (ret)
- return NULL;
+ goto fail;
- phy->pll = msm_dsi_pll_init(pdev, type, id);
+ phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id);
if (!phy->pll)
- pr_info("%s: pll init failed, need separate pll clk driver\n",
+ dev_info(&pdev->dev,
+ "%s: pll init failed, need separate pll clk driver\n",
__func__);
dsi_phy_disable_resource(phy);
- return phy;
+ platform_set_drvdata(pdev, phy);
+
+ return 0;
+
+fail:
+ return ret;
}
-void msm_dsi_phy_destroy(struct msm_dsi_phy *phy)
+static int dsi_phy_driver_remove(struct platform_device *pdev)
{
- if (phy->pll) {
+ struct msm_dsi_phy *phy = platform_get_drvdata(pdev);
+
+ if (phy && phy->pll) {
msm_dsi_pll_destroy(phy->pll);
phy->pll = NULL;
}
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver dsi_phy_platform_driver = {
+ .probe = dsi_phy_driver_probe,
+ .remove = dsi_phy_driver_remove,
+ .driver = {
+ .name = "msm_dsi_phy",
+ .of_match_table = dsi_phy_dt_match,
+ },
+};
+
+void __init msm_dsi_phy_driver_register(void)
+{
+ platform_driver_register(&dsi_phy_platform_driver);
+}
+
+void __exit msm_dsi_phy_driver_unregister(void)
+{
+ platform_driver_unregister(&dsi_phy_platform_driver);
}
int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
const unsigned long bit_rate, const unsigned long esc_rate)
{
- if (!phy || !phy->enable)
+ int ret;
+
+ if (!phy || !phy->cfg->ops.enable)
return -EINVAL;
- return phy->enable(phy, is_dual_panel, bit_rate, esc_rate);
+
+ ret = dsi_phy_regulator_enable(phy);
+ if (ret) {
+ dev_err(&phy->pdev->dev, "%s: regulator enable failed, %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return phy->cfg->ops.enable(phy, is_dual_panel, bit_rate, esc_rate);
}
int msm_dsi_phy_disable(struct msm_dsi_phy *phy)
{
- if (!phy || !phy->disable)
+ if (!phy || !phy->cfg->ops.disable)
return -EINVAL;
- return phy->disable(phy);
+
+ phy->cfg->ops.disable(phy);
+ dsi_phy_regulator_disable(phy);
+
+ return 0;
}
void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,