diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dsi/dsi_phy.c')
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_phy.c | 315 |
1 files changed, 285 insertions, 30 deletions
diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c index f0cea8927388..2d3b33ce1cc5 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; @@ -34,15 +49,106 @@ struct dsi_dphy_timing { }; struct msm_dsi_phy { + struct platform_device *pdev; void __iomem *base; void __iomem *reg_base; int id; + + struct clk *ahb_clk; + struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; + struct dsi_dphy_timing timing; - 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); + const struct dsi_phy_cfg *cfg; + + struct msm_dsi_pll *pll; }; +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))) @@ -284,59 +390,200 @@ static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy) return 0; } -#define dsi_phy_func_init(name) \ - do { \ - phy->enable = dsi_##name##_phy_enable; \ - phy->disable = dsi_##name##_phy_disable; \ - } while (0) +static int dsi_phy_enable_resource(struct msm_dsi_phy *phy) +{ + int ret; + + pm_runtime_get_sync(&phy->pdev->dev); -struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, - enum msm_dsi_phy_type type, int id) + ret = clk_prepare_enable(phy->ahb_clk); + if (ret) { + pr_err("%s: can't enable ahb clk, %d\n", __func__, ret); + pm_runtime_put_sync(&phy->pdev->dev); + } + + return ret; +} + +static void dsi_phy_disable_resource(struct msm_dsi_phy *phy) +{ + clk_disable_unprepare(phy->ahb_clk); + pm_runtime_put_sync(&phy->pdev->dev); +} + +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, + } + }, +}; + +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_OR_NULL(phy->base)) { - pr_err("%s: failed to map phy base\n", __func__); - return NULL; + if (IS_ERR(phy->base)) { + 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_OR_NULL(phy->reg_base)) { - pr_err("%s: failed to map phy regulator base\n", __func__); - return NULL; + if (IS_ERR(phy->reg_base)) { + dev_err(&pdev->dev, + "%s: failed to map phy regulator base\n", __func__); + ret = -ENOMEM; + goto fail; } - switch (type) { - case MSM_DSI_PHY_28NM: - 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->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__); + ret = PTR_ERR(phy->ahb_clk); + goto fail; } - phy->id = id; + /* PLL init will call into clk_register which requires + * register access, so we need to enable power and ahb clock. + */ + ret = dsi_phy_enable_resource(phy); + if (ret) + goto fail; + + phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id); + if (!phy->pll) + dev_info(&pdev->dev, + "%s: pll init failed, need separate pll clk driver\n", + __func__); + + dsi_phy_disable_resource(phy); + + platform_set_drvdata(pdev, phy); + + return 0; - return phy; +fail: + return ret; +} + +static int dsi_phy_driver_remove(struct platform_device *pdev) +{ + 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, @@ -350,3 +597,11 @@ void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, *clk_post = phy->timing.clk_post; } +struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) +{ + if (!phy) + return NULL; + + return phy->pll; +} + |