diff options
| author | Padmanabhan Komanduru <pkomandu@codeaurora.org> | 2014-03-27 20:03:11 +0530 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:41:15 -0700 |
| commit | 269cc0b104b8df1f975a466b2ecde0f64e1d3cfb (patch) | |
| tree | e975b7f61485ecca7a313d865b5049b4f1625a20 | |
| parent | 2a80bf39568c01a79576a5079caabc5aaddaf004 (diff) | |
clk: qcom: mdss: split the DSI PLL driver based on PLL mode
Re-organize the DSI PLL driver code and split it based on
the DSI PLL HPM/LPM mode. Add a common PLL util file to use
the APIs which are common for both PLLs. Update the DSI PLL
enable sequence with the recommended settings for LPM mode.
Change-Id: I3f86554522e16579d5c2eccab976136c7afb0dd2
Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
| -rw-r--r-- | drivers/clk/msm/mdss/Makefile | 2 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c | 899 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c | 304 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll-util.c | 570 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll.h | 32 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-pll.c | 23 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-pll.h | 17 |
7 files changed, 983 insertions, 864 deletions
diff --git a/drivers/clk/msm/mdss/Makefile b/drivers/clk/msm/mdss/Makefile index 25fb1d1e86f9..f18ee98410f8 100644 --- a/drivers/clk/msm/mdss/Makefile +++ b/drivers/clk/msm/mdss/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll-util.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-util.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-28hpm.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-28lpm.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-edp-pll-28hpm.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-28hpm.o diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c index b7c698802ed7..aab7e5e2263c 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c @@ -15,64 +15,16 @@ #include <linux/kernel.h> #include <linux/err.h> -#include <linux/iopoll.h> #include <linux/delay.h> #include <linux/clk/msm-clk-provider.h> #include <linux/clk/msm-clk.h> #include <linux/clk/msm-clock-generic.h> - #include <dt-bindings/clock/msm-clocks-8974.h> -#include <dt-bindings/clock/msm-clocks-8916.h> #include "mdss-pll.h" #include "mdss-dsi-pll.h" -#define DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0) -#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004) -#define DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x0008) -#define DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C) -#define DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x0010) -#define DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x0014) -#define DSI_PHY_PLL_UNIPHY_PLL_DMUX_CFG (0x0018) -#define DSI_PHY_PLL_UNIPHY_PLL_AMUX_CFG (0x001C) -#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020) -#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024) -#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028) -#define DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x002C) -#define DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x0030) -#define DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x0034) -#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038) -#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C) -#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040) -#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044) -#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048) -#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x004C) -#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x0050) -#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x0054) -#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x0058) -#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG0 (0x005C) -#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG1 (0x0060) -#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064) -#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x0078) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x007C) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x0080) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094) -#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098) -#define DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x009C) -#define DSI_PHY_PLL_UNIPHY_PLL_STATUS (0x00C0) - - -#define DSI_PLL_POLL_MAX_READS 10 -#define DSI_PLL_POLL_TIMEOUT_US 50 -#define DSI_PLL_SEQ_M_MAX_COUNTER 7 +#define VCO_DELAY_USEC 1 static struct clk_div_ops fixed_2div_ops; static struct clk_ops byte_mux_clk_ops; @@ -92,157 +44,25 @@ static struct lpfr_cfg lpfr_lut_struct[] = { {750000000, 11}, }; -int set_byte_mux_sel(struct mux_clk *clk, int sel) -{ - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - pr_debug("byte mux set to %s mode\n", sel ? "indirect" : "direct"); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, (sel << 1)); - - return 0; -} - -int get_byte_mux_sel(struct mux_clk *clk) -{ - int mux_mode, rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - mux_mode = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG) & BIT(1); - - pr_debug("byte mux mode = %s", mux_mode ? "indirect" : "direct"); - mdss_pll_resource_enable(dsi_pll_res, false); - - return !!mux_mode; -} - -static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk) -{ - return container_of(clk, struct dsi_pll_vco_clk, c); -} - -int dsi_pll_div_prepare(struct clk *c) -{ - struct div_clk *div = to_div_clk(c); - /* Restore the divider's value */ - return div->ops->set_div(div, div->data.div); -} - -int dsi_pll_mux_prepare(struct clk *c) -{ - struct mux_clk *mux = to_mux_clk(c); - int i, rc, sel = 0; - struct mdss_pll_resources *dsi_pll_res = mux->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - for (i = 0; i < mux->num_parents; i++) - if (mux->parents[i].src == c->parent) { - sel = mux->parents[i].sel; - break; - } - - if (i == mux->num_parents) { - pr_err("Failed to select the parent clock\n"); - rc = -EINVAL; - goto error; - } - - /* Restore the mux source select value */ - rc = mux->ops->set_mux_sel(mux, sel); - -error: - mdss_pll_resource_enable(dsi_pll_res, false); - return rc; -} - -static int fixed_4div_set_div(struct div_clk *clk, int div) +static void dsi_pll_software_reset(struct mdss_pll_resources *dsi_pll_res) { - int rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - + /* + * Add HW recommended delays after toggling the software + * reset bit off and back on. + */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1)); - - mdss_pll_resource_enable(dsi_pll_res, false); - return rc; -} - -static int fixed_4div_get_div(struct div_clk *clk) -{ - int div = 0, rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG); - - mdss_pll_resource_enable(dsi_pll_res, false); - return div + 1; -} - -static int digital_set_div(struct div_clk *clk, int div) -{ - int rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - + DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); + udelay(1); MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, (div - 1)); - - mdss_pll_resource_enable(dsi_pll_res, false); - return rc; -} - -static int digital_get_div(struct div_clk *clk) -{ - int div = 0, rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG); - - mdss_pll_resource_enable(dsi_pll_res, false); - return div + 1; + DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); + udelay(1); } -static int analog_set_div(struct div_clk *clk, int div) +static int vco_set_rate_hpm(struct clk *c, unsigned long rate) { int rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; rc = mdss_pll_resource_enable(dsi_pll_res, true); if (rc) { @@ -250,82 +70,15 @@ static int analog_set_div(struct div_clk *clk, int div) return rc; } - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, div - 1); + rc = vco_set_rate(vco, rate); mdss_pll_resource_enable(dsi_pll_res, false); return rc; } -static int analog_get_div(struct div_clk *clk) -{ - int div = 0, rc; - struct mdss_pll_resources *dsi_pll_res = clk->priv; - - rc = mdss_pll_resource_enable(clk->priv, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG) + 1; - - mdss_pll_resource_enable(dsi_pll_res, false); - - return div; -} - -static int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res) -{ - u32 status; - int pll_locked; - - /* poll for PLL ready status */ - if (readl_poll_timeout_noirq((dsi_pll_res->pll_base + - DSI_PHY_PLL_UNIPHY_PLL_STATUS), - status, - ((status & BIT(0)) == 1), - DSI_PLL_POLL_MAX_READS, - DSI_PLL_POLL_TIMEOUT_US)) { - pr_debug("DSI PLL status=%x failed to Lock\n", status); - pll_locked = 0; - } else { - pll_locked = 1; - } - - return pll_locked; -} - -static void dsi_pll_software_reset(struct mdss_pll_resources *dsi_pll_res) -{ - /* - * Add HW recommended delays after toggling the software - * reset bit off and back on. - */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); - udelay(1); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); - udelay(1); -} - -static void dsi_pll_toggle_lock_detect(struct mdss_pll_resources *dsi_pll_res) -{ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, - 0x0d); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, - 0x0c); - udelay(1); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, - 0x0d); -} - static int dsi_pll_enable_seq_8974(struct mdss_pll_resources *dsi_pll_res) { int i, rc = 0; - u32 max_reads, timeout_us; int pll_locked; dsi_pll_software_reset(dsi_pll_res); @@ -355,9 +108,6 @@ static int dsi_pll_enable_seq_8974(struct mdss_pll_resources *dsi_pll_res) udelay(100); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d); - /* poll for PLL ready status */ - max_reads = 5; - timeout_us = 100; pll_locked = dsi_pll_lock_status(dsi_pll_res); if (pll_locked) @@ -399,523 +149,10 @@ static int dsi_pll_enable_seq_8974(struct mdss_pll_resources *dsi_pll_res) return rc; } -static int dsi_pll_enable_seq_m(struct mdss_pll_resources *dsi_pll_res) -{ - int i = 0; - int pll_locked = 0; - - dsi_pll_software_reset(dsi_pll_res); - - /* - * Add hardware recommended delays between register writes for - * the updates to take effect. These delays are necessary for the - * PLL to successfully lock - */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(600); - - dsi_pll_toggle_lock_detect(dsi_pll_res); - pll_locked = dsi_pll_lock_status(dsi_pll_res); - for (i = 0; (i < DSI_PLL_SEQ_M_MAX_COUNTER) && !pll_locked; i++) { - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); - udelay(50); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(100); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(600); - dsi_pll_toggle_lock_detect(dsi_pll_res); - pll_locked = dsi_pll_lock_status(dsi_pll_res); - } - - if (pll_locked) - pr_debug("PLL Locked at attempt #%d\n", i); - else - pr_debug("PLL failed to lock after %d attempt(s)\n", i); - - return pll_locked ? 0 : -EINVAL; -} - -static int dsi_pll_enable_seq_d(struct mdss_pll_resources *dsi_pll_res) -{ - int pll_locked = 0; - - dsi_pll_software_reset(dsi_pll_res); - - /* - * Add hardware recommended delays between register writes for - * the updates to take effect. These delays are necessary for the - * PLL to successfully lock - */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); - udelay(50); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(600); - - dsi_pll_toggle_lock_detect(dsi_pll_res); - pll_locked = dsi_pll_lock_status(dsi_pll_res); - pr_debug("PLL status = %s\n", pll_locked ? "Locked" : "Unlocked"); - - return pll_locked ? 0 : -EINVAL; -} - -static int dsi_pll_enable_seq_f1(struct mdss_pll_resources *dsi_pll_res) -{ - int pll_locked = 0; - - dsi_pll_software_reset(dsi_pll_res); - - /* - * Add hardware recommended delays between register writes for - * the updates to take effect. These delays are necessary for the - * PLL to successfully lock - */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); - udelay(50); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(600); - - dsi_pll_toggle_lock_detect(dsi_pll_res); - pll_locked = dsi_pll_lock_status(dsi_pll_res); - pr_debug("PLL status = %s\n", pll_locked ? "Locked" : "Unlocked"); - - return pll_locked ? 0 : -EINVAL; -} - -static int dsi_pll_enable_seq_c(struct mdss_pll_resources *dsi_pll_res) -{ - int pll_locked = 0; - - dsi_pll_software_reset(dsi_pll_res); - - /* - * Add hardware recommended delays between register writes for - * the updates to take effect. These delays are necessary for the - * PLL to successfully lock - */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); - udelay(50); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(600); - - dsi_pll_toggle_lock_detect(dsi_pll_res); - pll_locked = dsi_pll_lock_status(dsi_pll_res); - pr_debug("PLL status = %s\n", pll_locked ? "Locked" : "Unlocked"); - - return pll_locked ? 0 : -EINVAL; -} - -static int dsi_pll_enable_seq_e(struct mdss_pll_resources *dsi_pll_res) -{ - int pll_locked = 0; - - dsi_pll_software_reset(dsi_pll_res); - - /* - * Add hardware recommended delays between register writes for - * the updates to take effect. These delays are necessary for the - * PLL to successfully lock - */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); - udelay(50); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); - udelay(200); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d); - udelay(1); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); - udelay(600); - - dsi_pll_toggle_lock_detect(dsi_pll_res); - pll_locked = dsi_pll_lock_status(dsi_pll_res); - pr_debug("PLL status = %s\n", pll_locked ? "Locked" : "Unlocked"); - - return pll_locked ? 0 : -EINVAL; -} - -static int dsi_pll_enable(struct clk *c) -{ - int i, rc; - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - /* Try all enable sequences until one succeeds */ - for (i = 0; i < vco->pll_en_seq_cnt; i++) { - rc = vco->pll_enable_seqs[i](dsi_pll_res); - pr_debug("DSI PLL %s after sequence #%d\n", - rc ? "unlocked" : "locked", i + 1); - if (!rc) - break; - } - - if (rc) { - mdss_pll_resource_enable(dsi_pll_res, false); - pr_err("DSI PLL failed to lock\n"); - } - dsi_pll_res->pll_on = true; - - return rc; -} - -static void dsi_pll_disable(struct clk *c) -{ - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - if (!dsi_pll_res->pll_on && - mdss_pll_resource_enable(dsi_pll_res, true)) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return; - } - - dsi_pll_res->handoff_resources = false; - - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00); - - mdss_pll_resource_enable(dsi_pll_res, false); - dsi_pll_res->pll_on = false; - - pr_debug("DSI PLL Disabled\n"); - return; -} - -static int vco_set_rate(struct clk *c, unsigned long rate) -{ - s64 vco_clk_rate = rate; - s32 rem; - s64 refclk_cfg, frac_n_mode, ref_doubler_en_b; - s64 ref_clk_to_pll, div_fbx1000, frac_n_value; - s64 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3; - s64 gen_vco_clk, cal_cfg10, cal_cfg11; - u32 res; - int i, rc; - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - /* Configure the Loop filter resistance */ - for (i = 0; i < vco->lpfr_lut_size; i++) - if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate) - break; - if (i == vco->lpfr_lut_size) { - pr_err("unable to get loop filter resistance. vco=%ld\n", rate); - rc = -EINVAL; - goto error; - } - res = vco->lpfr_lut[i].r; - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG, res); - - /* Loop filter capacitance values : c1 and c2 */ - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15); - - div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem); - if (rem) { - refclk_cfg = 0x1; - frac_n_mode = 1; - ref_doubler_en_b = 0; - } else { - refclk_cfg = 0x0; - frac_n_mode = 0; - ref_doubler_en_b = 1; - } - - pr_debug("refclk_cfg = %lld\n", refclk_cfg); - - ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (refclk_cfg)) - + (ref_doubler_en_b * vco->ref_clk_rate)); - div_fbx1000 = div_s64((vco_clk_rate * 1000), ref_clk_to_pll); - - div_s64_rem(div_fbx1000, 1000, &rem); - frac_n_value = div_s64((rem * (1 << 16)), 1000); - gen_vco_clk = div_s64(div_fbx1000 * ref_clk_to_pll, 1000); - - pr_debug("ref_clk_to_pll = %lld\n", ref_clk_to_pll); - pr_debug("div_fb = %lld\n", div_fbx1000); - pr_debug("frac_n_value = %lld\n", frac_n_value); - - pr_debug("Generated VCO Clock: %lld\n", gen_vco_clk); - rem = 0; - if (frac_n_mode) { - sdm_cfg0 = (0x0 << 5); - sdm_cfg0 |= (0x0 & 0x3f); - sdm_cfg1 = (div_s64(div_fbx1000, 1000) & 0x3f) - 1; - sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem); - sdm_cfg2 = rem; - } else { - sdm_cfg0 = (0x1 << 5); - sdm_cfg0 |= (div_s64(div_fbx1000, 1000) & 0x3f) - 1; - sdm_cfg1 = (0x0 & 0x3f); - sdm_cfg2 = 0; - sdm_cfg3 = 0; - } - - pr_debug("sdm_cfg0=%lld\n", sdm_cfg0); - pr_debug("sdm_cfg1=%lld\n", sdm_cfg1); - pr_debug("sdm_cfg2=%lld\n", sdm_cfg2); - pr_debug("sdm_cfg3=%lld\n", sdm_cfg3); - - cal_cfg11 = div_s64_rem(gen_vco_clk, 256 * 1000000, &rem); - cal_cfg10 = rem / 1000000; - pr_debug("cal_cfg10=%lld, cal_cfg11=%lld\n", cal_cfg10, cal_cfg11); - - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d); - - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1, (u32)(sdm_cfg1 & 0xff)); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2, (u32)(sdm_cfg2 & 0xff)); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3, (u32)(sdm_cfg3 & 0xff)); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00); - - /* Add hardware recommended delay for correct PLL configuration */ - udelay(1); - - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, (u32)refclk_cfg); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0, (u32)sdm_cfg0); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10, (u32)(cal_cfg10 & 0xff)); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11, (u32)(cal_cfg11 & 0xff)); - MDSS_PLL_REG_W(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20); - -error: - mdss_pll_resource_enable(dsi_pll_res, false); - return rc; -} - -static long vco_round_rate(struct clk *c, unsigned long rate) -{ - unsigned long rrate = rate; - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - - if (rate < vco->min_rate) - rrate = vco->min_rate; - if (rate > vco->max_rate) - rrate = vco->max_rate; - - return rrate; -} - -static unsigned long vco_get_rate(struct clk *c) -{ - u32 sdm0, doubler, sdm_byp_div; - u64 vco_rate; - u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3; - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - u64 ref_clk = vco->ref_clk_rate; - int rc; - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return rc; - } - - /* Check to see if the ref clk doubler is enabled */ - doubler = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG) & BIT(0); - ref_clk += (doubler * vco->ref_clk_rate); - - /* see if it is integer mode or sdm mode */ - sdm0 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0); - if (sdm0 & BIT(6)) { - /* integer mode */ - sdm_byp_div = (MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1; - vco_rate = ref_clk * sdm_byp_div; - } else { - /* sdm mode */ - sdm_dc_off = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF; - pr_debug("sdm_dc_off = %d\n", sdm_dc_off); - sdm2 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF; - sdm3 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, - DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF; - sdm_freq_seed = (sdm3 << 8) | sdm2; - pr_debug("sdm_freq_seed = %d\n", sdm_freq_seed); - - vco_rate = (ref_clk * (sdm_dc_off + 1)) + - mult_frac(ref_clk, sdm_freq_seed, BIT(16)); - pr_debug("vco rate = %lld", vco_rate); - } - - pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate); - - mdss_pll_resource_enable(dsi_pll_res, false); - - return (unsigned long)vco_rate; -} - -static enum handoff vco_handoff(struct clk *c) -{ - int rc; - enum handoff ret = HANDOFF_DISABLED_CLK; - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - rc = mdss_pll_resource_enable(dsi_pll_res, true); - if (rc) { - pr_err("Failed to enable mdss dsi pll resources\n"); - return ret; - } - - if (dsi_pll_lock_status(dsi_pll_res)) { - dsi_pll_res->handoff_resources = true; - dsi_pll_res->pll_on = true; - c->rate = vco_get_rate(c); - ret = HANDOFF_ENABLED_CLK; - } else { - mdss_pll_resource_enable(dsi_pll_res, false); - } - - return ret; -} - -static int vco_prepare(struct clk *c) -{ - int rc = 0; - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - if (!dsi_pll_res) { - pr_err("Dsi pll resources are not available\n"); - return -EINVAL; - } - - if ((dsi_pll_res->vco_cached_rate != 0) - && (dsi_pll_res->vco_cached_rate == c->rate)) { - rc = vco_set_rate(c, dsi_pll_res->vco_cached_rate); - if (rc) { - pr_err("vco_set_rate failed. rc=%d\n", rc); - goto error; - } - } - - rc = dsi_pll_enable(c); - -error: - return rc; -} - -static void vco_unprepare(struct clk *c) -{ - struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *dsi_pll_res = vco->priv; - - if (!dsi_pll_res) { - pr_err("Dsi pll resources are not available\n"); - return; - } - - dsi_pll_res->vco_cached_rate = c->rate; - dsi_pll_disable(c); -} - /* Op structures */ static struct clk_ops clk_ops_dsi_vco = { - .set_rate = vco_set_rate, + .set_rate = vco_set_rate_hpm, .round_rate = vco_round_rate, .handoff = vco_handoff, .prepare = vco_prepare, @@ -960,7 +197,7 @@ static struct dsi_pll_vco_clk dsi_vco_clk_8974 = { }, }; -static struct div_clk analog_postdiv_clk = { +static struct div_clk analog_postdiv_clk_8974 = { .data = { .max_div = 255, .min_div = 1, @@ -971,11 +208,11 @@ static struct div_clk analog_postdiv_clk = { .dbg_name = "analog_postdiv_clk", .ops = &analog_postdiv_clk_ops, .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(analog_postdiv_clk.c), + CLK_INIT(analog_postdiv_clk_8974.c), }, }; -static struct div_clk indirect_path_div2_clk = { +static struct div_clk indirect_path_div2_clk_8974 = { .ops = &fixed_2div_ops, .data = { .div = 2, @@ -983,15 +220,15 @@ static struct div_clk indirect_path_div2_clk = { .max_div = 2, }, .c = { - .parent = &analog_postdiv_clk.c, + .parent = &analog_postdiv_clk_8974.c, .dbg_name = "indirect_path_div2_clk", .ops = &clk_ops_div, .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(indirect_path_div2_clk.c), + CLK_INIT(indirect_path_div2_clk_8974.c), }, }; -struct div_clk pixel_clk_src = { +static struct div_clk pixel_clk_src_8974 = { .data = { .max_div = 255, .min_div = 1, @@ -999,105 +236,72 @@ struct div_clk pixel_clk_src = { .ops = &digital_postdiv_ops, .c = { .parent = &dsi_vco_clk_8974.c, - .dbg_name = "pixel_clk_src", + .dbg_name = "pixel_clk_src_8974", .ops = &pixel_clk_src_ops, .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(pixel_clk_src.c), + CLK_INIT(pixel_clk_src_8974.c), }, }; -struct mux_clk byte_mux = { +static struct mux_clk byte_mux_8974 = { .num_parents = 2, .parents = (struct clk_src[]){ {&dsi_vco_clk_8974.c, 0}, - {&indirect_path_div2_clk.c, 1}, + {&indirect_path_div2_clk_8974.c, 1}, }, .ops = &byte_mux_ops, .c = { .parent = &dsi_vco_clk_8974.c, - .dbg_name = "byte_mux", + .dbg_name = "byte_mux_8974", .ops = &byte_mux_clk_ops, - CLK_INIT(byte_mux.c), + CLK_INIT(byte_mux_8974.c), }, }; -struct div_clk byte_clk_src = { +static struct div_clk byte_clk_src_8974 = { .ops = &fixed_4div_ops, .data = { .min_div = 4, .max_div = 4, }, .c = { - .parent = &byte_mux.c, - .dbg_name = "byte_clk_src", + .parent = &byte_mux_8974.c, + .dbg_name = "byte_clk_src_8974", .ops = &byte_clk_src_ops, - CLK_INIT(byte_clk_src.c), - }, -}; - -struct dsi_pll_vco_clk dsi_vco_clk_8916 = { - .ref_clk_rate = 19200000, - .min_rate = 350000000, - .max_rate = 750000000, - .pll_en_seq_cnt = 7, - .pll_enable_seqs[0] = dsi_pll_enable_seq_m, - .pll_enable_seqs[1] = dsi_pll_enable_seq_m, - .pll_enable_seqs[2] = dsi_pll_enable_seq_d, - .pll_enable_seqs[3] = dsi_pll_enable_seq_d, - .pll_enable_seqs[4] = dsi_pll_enable_seq_f1, - .pll_enable_seqs[5] = dsi_pll_enable_seq_c, - .pll_enable_seqs[6] = dsi_pll_enable_seq_e, - .lpfr_lut_size = 10, - .lpfr_lut = lpfr_lut_struct, - .c = { - .dbg_name = "dsi_vco_clk_8916", - .ops = &clk_ops_dsi_vco, - CLK_INIT(dsi_vco_clk_8916.c), + CLK_INIT(byte_clk_src_8974.c), }, }; static struct clk_lookup mdss_dsi_pllcc_8974[] = { - CLK_LOOKUP_OF("pixel_src", pixel_clk_src, + CLK_LOOKUP_OF("pixel_src", pixel_clk_src_8974, "fd8c0000.qcom,mmsscc-mdss"), - CLK_LOOKUP_OF("byte_src", byte_clk_src, + CLK_LOOKUP_OF("byte_src", byte_clk_src_8974, "fd8c0000.qcom,mmsscc-mdss"), }; -static struct clk_lookup mdss_dsi_pllcc_8916[] = { - CLK_LIST(pixel_clk_src), - CLK_LIST(byte_clk_src), -}; - -int dsi_pll_clock_register(struct platform_device *pdev, +int dsi_pll_clock_register_hpm(struct platform_device *pdev, struct mdss_pll_resources *pll_res) { int rc; - const char *compatible_stream = NULL; - int compat_len = 0; - if (!pdev || !pll_res || !pdev->dev.of_node) { + if (!pdev || !pdev->dev.of_node) { pr_err("Invalid input parameters\n"); return -EINVAL; } - compatible_stream = of_get_property(pdev->dev.of_node, "compatible", - &compat_len); - if (!compatible_stream || (compat_len <= 0)) { - pr_err("Invalid compatible string\n"); - return -EINVAL; - } - if (!pll_res || !pll_res->pll_base) { - pr_err("Invalid input parameters\n"); + pr_err("Invalid PLL resources\n"); return -EPROBE_DEFER; } /* Set client data to mux, div and vco clocks */ - byte_clk_src.priv = pll_res; - pixel_clk_src.priv = pll_res; - byte_mux.priv = pll_res; - indirect_path_div2_clk.priv = pll_res; - analog_postdiv_clk.priv = pll_res; + byte_clk_src_8974.priv = pll_res; + pixel_clk_src_8974.priv = pll_res; + byte_mux_8974.priv = pll_res; + indirect_path_div2_clk_8974.priv = pll_res; + analog_postdiv_clk_8974.priv = pll_res; + dsi_vco_clk_8974.priv = pll_res; + pll_res->vco_delay = VCO_DELAY_USEC; /* Set clock source operations */ pixel_clk_src_ops = clk_ops_slave_div; @@ -1112,20 +316,7 @@ int dsi_pll_clock_register(struct platform_device *pdev, byte_mux_clk_ops = clk_ops_gen_mux; byte_mux_clk_ops.prepare = dsi_pll_mux_prepare; - if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8916")) { - dsi_vco_clk_8916.priv = pll_res; - analog_postdiv_clk.c.parent = &dsi_vco_clk_8916.c; - byte_mux.parents[0] = (struct clk_src) {&dsi_vco_clk_8916.c, 0}; - byte_mux.c.parent = &dsi_vco_clk_8916.c; - pixel_clk_src.c.parent = &dsi_vco_clk_8916.c; - rc = of_msm_clock_register(pdev->dev.of_node, - mdss_dsi_pllcc_8916, ARRAY_SIZE(mdss_dsi_pllcc_8916)); - if (rc) { - pr_err("Clock register failed\n"); - rc = -EPROBE_DEFER; - } - } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8974")) { - dsi_vco_clk_8974.priv = pll_res; + if (pll_res->target_id == MDSS_PLL_TARGET_8974) { rc = of_msm_clock_register(pdev->dev.of_node, mdss_dsi_pllcc_8974, ARRAY_SIZE(mdss_dsi_pllcc_8974)); if (rc) { @@ -1133,7 +324,7 @@ int dsi_pll_clock_register(struct platform_device *pdev, rc = -EPROBE_DEFER; } } else { - pr_err("Invalid compatible string\n"); + pr_err("Invalid target ID\n"); rc = -EINVAL; } diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c new file mode 100644 index 000000000000..c2520d0ca1a5 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2012-2014, 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/clk/msm-clk.h> +#include <linux/clk/msm-clock-generic.h> +#include <linux/clk/msm-clk-provider.h> +#include <dt-bindings/clock/msm-clocks-8916.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" + +#define VCO_DELAY_USEC 1000 + +static struct clk_div_ops fixed_2div_ops; +static struct clk_ops byte_mux_clk_ops; +static struct clk_ops pixel_clk_src_ops; +static struct clk_ops byte_clk_src_ops; +static struct clk_ops analog_postdiv_clk_ops; +static struct lpfr_cfg lpfr_lut_struct[] = { + {479500000, 8}, + {480000000, 11}, + {575500000, 8}, + {576000000, 12}, + {610500000, 8}, + {659500000, 9}, + {671500000, 10}, + {672000000, 14}, + {708500000, 10}, + {750000000, 11}, +}; + +static int vco_set_rate_lpm(struct clk *c, unsigned long rate) +{ + int rc; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + /* + * DSI PLL software reset. Add HW recommended delays after toggling + * the software reset bit off and back on. + */ + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); + udelay(1000); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); + udelay(1000); + + rc = vco_set_rate(vco, rate); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +static int dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res) +{ + int pll_locked = 0; + + /* + * DSI PLL software reset. Add HW recommended delays after toggling + * the software reset bit off and back on. + */ + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); + ndelay(500); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34); + ndelay(500); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); + ndelay(500); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); + ndelay(500); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); + ndelay(500); + + /* DSI PLL toggle lock detect setting */ + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x04); + ndelay(500); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05); + udelay(512); + + pll_locked = dsi_pll_lock_status(dsi_pll_res); + + if (pll_locked) + pr_debug("PLL Locked\n"); + else + pr_err("PLL failed to lock\n"); + + return pll_locked ? 0 : -EINVAL; +} + +/* Op structures */ + +static struct clk_ops clk_ops_dsi_vco = { + .set_rate = vco_set_rate_lpm, + .round_rate = vco_round_rate, + .handoff = vco_handoff, + .prepare = vco_prepare, + .unprepare = vco_unprepare, +}; + + +static struct clk_div_ops fixed_4div_ops = { + .set_div = fixed_4div_set_div, + .get_div = fixed_4div_get_div, +}; + +static struct clk_div_ops analog_postdiv_ops = { + .set_div = analog_set_div, + .get_div = analog_get_div, +}; + +static struct clk_div_ops digital_postdiv_ops = { + .set_div = digital_set_div, + .get_div = digital_get_div, +}; + +static struct clk_mux_ops byte_mux_ops = { + .set_mux_sel = set_byte_mux_sel, + .get_mux_sel = get_byte_mux_sel, +}; + +static struct dsi_pll_vco_clk dsi_vco_clk_8916 = { + .ref_clk_rate = 19200000, + .min_rate = 350000000, + .max_rate = 750000000, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8916, + .lpfr_lut_size = 10, + .lpfr_lut = lpfr_lut_struct, + .c = { + .dbg_name = "dsi_vco_clk_8916", + .ops = &clk_ops_dsi_vco, + CLK_INIT(dsi_vco_clk_8916.c), + }, +}; + +static struct div_clk analog_postdiv_clk_8916 = { + .data = { + .max_div = 255, + .min_div = 1, + }, + .ops = &analog_postdiv_ops, + .c = { + .parent = &dsi_vco_clk_8916.c, + .dbg_name = "analog_postdiv_clk", + .ops = &analog_postdiv_clk_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(analog_postdiv_clk_8916.c), + }, +}; + +static struct div_clk indirect_path_div2_clk_8916 = { + .ops = &fixed_2div_ops, + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &analog_postdiv_clk_8916.c, + .dbg_name = "indirect_path_div2_clk", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(indirect_path_div2_clk_8916.c), + }, +}; + +static struct div_clk pixel_clk_src = { + .data = { + .max_div = 255, + .min_div = 1, + }, + .ops = &digital_postdiv_ops, + .c = { + .parent = &dsi_vco_clk_8916.c, + .dbg_name = "pixel_clk_src_8916", + .ops = &pixel_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(pixel_clk_src.c), + }, +}; + +static struct mux_clk byte_mux_8916 = { + .num_parents = 2, + .parents = (struct clk_src[]){ + {&dsi_vco_clk_8916.c, 0}, + {&indirect_path_div2_clk_8916.c, 1}, + }, + .ops = &byte_mux_ops, + .c = { + .parent = &dsi_vco_clk_8916.c, + .dbg_name = "byte_mux_8916", + .ops = &byte_mux_clk_ops, + CLK_INIT(byte_mux_8916.c), + }, +}; + +static struct div_clk byte_clk_src = { + .ops = &fixed_4div_ops, + .data = { + .min_div = 4, + .max_div = 4, + }, + .c = { + .parent = &byte_mux_8916.c, + .dbg_name = "byte_clk_src_8916", + .ops = &byte_clk_src_ops, + CLK_INIT(byte_clk_src.c), + }, +}; + +static struct clk_lookup mdss_dsi_pllcc_8916[] = { + CLK_LIST(pixel_clk_src), + CLK_LIST(byte_clk_src), +}; + +int dsi_pll_clock_register_lpm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc; + + if (!pdev || !pdev->dev.of_node) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + if (!pll_res || !pll_res->pll_base) { + pr_err("Invalid PLL resources\n"); + return -EPROBE_DEFER; + } + + /* Set client data to mux, div and vco clocks */ + byte_clk_src.priv = pll_res; + pixel_clk_src.priv = pll_res; + byte_mux_8916.priv = pll_res; + indirect_path_div2_clk_8916.priv = pll_res; + analog_postdiv_clk_8916.priv = pll_res; + dsi_vco_clk_8916.priv = pll_res; + pll_res->vco_delay = VCO_DELAY_USEC; + + /* Set clock source operations */ + pixel_clk_src_ops = clk_ops_slave_div; + pixel_clk_src_ops.prepare = dsi_pll_div_prepare; + + analog_postdiv_clk_ops = clk_ops_div; + analog_postdiv_clk_ops.prepare = dsi_pll_div_prepare; + + byte_clk_src_ops = clk_ops_div; + byte_clk_src_ops.prepare = dsi_pll_div_prepare; + + byte_mux_clk_ops = clk_ops_gen_mux; + byte_mux_clk_ops.prepare = dsi_pll_mux_prepare; + + if (pll_res->target_id == MDSS_PLL_TARGET_8916) { + rc = of_msm_clock_register(pdev->dev.of_node, + mdss_dsi_pllcc_8916, ARRAY_SIZE(mdss_dsi_pllcc_8916)); + if (rc) { + pr_err("Clock register failed\n"); + rc = -EPROBE_DEFER; + } + } else { + pr_err("Invalid target ID\n"); + rc = -EINVAL; + } + + if (!rc) + pr_info("Registered DSI PLL clocks successfully\n"); + + return rc; +} diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-util.c new file mode 100644 index 000000000000..d195f676edf2 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-util.c @@ -0,0 +1,570 @@ +/* Copyright (c) 2012-2014, 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/delay.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" + +#define DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0) +#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004) +#define DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x0008) +#define DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C) +#define DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x0010) +#define DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x0014) +#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024) +#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028) +#define DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x002C) +#define DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x0030) +#define DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x0034) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x0078) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x007C) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x0080) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098) +#define DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x009C) +#define DSI_PHY_PLL_UNIPHY_PLL_STATUS (0x00C0) + +#define DSI_PLL_POLL_MAX_READS 10 +#define DSI_PLL_POLL_TIMEOUT_US 50 + +int set_byte_mux_sel(struct mux_clk *clk, int sel) +{ + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + pr_debug("byte mux set to %s mode\n", sel ? "indirect" : "direct"); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, (sel << 1)); + + return 0; +} + +int get_byte_mux_sel(struct mux_clk *clk) +{ + int mux_mode, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + mux_mode = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG) & BIT(1); + + pr_debug("byte mux mode = %s", mux_mode ? "indirect" : "direct"); + mdss_pll_resource_enable(dsi_pll_res, false); + + return !!mux_mode; +} + +int dsi_pll_div_prepare(struct clk *c) +{ + struct div_clk *div = to_div_clk(c); + /* Restore the divider's value */ + return div->ops->set_div(div, div->data.div); +} + +int dsi_pll_mux_prepare(struct clk *c) +{ + struct mux_clk *mux = to_mux_clk(c); + int i, rc, sel = 0; + struct mdss_pll_resources *dsi_pll_res = mux->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + for (i = 0; i < mux->num_parents; i++) + if (mux->parents[i].src == c->parent) { + sel = mux->parents[i].sel; + break; + } + + if (i == mux->num_parents) { + pr_err("Failed to select the parent clock\n"); + rc = -EINVAL; + goto error; + } + + /* Restore the mux source select value */ + rc = mux->ops->set_mux_sel(mux, sel); + +error: + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +int fixed_4div_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1)); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +int fixed_4div_get_div(struct div_clk *clk) +{ + int div = 0, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG); + + mdss_pll_resource_enable(dsi_pll_res, false); + return div + 1; +} + +int digital_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, (div - 1)); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +int digital_get_div(struct div_clk *clk) +{ + int div = 0, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG); + + mdss_pll_resource_enable(dsi_pll_res, false); + return div + 1; +} + +int analog_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, div - 1); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +int analog_get_div(struct div_clk *clk) +{ + int div = 0, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(clk->priv, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG) + 1; + + mdss_pll_resource_enable(dsi_pll_res, false); + + return div; +} + +int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res) +{ + u32 status; + int pll_locked; + + /* poll for PLL ready status */ + if (readl_poll_timeout_noirq((dsi_pll_res->pll_base + + DSI_PHY_PLL_UNIPHY_PLL_STATUS), + status, + ((status & BIT(0)) == 1), + DSI_PLL_POLL_MAX_READS, + DSI_PLL_POLL_TIMEOUT_US)) { + pr_debug("DSI PLL status=%x failed to Lock\n", status); + pll_locked = 0; + } else { + pll_locked = 1; + } + + return pll_locked; +} + +int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate) +{ + s64 vco_clk_rate = rate; + s32 rem; + s64 refclk_cfg, frac_n_mode, ref_doubler_en_b; + s64 ref_clk_to_pll, div_fbx1000, frac_n_value; + s64 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3; + s64 gen_vco_clk, cal_cfg10, cal_cfg11; + u32 res; + int i; + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + /* Configure the Loop filter resistance */ + for (i = 0; i < vco->lpfr_lut_size; i++) + if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate) + break; + if (i == vco->lpfr_lut_size) { + pr_err("unable to get loop filter resistance. vco=%ld\n", rate); + return -EINVAL; + } + res = vco->lpfr_lut[i].r; + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG, res); + + /* Loop filter capacitance values : c1 and c2 */ + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15); + + div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem); + if (rem) { + refclk_cfg = 0x1; + frac_n_mode = 1; + ref_doubler_en_b = 0; + } else { + refclk_cfg = 0x0; + frac_n_mode = 0; + ref_doubler_en_b = 1; + } + + pr_debug("refclk_cfg = %lld\n", refclk_cfg); + + ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (refclk_cfg)) + + (ref_doubler_en_b * vco->ref_clk_rate)); + div_fbx1000 = div_s64((vco_clk_rate * 1000), ref_clk_to_pll); + + div_s64_rem(div_fbx1000, 1000, &rem); + frac_n_value = div_s64((rem * (1 << 16)), 1000); + gen_vco_clk = div_s64(div_fbx1000 * ref_clk_to_pll, 1000); + + pr_debug("ref_clk_to_pll = %lld\n", ref_clk_to_pll); + pr_debug("div_fb = %lld\n", div_fbx1000); + pr_debug("frac_n_value = %lld\n", frac_n_value); + + pr_debug("Generated VCO Clock: %lld\n", gen_vco_clk); + rem = 0; + if (frac_n_mode) { + sdm_cfg0 = (0x0 << 5); + sdm_cfg0 |= (0x0 & 0x3f); + sdm_cfg1 = (div_s64(div_fbx1000, 1000) & 0x3f) - 1; + sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem); + sdm_cfg2 = rem; + } else { + sdm_cfg0 = (0x1 << 5); + sdm_cfg0 |= (div_s64(div_fbx1000, 1000) & 0x3f) - 1; + sdm_cfg1 = (0x0 & 0x3f); + sdm_cfg2 = 0; + sdm_cfg3 = 0; + } + + pr_debug("sdm_cfg0=%lld\n", sdm_cfg0); + pr_debug("sdm_cfg1=%lld\n", sdm_cfg1); + pr_debug("sdm_cfg2=%lld\n", sdm_cfg2); + pr_debug("sdm_cfg3=%lld\n", sdm_cfg3); + + cal_cfg11 = div_s64_rem(gen_vco_clk, 256 * 1000000, &rem); + cal_cfg10 = rem / 1000000; + pr_debug("cal_cfg10=%lld, cal_cfg11=%lld\n", cal_cfg10, cal_cfg11); + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d); + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1, (u32)(sdm_cfg1 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2, (u32)(sdm_cfg2 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3, (u32)(sdm_cfg3 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00); + + /* Add hardware recommended delay for correct PLL configuration */ + if (dsi_pll_res->vco_delay) + udelay(dsi_pll_res->vco_delay); + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, (u32)refclk_cfg); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0, (u32)sdm_cfg0); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10, (u32)(cal_cfg10 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11, (u32)(cal_cfg11 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20); + + return 0; +} + +unsigned long vco_get_rate(struct clk *c) +{ + u32 sdm0, doubler, sdm_byp_div; + u64 vco_rate; + u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + u64 ref_clk = vco->ref_clk_rate; + int rc; + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + /* Check to see if the ref clk doubler is enabled */ + doubler = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG) & BIT(0); + ref_clk += (doubler * vco->ref_clk_rate); + + /* see if it is integer mode or sdm mode */ + sdm0 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0); + if (sdm0 & BIT(6)) { + /* integer mode */ + sdm_byp_div = (MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1; + vco_rate = ref_clk * sdm_byp_div; + } else { + /* sdm mode */ + sdm_dc_off = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF; + pr_debug("sdm_dc_off = %d\n", sdm_dc_off); + sdm2 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF; + sdm3 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF; + sdm_freq_seed = (sdm3 << 8) | sdm2; + pr_debug("sdm_freq_seed = %d\n", sdm_freq_seed); + + vco_rate = (ref_clk * (sdm_dc_off + 1)) + + mult_frac(ref_clk, sdm_freq_seed, BIT(16)); + pr_debug("vco rate = %lld", vco_rate); + } + + pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate); + + mdss_pll_resource_enable(dsi_pll_res, false); + + return (unsigned long)vco_rate; +} + +static int dsi_pll_enable(struct clk *c) +{ + int i, rc; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + /* Try all enable sequences until one succeeds */ + for (i = 0; i < vco->pll_en_seq_cnt; i++) { + rc = vco->pll_enable_seqs[i](dsi_pll_res); + pr_debug("DSI PLL %s after sequence #%d\n", + rc ? "unlocked" : "locked", i + 1); + if (!rc) + break; + } + + if (rc) { + mdss_pll_resource_enable(dsi_pll_res, false); + pr_err("DSI PLL failed to lock\n"); + } + dsi_pll_res->pll_on = true; + + return rc; +} + +static void dsi_pll_disable(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + if (!dsi_pll_res->pll_on && + mdss_pll_resource_enable(dsi_pll_res, true)) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return; + } + + dsi_pll_res->handoff_resources = false; + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00); + + mdss_pll_resource_enable(dsi_pll_res, false); + dsi_pll_res->pll_on = false; + + pr_debug("DSI PLL Disabled\n"); + return; +} + +long vco_round_rate(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + + if (rate < vco->min_rate) + rrate = vco->min_rate; + if (rate > vco->max_rate) + rrate = vco->max_rate; + + return rrate; +} + +enum handoff vco_handoff(struct clk *c) +{ + int rc; + enum handoff ret = HANDOFF_DISABLED_CLK; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return ret; + } + + if (dsi_pll_lock_status(dsi_pll_res)) { + dsi_pll_res->handoff_resources = true; + dsi_pll_res->pll_on = true; + c->rate = vco_get_rate(c); + ret = HANDOFF_ENABLED_CLK; + } else { + mdss_pll_resource_enable(dsi_pll_res, false); + } + + return ret; +} + +int vco_prepare(struct clk *c) +{ + int rc = 0; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + if (!dsi_pll_res) { + pr_err("Dsi pll resources are not available\n"); + return -EINVAL; + } + + if ((dsi_pll_res->vco_cached_rate != 0) + && (dsi_pll_res->vco_cached_rate == c->rate)) { + rc = c->ops->set_rate(c, dsi_pll_res->vco_cached_rate); + if (rc) { + pr_err("vco_set_rate failed. rc=%d\n", rc); + goto error; + } + } + + rc = dsi_pll_enable(c); + +error: + return rc; +} + +void vco_unprepare(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + if (!dsi_pll_res) { + pr_err("Dsi pll resources are not available\n"); + return; + } + + dsi_pll_res->vco_cached_rate = c->rate; + dsi_pll_disable(c); +} + diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll.h b/drivers/clk/msm/mdss/mdss-dsi-pll.h index 15ca2fbd5281..53a6afac2cc6 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll.h +++ b/drivers/clk/msm/mdss/mdss-dsi-pll.h @@ -15,6 +15,11 @@ #define MAX_DSI_PLL_EN_SEQS 10 +#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020) +#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064) +#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070) + struct lpfr_cfg { unsigned long vco_rate; u32 r; @@ -35,6 +40,31 @@ struct dsi_pll_vco_clk { (struct mdss_pll_resources *dsi_pll_Res); }; -int dsi_pll_clock_register(struct platform_device *pdev, +static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk) +{ + return container_of(clk, struct dsi_pll_vco_clk, c); +} + +int dsi_pll_clock_register_hpm(struct platform_device *pdev, struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_lpm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int set_byte_mux_sel(struct mux_clk *clk, int sel); +int get_byte_mux_sel(struct mux_clk *clk); +int dsi_pll_div_prepare(struct clk *c); +int dsi_pll_mux_prepare(struct clk *c); +int fixed_4div_set_div(struct div_clk *clk, int div); +int fixed_4div_get_div(struct div_clk *clk); +int digital_set_div(struct div_clk *clk, int div); +int digital_get_div(struct div_clk *clk); +int analog_set_div(struct div_clk *clk, int div); +int analog_get_div(struct div_clk *clk); +int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res); +int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate); +unsigned long vco_get_rate(struct clk *c); +long vco_round_rate(struct clk *c, unsigned long rate); +enum handoff vco_handoff(struct clk *c); +int vco_prepare(struct clk *c); +void vco_unprepare(struct clk *c); #endif diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c index 60b742e2cba3..25eda2ede865 100644 --- a/drivers/clk/msm/mdss/mdss-pll.c +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -110,15 +110,19 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, goto err; } - if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8916") || - !strcmp(compatible_stream, "qcom,mdss_dsi_pll_8974")) - pll_res->pll_interface_type = MDSS_DSI_PLL; - else if (!strcmp(compatible_stream, "qcom,mdss_edp_pll")) + if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8916")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_LPM; + pll_res->target_id = MDSS_PLL_TARGET_8916; + } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8974")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_HPM; + pll_res->target_id = MDSS_PLL_TARGET_8974; + } else if (!strcmp(compatible_stream, "qcom,mdss_edp_pll")) { pll_res->pll_interface_type = MDSS_EDP_PLL; - else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll")) + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll")) { pll_res->pll_interface_type = MDSS_HDMI_PLL; - else + } else { goto err; + } return rc; @@ -139,8 +143,11 @@ static int mdss_pll_clock_register(struct platform_device *pdev, } switch (pll_res->pll_interface_type) { - case MDSS_DSI_PLL: - rc = dsi_pll_clock_register(pdev, pll_res); + case MDSS_DSI_PLL_LPM: + rc = dsi_pll_clock_register_lpm(pdev, pll_res); + break; + case MDSS_DSI_PLL_HPM: + rc = dsi_pll_clock_register_hpm(pdev, pll_res); break; case MDSS_EDP_PLL: rc = edp_pll_clock_register(pdev, pll_res); diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h index 27b7b4fd4d1b..9da57f7f96f3 100644 --- a/drivers/clk/msm/mdss/mdss-pll.h +++ b/drivers/clk/msm/mdss/mdss-pll.h @@ -21,12 +21,18 @@ #define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset)) enum { - MDSS_DSI_PLL, + MDSS_DSI_PLL_LPM, + MDSS_DSI_PLL_HPM, MDSS_EDP_PLL, MDSS_HDMI_PLL, MDSS_UNKNOWN_PLL, }; +enum { + MDSS_PLL_TARGET_8974, + MDSS_PLL_TARGET_8916, +}; + struct mdss_pll_resources { /* Pll specific resources like GPIO, power supply, clocks, etc*/ @@ -46,6 +52,15 @@ struct mdss_pll_resources { u32 pll_interface_type; /* + * Target ID. Used in pll_register API for valid target check before + * registering the PLL clocks. + */ + u32 target_id; + + /* HW recommended delay during configuration of vco clock rate */ + u32 vco_delay; + + /* * Keep track to resource status to avoid updating same status for the * pll from different paths */ |
