OMAPDSS: HDMI: rewrite HDMI PLL calculation code

The code calculating HDMI PLL parameters has always been very confusing.
Now that we are implementing a common PLL library for the DSS, it's
important that the PLL code is understandable.

This patch rewrites the calculation code, and removes a few hacks that
were used there.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
This commit is contained in:
Tomi Valkeinen 2014-09-15 15:40:47 +03:00
parent 31dd0f4be4
commit 33f13120e5
5 changed files with 67 additions and 79 deletions

View File

@ -191,7 +191,9 @@ struct hdmi_pll_info {
u32 regmf;
u16 regm2;
u16 regsd;
u16 dcofreq;
unsigned long clkdco;
unsigned long clkout;
};
struct hdmi_audio_format {
@ -313,11 +315,13 @@ int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp);
int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp);
void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp);
void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s);
void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin,
unsigned long target_tmds);
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
/* HDMI PHY funcs */
int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg);
int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk,
unsigned long lfbitclk);
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes);

View File

@ -180,7 +180,6 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
int r;
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
unsigned long phy;
struct hdmi_wp_data *wp = &hdmi.wp;
r = hdmi_power_on_core(dssdev);
@ -195,10 +194,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
/* the functions below use kHz pixel clock. TODO: change to Hz */
phy = p->pixelclock / 1000;
hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy);
hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), p->pixelclock);
/* config the PLL and PHY hdmi_set_pll_pwrfirst */
r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp);
@ -207,7 +203,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
goto err_pll_enable;
}
r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
r = hdmi_phy_configure(&hdmi.phy, hdmi.pll.info.clkdco,
hdmi.pll.info.clkout);
if (r) {
DSSDBG("Failed to configure PHY\n");
goto err_phy_cfg;

View File

@ -198,7 +198,6 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
int r;
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
unsigned long phy;
r = hdmi_power_on_core(dssdev);
if (r)
@ -208,10 +207,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
/* the functions below use kHz pixel clock. TODO: change to Hz */
phy = p->pixelclock / 1000;
hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy);
hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), p->pixelclock);
/* disable and clear irqs */
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
@ -225,7 +221,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
goto err_pll_enable;
}
r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
r = hdmi_phy_configure(&hdmi.phy, hdmi.pll.info.clkdco,
hdmi.pll.info.clkout);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_cfg;

View File

@ -20,9 +20,7 @@
struct hdmi_phy_features {
bool bist_ctrl;
bool calc_freqout;
bool ldo_voltage;
unsigned long dcofreq_min;
unsigned long max_phy;
};
@ -132,7 +130,8 @@ static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
}
int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk,
unsigned long lfbitclk)
{
u8 freqout;
@ -149,20 +148,16 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
if (phy_feat->bist_ctrl)
REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11);
if (phy_feat->calc_freqout) {
/* DCOCLK/10 is pixel clock, compare pclk with DCOCLK_MIN/10 */
u32 dco_min = phy_feat->dcofreq_min / 10;
u32 pclk = cfg->timings.pixelclock;
if (pclk < dco_min)
freqout = 0;
else if ((pclk >= dco_min) && (pclk < phy_feat->max_phy))
freqout = 1;
else
freqout = 2;
} else {
/*
* If the hfbitclk != lfbitclk, it means the lfbitclk was configured
* to be used for TMDS.
*/
if (hfbitclk != lfbitclk)
freqout = 0;
else if (hfbitclk / 10 < phy_feat->max_phy)
freqout = 1;
}
else
freqout = 2;
/*
* Write to phy address 0 to configure the clock
@ -184,17 +179,13 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
static const struct hdmi_phy_features omap44xx_phy_feats = {
.bist_ctrl = false,
.calc_freqout = false,
.ldo_voltage = true,
.dcofreq_min = 500000000,
.max_phy = 185675000,
};
static const struct hdmi_phy_features omap54xx_phy_feats = {
.bist_ctrl = true,
.calc_freqout = true,
.ldo_voltage = false,
.dcofreq_min = 750000000,
.max_phy = 186000000,
};

View File

@ -20,14 +20,9 @@
#include "dss.h"
#include "hdmi.h"
#define HDMI_DEFAULT_REGN 16
#define HDMI_DEFAULT_REGM2 1
struct hdmi_pll_features {
bool has_refsel;
bool sys_reset;
/* this is a hack, need to replace it with a better computation of M2 */
bool bound_dcofreq;
unsigned long fint_min, fint_max;
u16 regm_max;
unsigned long dcofreq_low_min, dcofreq_low_max;
@ -52,56 +47,62 @@ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
DUMPPLL(PLLCTRL_CFG4);
}
void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy)
void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin,
unsigned long target_tmds)
{
struct hdmi_pll_info *pi = &pll->info;
unsigned long refclk;
u32 mf;
unsigned long fint, clkdco, clkout;
unsigned long target_bitclk, target_clkdco;
unsigned long min_dco;
unsigned n, m, mf, m2, sd;
/* use our funky units */
clkin /= 10000;
DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds);
/*
* Input clock is predivided by N + 1
* out put of which is reference clk
*/
target_bitclk = target_tmds * 10;
pi->regn = HDMI_DEFAULT_REGN;
/* Fint */
n = DIV_ROUND_UP(clkin, pll_feat->fint_max);
fint = clkin / n;
refclk = clkin / pi->regn;
/* adjust m2 so that the clkdco will be high enough */
min_dco = roundup(pll_feat->dcofreq_low_min, fint);
m2 = DIV_ROUND_UP(min_dco, target_bitclk);
if (m2 == 0)
m2 = 1;
/* temorary hack to make sure DCO freq isn't calculated too low */
if (pll_feat->bound_dcofreq && phy <= 65000)
pi->regm2 = 3;
target_clkdco = target_bitclk * m2;
m = target_clkdco / fint;
clkdco = fint * m;
/* adjust clkdco with fractional mf */
if (WARN_ON(target_clkdco - clkdco > fint))
mf = 0;
else
pi->regm2 = HDMI_DEFAULT_REGM2;
mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint);
/*
* multiplier is pixel_clk/ref_clk
* Multiplying by 100 to avoid fractional part removal
*/
pi->regm = phy * pi->regm2 / refclk;
if (mf > 0)
clkdco += (u32)div_u64((u64)mf * fint, 262144);
/*
* fractional multiplier is remainder of the difference between
* multiplier and actual phy(required pixel clock thus should be
* multiplied by 2^18(262144) divided by the reference clock
*/
mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
pi->regmf = pi->regm2 * mf / refclk;
clkout = clkdco / m2;
/*
* Dcofreq should be set to 1 if required pixel clock
* is greater than 1000MHz
*/
pi->dcofreq = phy > 1000 * 100;
pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
/* sigma-delta */
sd = DIV_ROUND_UP(fint * m, 250000000);
DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n",
n, m, mf, m2, sd);
DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout);
pi->regn = n;
pi->regm = m;
pi->regmf = mf;
pi->regm2 = m2;
pi->regsd = sd;
pi->clkdco = clkdco;
pi->clkout = clkout;
}
static int hdmi_pll_config(struct hdmi_pll_data *pll)
{
u32 r;
@ -123,7 +124,7 @@ static int hdmi_pll_config(struct hdmi_pll_data *pll)
if (pll_feat->has_refsel)
r = FLD_MOD(r, 0x3, 22, 21); /* REFSEL = SYSCLK */
if (fmt->dcofreq)
if (fmt->clkdco > pll_feat->dcofreq_low_max)
r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
else
r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
@ -210,7 +211,6 @@ void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
static const struct hdmi_pll_features omap44xx_pll_feats = {
.sys_reset = false,
.bound_dcofreq = false,
.fint_min = 500000,
.fint_max = 2500000,
.regm_max = 4095,
@ -223,7 +223,6 @@ static const struct hdmi_pll_features omap44xx_pll_feats = {
static const struct hdmi_pll_features omap54xx_pll_feats = {
.has_refsel = true,
.sys_reset = true,
.bound_dcofreq = true,
.fint_min = 620000,
.fint_max = 2500000,
.regm_max = 2046,