Files
android_kernel_samsung_sm86…/pll/dsi_pll_28lpm.c
Narendra Muppalla 3709853456 Display drivers kernel project initial snapshot
This change brings msm display driver including sde,
dp, dsi, rotator, dsi pll and dp pll from base 4.19 kernel
project. It is first source code snapshot from base kernel project.

Change-Id: Iec864c064ce5ea04e170f24414c728684002f284
Signed-off-by: Narendra Muppalla <NarendraM@codeaurora.org>
2019-04-14 22:20:59 -07:00

546 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <dt-bindings/clock/mdss-28nm-pll-clk.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#include "dsi_pll_28nm.h"
#define VCO_DELAY_USEC 1000
enum {
DSI_PLL_0,
DSI_PLL_1,
DSI_PLL_MAX
};
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 void dsi_pll_sw_reset(struct mdss_pll_resources *rsc)
{
/*
* DSI PLL software reset. Add HW recommended delays after toggling
* the software reset bit off and back on.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
ndelay(500);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
}
static void dsi_pll_toggle_lock_detect(
struct mdss_pll_resources *rsc)
{
/* DSI PLL toggle lock detect setting */
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x04);
ndelay(500);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05);
udelay(512);
}
static int dsi_pll_check_lock_status(
struct mdss_pll_resources *rsc)
{
int rc = 0;
rc = dsi_pll_lock_status(rsc);
if (rc)
pr_debug("PLL Locked\n");
else
pr_err("PLL failed to lock\n");
return rc;
}
static int dsi_pll_enable_seq_gf2(struct mdss_pll_resources *rsc)
{
int pll_locked = 0;
dsi_pll_sw_reset(rsc);
/*
* GF PART 2 PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x04);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(3);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
dsi_pll_toggle_lock_detect(rsc);
pll_locked = dsi_pll_check_lock_status(rsc);
return pll_locked ? 0 : -EINVAL;
}
static int dsi_pll_enable_seq_gf1(struct mdss_pll_resources *rsc)
{
int pll_locked = 0;
dsi_pll_sw_reset(rsc);
/*
* GF PART 1 PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x14);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(3);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
dsi_pll_toggle_lock_detect(rsc);
pll_locked = dsi_pll_check_lock_status(rsc);
return pll_locked ? 0 : -EINVAL;
}
static int dsi_pll_enable_seq_tsmc(struct mdss_pll_resources *rsc)
{
int pll_locked = 0;
dsi_pll_sw_reset(rsc);
/*
* TSMC PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
dsi_pll_toggle_lock_detect(rsc);
pll_locked = dsi_pll_check_lock_status(rsc);
return pll_locked ? 0 : -EINVAL;
}
static struct regmap_config dsi_pll_28lpm_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xF4,
};
static struct regmap_bus analog_postdiv_regmap_bus = {
.reg_write = analog_postdiv_reg_write,
.reg_read = analog_postdiv_reg_read,
};
static struct regmap_bus byteclk_src_mux_regmap_bus = {
.reg_write = byteclk_mux_write_sel,
.reg_read = byteclk_mux_read_sel,
};
static struct regmap_bus pclk_src_regmap_bus = {
.reg_write = pixel_clk_set_div,
.reg_read = pixel_clk_get_div,
};
static const struct clk_ops clk_ops_vco_28lpm = {
.recalc_rate = vco_28nm_recalc_rate,
.set_rate = vco_28nm_set_rate,
.round_rate = vco_28nm_round_rate,
.prepare = vco_28nm_prepare,
.unprepare = vco_28nm_unprepare,
};
static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 350000000UL,
.max_rate = 750000000UL,
.pll_en_seq_cnt = 9,
.pll_enable_seqs[0] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[1] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[2] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[3] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[4] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[5] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[6] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[7] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[8] = dsi_pll_enable_seq_gf2,
.lpfr_lut_size = 10,
.lpfr_lut = lpfr_lut_struct,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_vco_clk",
.parent_names = (const char *[]){"cxo"},
.num_parents = 1,
.ops = &clk_ops_vco_28lpm,
.flags = CLK_GET_RATE_NOCACHE,
},
};
static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 350000000UL,
.max_rate = 750000000UL,
.pll_en_seq_cnt = 9,
.pll_enable_seqs[0] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[1] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[2] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[3] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[4] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[5] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[6] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[7] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[8] = dsi_pll_enable_seq_gf2,
.lpfr_lut_size = 10,
.lpfr_lut = lpfr_lut_struct,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_vco_clk",
.parent_names = (const char *[]){"cxo"},
.num_parents = 1,
.ops = &clk_ops_vco_28lpm,
.flags = CLK_GET_RATE_NOCACHE,
},
};
static struct clk_regmap_div dsi0pll_analog_postdiv = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_analog_postdiv",
.parent_names = (const char *[]){"dsi0pll_vco_clk"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_analog_postdiv = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_analog_postdiv",
.parent_names = (const char *[]){"dsi1pll_vco_clk"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_fixed_factor dsi0pll_indirect_path_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_indirect_path_src",
.parent_names = (const char *[]){"dsi0pll_analog_postdiv"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_indirect_path_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_indirect_path_src",
.parent_names = (const char *[]){"dsi1pll_analog_postdiv"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_mux dsi0pll_byteclk_src_mux = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG,
.shift = 1,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_byteclk_src_mux",
.parent_names = (const char *[]){
"dsi0pll_vco_clk",
"dsi0pll_indirect_path_src"},
.num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
},
};
static struct clk_regmap_mux dsi1pll_byteclk_src_mux = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG,
.shift = 1,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_byteclk_src_mux",
.parent_names = (const char *[]){
"dsi1pll_vco_clk",
"dsi1pll_indirect_path_src"},
.num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
},
};
static struct clk_fixed_factor dsi0pll_byteclk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_byteclk_src",
.parent_names = (const char *[]){
"dsi0pll_byteclk_src_mux"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_byteclk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_byteclk_src",
.parent_names = (const char *[]){
"dsi1pll_byteclk_src_mux"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_div dsi0pll_pclk_src = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG,
.shift = 0,
.width = 8,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_pclk_src",
.parent_names = (const char *[]){"dsi0pll_vco_clk"},
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_pclk_src = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG,
.shift = 0,
.width = 8,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_pclk_src",
.parent_names = (const char *[]){"dsi1pll_vco_clk"},
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_hw *mdss_dsi_pllcc_28lpm[] = {
[VCO_CLK_0] = &dsi0pll_vco_clk.hw,
[ANALOG_POSTDIV_0_CLK] = &dsi0pll_analog_postdiv.clkr.hw,
[INDIRECT_PATH_SRC_0_CLK] = &dsi0pll_indirect_path_src.hw,
[BYTECLK_SRC_MUX_0_CLK] = &dsi0pll_byteclk_src_mux.clkr.hw,
[BYTECLK_SRC_0_CLK] = &dsi0pll_byteclk_src.hw,
[PCLK_SRC_0_CLK] = &dsi0pll_pclk_src.clkr.hw,
[VCO_CLK_1] = &dsi1pll_vco_clk.hw,
[ANALOG_POSTDIV_1_CLK] = &dsi1pll_analog_postdiv.clkr.hw,
[INDIRECT_PATH_SRC_1_CLK] = &dsi1pll_indirect_path_src.hw,
[BYTECLK_SRC_MUX_1_CLK] = &dsi1pll_byteclk_src_mux.clkr.hw,
[BYTECLK_SRC_1_CLK] = &dsi1pll_byteclk_src.hw,
[PCLK_SRC_1_CLK] = &dsi1pll_pclk_src.clkr.hw,
};
int dsi_pll_clock_register_28lpm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0, ndx, i;
struct clk *clk;
struct clk_onecell_data *clk_data;
int num_clks = ARRAY_SIZE(mdss_dsi_pllcc_28lpm);
struct regmap *rmap;
int const ssc_freq_min = 30000; /* min. recommended freq. value */
int const ssc_freq_max = 33000; /* max. recommended freq. value */
int const ssc_ppm_max = 5000; /* max. recommended ppm */
ndx = pll_res->index;
if (ndx >= DSI_PLL_MAX) {
pr_err("pll index(%d) NOT supported\n", ndx);
return -EINVAL;
}
pll_res->vco_delay = VCO_DELAY_USEC;
if (pll_res->ssc_en) {
if (!pll_res->ssc_freq || (pll_res->ssc_freq < ssc_freq_min) ||
(pll_res->ssc_freq > ssc_freq_max)) {
pll_res->ssc_freq = ssc_freq_min;
pr_debug("SSC frequency out of recommended range. Set to default=%d\n",
pll_res->ssc_freq);
}
if (!pll_res->ssc_ppm || (pll_res->ssc_ppm > ssc_ppm_max)) {
pll_res->ssc_ppm = ssc_ppm_max;
pr_debug("SSC PPM out of recommended range. Set to default=%d\n",
pll_res->ssc_ppm);
}
}
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kcalloc(&pdev->dev, num_clks,
sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
clk_data->clk_num = num_clks;
/* Establish client data */
if (ndx == 0) {
rmap = devm_regmap_init(&pdev->dev, &byteclk_src_mux_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi0pll_byteclk_src_mux.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &analog_postdiv_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi0pll_analog_postdiv.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &pclk_src_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi0pll_pclk_src.clkr.regmap = rmap;
dsi0pll_vco_clk.priv = pll_res;
for (i = VCO_CLK_0; i <= PCLK_SRC_0_CLK; i++) {
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_28lpm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DSI clock:%d\n",
pll_res->index);
rc = -EINVAL;
goto clk_register_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
} else {
rmap = devm_regmap_init(&pdev->dev, &byteclk_src_mux_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi1pll_byteclk_src_mux.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &analog_postdiv_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi1pll_analog_postdiv.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &pclk_src_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi1pll_pclk_src.clkr.regmap = rmap;
dsi1pll_vco_clk.priv = pll_res;
for (i = VCO_CLK_1; i <= PCLK_SRC_1_CLK; i++) {
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_28lpm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DSI clock:%d\n",
pll_res->index);
rc = -EINVAL;
goto clk_register_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
}
if (!rc) {
pr_info("Registered DSI PLL ndx=%d, clocks successfully\n",
ndx);
return rc;
}
clk_register_fail:
return rc;
}