clk: msm: hdmi: add support for live programming of HDMI PLL
Clock recovery and dynamic resolution change require changing HDMI clock rate while HDMI PLL is on. There are two paths while clock is changed, one is atomic update which doesn't require PLL tear down, the other is when clock rate change is too big and a full PLL tear down is needed. CRs-Fixed: 1086894 Change-Id: Ia202e0aee09f506a7bbe4e13702f30dee119ce8e Signed-off-by: Ray Zhang <rayz@codeaurora.org>
This commit is contained in:
parent
bee274711e
commit
83c88144ae
1 changed files with 145 additions and 43 deletions
|
@ -29,6 +29,7 @@
|
|||
#define _R(x, y) MDSS_PLL_REG_R(x, y)
|
||||
|
||||
/* PLL REGISTERS */
|
||||
#define FREQ_UPDATE (0x008)
|
||||
#define BIAS_EN_CLKBUFLR_EN (0x034)
|
||||
#define CLK_ENABLE1 (0x038)
|
||||
#define SYS_CLK_CTRL (0x03C)
|
||||
|
@ -80,6 +81,7 @@
|
|||
#define HDMI_BIT_CLK_TO_PIX_CLK_RATIO 10
|
||||
#define HDMI_MHZ_TO_HZ 1000000
|
||||
#define HDMI_HZ_TO_MHZ 1000000
|
||||
#define HDMI_KHZ_TO_HZ 1000
|
||||
#define HDMI_REF_CLOCK_MHZ 19.2
|
||||
#define HDMI_REF_CLOCK_HZ (HDMI_REF_CLOCK_MHZ * 1000000)
|
||||
#define HDMI_VCO_MIN_RATE_HZ 25000000
|
||||
|
@ -609,49 +611,6 @@ static int hdmi_8998_phy_ready_status(struct mdss_pll_resources *io)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int rc = 0;
|
||||
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
|
||||
struct mdss_pll_resources *io = vco->priv;
|
||||
|
||||
rc = mdss_pll_resource_enable(io, true);
|
||||
if (rc) {
|
||||
pr_err("pll resource enable failed, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (io->pll_on)
|
||||
goto error;
|
||||
|
||||
rc = hdmi_8998_pll_set_clk_rate(c, rate);
|
||||
if (rc) {
|
||||
pr_err("failed to set clk rate, rc=%d\n", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vco->rate = rate;
|
||||
vco->rate_set = true;
|
||||
|
||||
error:
|
||||
(void)mdss_pll_resource_enable(io, false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long hdmi_8998_vco_round_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
unsigned long rrate = rate;
|
||||
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
|
||||
|
||||
if (rate < vco->min_rate)
|
||||
rrate = vco->min_rate;
|
||||
if (rate > vco->max_rate)
|
||||
rrate = vco->max_rate;
|
||||
|
||||
return rrate;
|
||||
}
|
||||
|
||||
static int hdmi_8998_pll_enable(struct clk *c)
|
||||
{
|
||||
int rc = 0;
|
||||
|
@ -700,6 +659,149 @@ static int hdmi_8998_pll_enable(struct clk *c)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the clock range allowed in atomic update. If clock rate
|
||||
* goes beyond this range, a full tear down is required to set
|
||||
* the new pixel clock.
|
||||
*/
|
||||
static int hdmi_8998_vco_get_lock_range(struct clk *c,
|
||||
unsigned long pixel_clk)
|
||||
{
|
||||
const u32 rng = 64, cmp_cnt = 1024;
|
||||
const u32 coreclk_div = 5, clks_pll_divsel = 2;
|
||||
u32 vco_freq, vco_ratio, ppm_range;
|
||||
struct hdmi_8998_reg_cfg cfg = {0};
|
||||
|
||||
pr_debug("rate=%ld\n", pixel_clk);
|
||||
|
||||
hdmi_8998_get_div(&cfg, pixel_clk);
|
||||
if (cfg.vco_ratio <= 0 || cfg.vco_freq <= 0) {
|
||||
pr_err("couldn't get post div\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vco_freq = (u32) cfg.vco_freq;
|
||||
vco_ratio = (u32) cfg.vco_ratio;
|
||||
|
||||
pr_debug("freq %d, ratio %d\n", vco_freq, vco_ratio);
|
||||
|
||||
ppm_range = (rng * HDMI_REF_CLOCK_HZ) / cmp_cnt;
|
||||
ppm_range /= vco_freq / vco_ratio;
|
||||
ppm_range *= coreclk_div * clks_pll_divsel;
|
||||
|
||||
pr_debug("ppm range: %d\n", ppm_range);
|
||||
|
||||
return ppm_range;
|
||||
}
|
||||
|
||||
static int hdmi_8998_vco_rate_atomic_update(struct clk *c,
|
||||
unsigned long rate)
|
||||
{
|
||||
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
|
||||
struct mdss_pll_resources *io = vco->priv;
|
||||
void __iomem *pll;
|
||||
struct hdmi_8998_reg_cfg cfg = {0};
|
||||
int rc = 0;
|
||||
|
||||
rc = hdmi_8998_config_phy(rate, &cfg);
|
||||
if (rc) {
|
||||
pr_err("rate calculation failed\n, rc=%d", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
pll = io->pll_base;
|
||||
|
||||
_W(pll, DEC_START_MODE0, cfg.dec_start_mode0);
|
||||
_W(pll, DIV_FRAC_START1_MODE0, cfg.div_frac_start1_mode0);
|
||||
_W(pll, DIV_FRAC_START2_MODE0, cfg.div_frac_start2_mode0);
|
||||
_W(pll, DIV_FRAC_START3_MODE0, cfg.div_frac_start3_mode0);
|
||||
|
||||
_W(pll, FREQ_UPDATE, 0x01);
|
||||
_W(pll, FREQ_UPDATE, 0x00);
|
||||
|
||||
pr_debug("updated to rate %ld\n", rate);
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
|
||||
struct mdss_pll_resources *io = vco->priv;
|
||||
unsigned int set_power_dwn = 0;
|
||||
bool atomic_update = false;
|
||||
int pll_lock_range = 0;
|
||||
int rc = 0;
|
||||
|
||||
rc = mdss_pll_resource_enable(io, true);
|
||||
if (rc) {
|
||||
pr_err("pll resource enable failed, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_debug("rate %ld\n", rate);
|
||||
|
||||
if (_R(io->pll_base, C_READY_STATUS) & BIT(0) &&
|
||||
_R(io->phy_base, PHY_STATUS) & BIT(0)) {
|
||||
pll_lock_range = hdmi_8998_vco_get_lock_range(c, vco->rate);
|
||||
|
||||
if (pll_lock_range > 0 && vco->rate) {
|
||||
u32 range_limit;
|
||||
|
||||
range_limit = vco->rate *
|
||||
(pll_lock_range / HDMI_KHZ_TO_HZ);
|
||||
range_limit /= HDMI_KHZ_TO_HZ;
|
||||
|
||||
pr_debug("range limit %d\n", range_limit);
|
||||
|
||||
if (abs(rate - vco->rate) < range_limit)
|
||||
atomic_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (io->pll_on && !atomic_update)
|
||||
set_power_dwn = 1;
|
||||
|
||||
if (atomic_update)
|
||||
rc = hdmi_8998_vco_rate_atomic_update(c, rate);
|
||||
else
|
||||
rc = hdmi_8998_pll_set_clk_rate(c, rate);
|
||||
|
||||
if (rc) {
|
||||
pr_err("failed to set clk rate\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (set_power_dwn) {
|
||||
rc = hdmi_8998_pll_enable(c);
|
||||
if (rc) {
|
||||
pr_err("failed to enable pll, rc=%d\n", rc);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
vco->rate = rate;
|
||||
vco->rate_set = true;
|
||||
|
||||
error:
|
||||
(void)mdss_pll_resource_enable(io, false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long hdmi_8998_vco_round_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
unsigned long rrate = rate;
|
||||
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
|
||||
|
||||
if (rate < vco->min_rate)
|
||||
rrate = vco->min_rate;
|
||||
if (rate > vco->max_rate)
|
||||
rrate = vco->max_rate;
|
||||
|
||||
return rrate;
|
||||
}
|
||||
|
||||
static int hdmi_8998_vco_prepare(struct clk *c)
|
||||
{
|
||||
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
|
||||
|
|
Loading…
Add table
Reference in a new issue