From 7a625cd32dbae45425527b1ca146348211624ab5 Mon Sep 17 00:00:00 2001 From: Padmanabhan Komanduru Date: Tue, 25 Feb 2014 23:05:03 +0530 Subject: clk: qcom: mdss: add DSI PLL clock driver support for 8916 This change adds the DSI PLL clock driver support for 8916. In addition, it adds support of DSI PLL programming of different MDSS revisions using the same DSI PLL driver. Also rename the compatibility string of the DSI PLL handle so that the detection and support of DSI PLL driver for 8974 and 8916 happens dynamically. Change-Id: I169ebeaf23e4be8ff4b533fce1057144edd8b692 Signed-off-by: Jeevan Shriram Signed-off-by: Padmanabhan Komanduru --- drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c | 344 ++++++++++++++++++++++++++---- drivers/clk/msm/mdss/mdss-pll.c | 6 +- 2 files changed, 308 insertions(+), 42 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c index 685c0863f7f1..8e6da378c304 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c @@ -22,6 +22,7 @@ #include #include +#include #include "mdss-pll.h" #include "mdss-dsi-pll.h" @@ -71,12 +72,25 @@ #define DSI_PLL_POLL_MAX_READS 10 #define DSI_PLL_POLL_TIMEOUT_US 50 +#define DSI_PLL_SEQ_M_MAX_COUNTER 7 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}, +}; int set_byte_mux_sel(struct mux_clk *clk, int sel) { @@ -297,6 +311,17 @@ static void dsi_pll_software_reset(struct mdss_pll_resources *dsi_pll_res) 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; @@ -374,6 +399,196 @@ 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; @@ -737,26 +952,15 @@ static struct dsi_pll_vco_clk dsi_vco_clk_8974 = { .pll_enable_seqs[1] = dsi_pll_enable_seq_8974, .pll_enable_seqs[2] = dsi_pll_enable_seq_8974, .lpfr_lut_size = 10, - .lpfr_lut = (struct lpfr_cfg[]){ - {479500000, 8}, - {480000000, 11}, - {575500000, 8}, - {576000000, 12}, - {610500000, 8}, - {659500000, 9}, - {671500000, 10}, - {672000000, 14}, - {708500000, 10}, - {750000000, 11}, - }, + .lpfr_lut = lpfr_lut_struct, .c = { - .dbg_name = "dsi_vco_clk", + .dbg_name = "dsi_vco_clk_8974", .ops = &clk_ops_dsi_vco, CLK_INIT(dsi_vco_clk_8974.c), }, }; -static struct div_clk analog_postdiv_clk_8974 = { +static struct div_clk analog_postdiv_clk = { .data = { .max_div = 255, .min_div = 1, @@ -767,11 +971,11 @@ static struct div_clk analog_postdiv_clk_8974 = { .dbg_name = "analog_postdiv_clk", .ops = &analog_postdiv_clk_ops, .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(analog_postdiv_clk_8974.c), + CLK_INIT(analog_postdiv_clk.c), }, }; -static struct div_clk indirect_path_div2_clk_8974 = { +static struct div_clk indirect_path_div2_clk = { .ops = &fixed_2div_ops, .data = { .div = 2, @@ -779,15 +983,15 @@ static struct div_clk indirect_path_div2_clk_8974 = { .max_div = 2, }, .c = { - .parent = &analog_postdiv_clk_8974.c, + .parent = &analog_postdiv_clk.c, .dbg_name = "indirect_path_div2_clk", .ops = &clk_ops_div, .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(indirect_path_div2_clk_8974.c), + CLK_INIT(indirect_path_div2_clk.c), }, }; -struct div_clk pixel_clk_src_8974 = { +struct div_clk pixel_clk_src = { .data = { .max_div = 255, .min_div = 1, @@ -798,63 +1002,102 @@ struct div_clk pixel_clk_src_8974 = { .dbg_name = "pixel_clk_src", .ops = &pixel_clk_src_ops, .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(pixel_clk_src_8974.c), + CLK_INIT(pixel_clk_src.c), }, }; -struct mux_clk byte_mux_8974 = { +struct mux_clk byte_mux = { .num_parents = 2, .parents = (struct clk_src[]){ {&dsi_vco_clk_8974.c, 0}, - {&indirect_path_div2_clk_8974.c, 1}, + {&indirect_path_div2_clk.c, 1}, }, .ops = &byte_mux_ops, .c = { .parent = &dsi_vco_clk_8974.c, .dbg_name = "byte_mux", .ops = &byte_mux_clk_ops, - CLK_INIT(byte_mux_8974.c), + CLK_INIT(byte_mux.c), }, }; -struct div_clk byte_clk_src_8974 = { +struct div_clk byte_clk_src = { .ops = &fixed_4div_ops, .data = { .min_div = 4, .max_div = 4, }, .c = { - .parent = &byte_mux_8974.c, + .parent = &byte_mux.c, .dbg_name = "byte_clk_src", .ops = &byte_clk_src_ops, - CLK_INIT(byte_clk_src_8974.c), + 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), }, }; static struct clk_lookup mdss_dsi_pllcc_8974[] = { - CLK_LOOKUP_OF("pixel_src", pixel_clk_src_8974, + CLK_LOOKUP_OF("pixel_src", pixel_clk_src, "fd8c0000.qcom,mmsscc-mdss"), - CLK_LOOKUP_OF("byte_src", byte_clk_src_8974, + CLK_LOOKUP_OF("byte_src", byte_clk_src, "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, 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) { + 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("Invalide input parameters\n"); + pr_err("Invalid input parameters\n"); return -EPROBE_DEFER; } /* Set client data to mux, div and vco clocks */ - byte_clk_src_8974.priv = pll_res; - byte_mux_8974.priv = pll_res; - pixel_clk_src_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; + 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; /* Set clock source operations */ pixel_clk_src_ops = clk_ops_slave_div; @@ -869,12 +1112,33 @@ 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; - rc = of_msm_clock_register(pdev->dev.of_node, mdss_dsi_pllcc_8974, - ARRAY_SIZE(mdss_dsi_pllcc_8974)); - if (rc) { - pr_err("Clock register failed\n"); - rc = -EPROBE_DEFER; + 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; + rc = of_msm_clock_register(pdev->dev.of_node, + mdss_dsi_pllcc_8974, ARRAY_SIZE(mdss_dsi_pllcc_8974)); + if (rc) { + pr_err("Clock register failed\n"); + rc = -EPROBE_DEFER; + } + } else { + pr_err("Invalid compatible string\n"); + rc = -EINVAL; } + if (!rc) + pr_info("Registered DSI PLL clocks successfully\n"); + return rc; } diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c index 7a225fb24488..b2de68eab067 100644 --- a/drivers/clk/msm/mdss/mdss-pll.c +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -112,7 +112,8 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, goto err; } - if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll")) + 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")) pll_res->pll_interface_type = MDSS_EDP_PLL; @@ -285,7 +286,8 @@ static int mdss_pll_remove(struct platform_device *pdev) } static const struct of_device_id mdss_pll_dt_match[] = { - {.compatible = "qcom,mdss_dsi_pll"}, + {.compatible = "qcom,mdss_dsi_pll_8974"}, + {.compatible = "qcom,mdss_dsi_pll_8916"}, {.compatible = "qcom,mdss_edp_pll"}, {.compatible = "qcom,mdss_hdmi_pll"}, {} -- cgit v1.2.3