Message ID | 1379401597-27222-3-git-send-email-archit@ti.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Quoting Archit Taneja (2013-09-17 00:06:28) > HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the > existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file. > These funcs are called directly from the hdmi driver rather than hdmi_ip_ops > function pointer calls. > > Add the PLL library function declarations to ti_hdmi.h. These will be shared > amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops > from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi > platform drivers and other libraries. > > Signed-off-by: Archit Taneja <archit@ti.com> Would be cool to see this convert to the common clock framework implementation (include/linux/clk-provider.h). It appears that this PLL only needs to support .enable, .disable and .recalc_rate callbacks at first glance. Regards, Mike > --- > drivers/video/omap2/dss/Makefile | 2 +- > drivers/video/omap2/dss/dss_features.c | 3 - > drivers/video/omap2/dss/hdmi.c | 65 ++------ > drivers/video/omap2/dss/hdmi_pll.c | 243 ++++++++++++++++++++++++++++++ > drivers/video/omap2/dss/ti_hdmi.h | 25 +-- > drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 132 ---------------- > 6 files changed, 267 insertions(+), 203 deletions(-) > create mode 100644 drivers/video/omap2/dss/hdmi_pll.c > > diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile > index 56ce6bd..5ea65d3 100644 > --- a/drivers/video/omap2/dss/Makefile > +++ b/drivers/video/omap2/dss/Makefile > @@ -10,5 +10,5 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o > omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o > omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o > omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o > -omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o ti_hdmi_4xxx_ip.o > +omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o ti_hdmi_4xxx_ip.o > ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG > diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c > index db359e8..9ee92e9 100644 > --- a/drivers/video/omap2/dss/dss_features.c > +++ b/drivers/video/omap2/dss/dss_features.c > @@ -797,10 +797,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { > .phy_enable = ti_hdmi_4xxx_phy_enable, > .phy_disable = ti_hdmi_4xxx_phy_disable, > .read_edid = ti_hdmi_4xxx_read_edid, > - .pll_enable = ti_hdmi_4xxx_pll_enable, > - .pll_disable = ti_hdmi_4xxx_pll_disable, > .dump_core = ti_hdmi_4xxx_core_dump, > - .dump_pll = ti_hdmi_4xxx_pll_dump, > .dump_phy = ti_hdmi_4xxx_phy_dump, > #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) > .audio_start = ti_hdmi_4xxx_audio_start, > diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c > index f2475fc..f6a2eba 100644 > --- a/drivers/video/omap2/dss/hdmi.c > +++ b/drivers/video/omap2/dss/hdmi.c > @@ -42,7 +42,6 @@ > > #define HDMI_CORE_SYS 0x400 > #define HDMI_CORE_AV 0x900 > -#define HDMI_PLLCTRL 0x200 > #define HDMI_PHY 0x300 > > /* HDMI EDID Length move this */ > @@ -53,9 +52,6 @@ > #define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 > #define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 > > -#define HDMI_DEFAULT_REGN 16 > -#define HDMI_DEFAULT_REGM2 1 > - > static struct { > struct mutex lock; > struct platform_device *pdev; > @@ -428,52 +424,6 @@ end: return cm; > > } > > -static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, > - struct hdmi_pll_info *pi) > -{ > - unsigned long clkin, refclk; > - u32 mf; > - > - clkin = clk_get_rate(hdmi.sys_clk) / 10000; > - /* > - * Input clock is predivided by N + 1 > - * out put of which is reference clk > - */ > - > - pi->regn = HDMI_DEFAULT_REGN; > - > - refclk = clkin / pi->regn; > - > - pi->regm2 = HDMI_DEFAULT_REGM2; > - > - /* > - * multiplier is pixel_clk/ref_clk > - * Multiplying by 100 to avoid fractional part removal > - */ > - pi->regm = phy * pi->regm2 / refclk; > - > - /* > - * 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; > - > - /* > - * 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; > - > - /* Set the reference clock to sysclk reference */ > - pi->refsel = HDMI_REFSEL_SYSCLK; > - > - DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); > - DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); > -} > - > static int hdmi_power_on_core(struct omap_dss_device *dssdev) > { > int r; > @@ -526,12 +476,12 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) > > phy = p->pixel_clock; > > - hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); > + hdmi_pll_compute(&hdmi.ip_data.pll, clk_get_rate(hdmi.sys_clk), phy); > > hdmi_wp_video_stop(&hdmi.ip_data.wp); > > /* config the PLL and PHY hdmi_set_pll_pwrfirst */ > - r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); > + r = hdmi_pll_enable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); > if (r) { > DSSDBG("Failed to lock PLL\n"); > goto err_pll_enable; > @@ -566,7 +516,7 @@ err_mgr_enable: > err_vid_enable: > hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); > err_phy_enable: > - hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); > + hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); > err_pll_enable: > hdmi_power_off_core(dssdev); > return -EIO; > @@ -580,7 +530,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) > > hdmi_wp_video_stop(&hdmi.ip_data.wp); > hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); > - hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); > + hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); > > hdmi_power_off_core(dssdev); > } > @@ -642,7 +592,7 @@ static void hdmi_dump_regs(struct seq_file *s) > } > > hdmi_wp_dump(&hdmi.ip_data.wp, s); > - hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s); > + hdmi_pll_dump(&hdmi.ip_data.pll, s); > hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s); > hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s); > > @@ -1095,6 +1045,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) > if (r) > return r; > > + r = hdmi_pll_init(pdev, &hdmi.ip_data.pll); > + if (r) > + return r; > + > hdmi.ip_data.irq = platform_get_irq(pdev, 0); > if (hdmi.ip_data.irq < 0) { > DSSERR("platform_get_irq failed\n"); > @@ -1111,7 +1065,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) > > hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS; > hdmi.ip_data.core_av_offset = HDMI_CORE_AV; > - hdmi.ip_data.pll_offset = HDMI_PLLCTRL; > hdmi.ip_data.phy_offset = HDMI_PHY; > > hdmi_init_output(pdev); > diff --git a/drivers/video/omap2/dss/hdmi_pll.c b/drivers/video/omap2/dss/hdmi_pll.c > new file mode 100644 > index 0000000..d53b8e2 > --- /dev/null > +++ b/drivers/video/omap2/dss/hdmi_pll.c > @@ -0,0 +1,243 @@ > +/* > + * HDMI PLL > + * > + * Copyright (C) 2013 Texas Instruments Incorporated > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License version 2 as published by > + * the Free Software Foundation. > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/platform_device.h> > +#include <video/omapdss.h> > + > +#include "dss.h" > +#include "ti_hdmi.h" > +#include "ti_hdmi_4xxx_ip.h" > + > +#define HDMI_DEFAULT_REGN 16 > +#define HDMI_DEFAULT_REGM2 1 > + > +static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, > + u32 val) > +{ > + __raw_writel(val, base_addr + idx); > +} > + > +static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx) > +{ > + return __raw_readl(base_addr + idx); > +} > + > +#define REG_FLD_MOD(base, idx, val, start, end) \ > + hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\ > + val, start, end)) > +#define REG_GET(base, idx, start, end) \ > + FLD_GET(hdmi_read_reg(base, idx), start, end) > + > +static inline int hdmi_wait_for_bit_change(void __iomem *base_addr, > + const u16 idx, int b2, int b1, u32 val) > +{ > + u32 t = 0; > + while (val != REG_GET(base_addr, idx, b2, b1)) { > + udelay(1); > + if (t++ > 10000) > + return !val; > + } > + return val; > +} > + > +void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) > +{ > +#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ > + hdmi_read_reg(pll->base, r)) > + > + DUMPPLL(PLLCTRL_PLL_CONTROL); > + DUMPPLL(PLLCTRL_PLL_STATUS); > + DUMPPLL(PLLCTRL_PLL_GO); > + DUMPPLL(PLLCTRL_CFG1); > + DUMPPLL(PLLCTRL_CFG2); > + DUMPPLL(PLLCTRL_CFG3); > + DUMPPLL(PLLCTRL_SSC_CFG1); > + DUMPPLL(PLLCTRL_SSC_CFG2); > + DUMPPLL(PLLCTRL_CFG4); > +} > + > +void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy) > +{ > + struct hdmi_pll_info *pi = &pll->info; > + unsigned long refclk; > + u32 mf; > + > + /* use our funky units */ > + clkin /= 10000; > + > + /* > + * Input clock is predivided by N + 1 > + * out put of which is reference clk > + */ > + > + pi->regn = HDMI_DEFAULT_REGN; > + > + refclk = clkin / pi->regn; > + > + pi->regm2 = HDMI_DEFAULT_REGM2; > + > + /* > + * multiplier is pixel_clk/ref_clk > + * Multiplying by 100 to avoid fractional part removal > + */ > + pi->regm = phy * pi->regm2 / refclk; > + > + /* > + * 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; > + > + /* > + * 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; > + > + /* Set the reference clock to sysclk reference */ > + pi->refsel = HDMI_REFSEL_SYSCLK; > + > + DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); > + DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); > +} > + > + > +static int hdmi_pll_config(struct hdmi_pll_data *pll) > +{ > + u32 r; > + struct hdmi_pll_info *fmt = &pll->info; > + > + /* PLL start always use manual mode */ > + REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); > + > + r = hdmi_read_reg(pll->base, PLLCTRL_CFG1); > + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ > + r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ > + hdmi_write_reg(pll->base, PLLCTRL_CFG1, r); > + > + r = hdmi_read_reg(pll->base, PLLCTRL_CFG2); > + > + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ > + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ > + r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ > + r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ > + > + if (fmt->dcofreq) { > + /* divider programming for frequency beyond 1000Mhz */ > + REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10); > + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ > + } else { > + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ > + } > + > + hdmi_write_reg(pll->base, PLLCTRL_CFG2, r); > + > + r = hdmi_read_reg(pll->base, PLLCTRL_CFG4); > + r = FLD_MOD(r, fmt->regm2, 24, 18); > + r = FLD_MOD(r, fmt->regmf, 17, 0); > + hdmi_write_reg(pll->base, PLLCTRL_CFG4, r); > + > + /* go now */ > + REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0); > + > + /* wait for bit change */ > + if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO, > + 0, 0, 1) != 1) { > + pr_err("PLL GO bit not set\n"); > + return -ETIMEDOUT; > + } > + > + /* Wait till the lock bit is set in PLL status */ > + if (hdmi_wait_for_bit_change(pll->base, > + PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { > + pr_err("cannot lock PLL\n"); > + pr_err("CFG1 0x%x\n", > + hdmi_read_reg(pll->base, PLLCTRL_CFG1)); > + pr_err("CFG2 0x%x\n", > + hdmi_read_reg(pll->base, PLLCTRL_CFG2)); > + pr_err("CFG4 0x%x\n", > + hdmi_read_reg(pll->base, PLLCTRL_CFG4)); > + return -ETIMEDOUT; > + } > + > + pr_debug("PLL locked!\n"); > + > + return 0; > +} > + > +static int hdmi_pll_reset(struct hdmi_pll_data *pll) > +{ > + /* SYSRESET controlled by power FSM */ > + REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 3, 3); > + > + /* READ 0x0 reset is in progress */ > + if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1) > + != 1) { > + pr_err("Failed to sysreset PLL\n"); > + return -ETIMEDOUT; > + } > + > + return 0; > +} > + > +int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) > +{ > + u16 r = 0; > + > + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); > + if (r) > + return r; > + > + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); > + if (r) > + return r; > + > + r = hdmi_pll_reset(pll); > + if (r) > + return r; > + > + r = hdmi_pll_config(pll); > + if (r) > + return r; > + > + return 0; > +} > + > +void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) > +{ > + hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); > +} > + > +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) > +{ > + struct resource *res; > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_pllctrl"); > + if (!res) { > + DSSERR("can't get PLL CTRL IORESOURCE_MEM HDMI\n"); > + return -EINVAL; > + } > + > + pll->base = devm_request_and_ioremap(&pdev->dev, res); > + if (!pll->base) { > + DSSERR("can't ioremap PLL ctrl\n"); > + return -ENOMEM; > + } > + > + return 0; > +} > diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h > index d16f28d..62a83c7 100644 > --- a/drivers/video/omap2/dss/ti_hdmi.h > +++ b/drivers/video/omap2/dss/ti_hdmi.h > @@ -155,14 +155,8 @@ struct ti_hdmi_ip_ops { > > int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len); > > - int (*pll_enable)(struct hdmi_ip_data *ip_data); > - > - void (*pll_disable)(struct hdmi_ip_data *ip_data); > - > void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s); > > - void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s); > - > void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); > > #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) > @@ -223,17 +217,22 @@ struct hdmi_wp_data { > void __iomem *base; > }; > > +struct hdmi_pll_data { > + void __iomem *base; > + > + struct hdmi_pll_info info; > +}; > + > struct hdmi_ip_data { > struct hdmi_wp_data wp; > + struct hdmi_pll_data pll; > > unsigned long core_sys_offset; > unsigned long core_av_offset; > - unsigned long pll_offset; > unsigned long phy_offset; > int irq; > const struct ti_hdmi_ip_ops *ops; > struct hdmi_config cfg; > - struct hdmi_pll_info pll_data; > struct hdmi_core_infoframe_avi avi_cfg; > > /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ > @@ -260,13 +259,17 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, > struct omap_video_timings *timings, struct hdmi_config *param); > int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp); > > +/* HDMI PLL funcs */ > +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); > +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); > + > int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); > void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); > int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); > -int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); > -void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); > void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); > -void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); > void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); > void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); > #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) > diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c > index d4b8883..8cfb54b 100644 > --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c > +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c > @@ -75,11 +75,6 @@ static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data) > return ip_data->wp.base + ip_data->phy_offset; > } > > -static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data) > -{ > - return ip_data->wp.base + ip_data->pll_offset; > -} > - > static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data) > { > return ip_data->wp.base + ip_data->core_av_offset; > @@ -90,117 +85,6 @@ static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data) > return ip_data->wp.base + ip_data->core_sys_offset; > } > > - > -static int hdmi_pll_init(struct hdmi_ip_data *ip_data) > -{ > - u32 r; > - void __iomem *pll_base = hdmi_pll_base(ip_data); > - struct hdmi_pll_info *fmt = &ip_data->pll_data; > - > - /* PLL start always use manual mode */ > - REG_FLD_MOD(pll_base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); > - > - r = hdmi_read_reg(pll_base, PLLCTRL_CFG1); > - r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ > - r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ > - > - hdmi_write_reg(pll_base, PLLCTRL_CFG1, r); > - > - r = hdmi_read_reg(pll_base, PLLCTRL_CFG2); > - > - r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ > - r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ > - r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ > - r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ > - > - if (fmt->dcofreq) { > - /* divider programming for frequency beyond 1000Mhz */ > - REG_FLD_MOD(pll_base, PLLCTRL_CFG3, fmt->regsd, 17, 10); > - r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ > - } else { > - r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ > - } > - > - hdmi_write_reg(pll_base, PLLCTRL_CFG2, r); > - > - r = hdmi_read_reg(pll_base, PLLCTRL_CFG4); > - r = FLD_MOD(r, fmt->regm2, 24, 18); > - r = FLD_MOD(r, fmt->regmf, 17, 0); > - > - hdmi_write_reg(pll_base, PLLCTRL_CFG4, r); > - > - /* go now */ > - REG_FLD_MOD(pll_base, PLLCTRL_PLL_GO, 0x1, 0, 0); > - > - /* wait for bit change */ > - if (hdmi_wait_for_bit_change(pll_base, PLLCTRL_PLL_GO, > - 0, 0, 1) != 1) { > - pr_err("PLL GO bit not set\n"); > - return -ETIMEDOUT; > - } > - > - /* Wait till the lock bit is set in PLL status */ > - if (hdmi_wait_for_bit_change(pll_base, > - PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { > - pr_err("cannot lock PLL\n"); > - pr_err("CFG1 0x%x\n", > - hdmi_read_reg(pll_base, PLLCTRL_CFG1)); > - pr_err("CFG2 0x%x\n", > - hdmi_read_reg(pll_base, PLLCTRL_CFG2)); > - pr_err("CFG4 0x%x\n", > - hdmi_read_reg(pll_base, PLLCTRL_CFG4)); > - return -ETIMEDOUT; > - } > - > - pr_debug("PLL locked!\n"); > - > - return 0; > -} > - > - > -static int hdmi_pll_reset(struct hdmi_ip_data *ip_data) > -{ > - /* SYSRESET controlled by power FSM */ > - REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3); > - > - /* READ 0x0 reset is in progress */ > - if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data), > - PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) { > - pr_err("Failed to sysreset PLL\n"); > - return -ETIMEDOUT; > - } > - > - return 0; > -} > - > -int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data) > -{ > - u16 r = 0; > - > - r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF); > - if (r) > - return r; > - > - r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); > - if (r) > - return r; > - > - r = hdmi_pll_reset(ip_data); > - if (r) > - return r; > - > - r = hdmi_pll_init(ip_data); > - if (r) > - return r; > - > - return 0; > -} > - > -void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) > -{ > - hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF); > -} > - > static irqreturn_t hdmi_irq_handler(int irq, void *data) > { > struct hdmi_ip_data *ip_data = data; > @@ -717,22 +601,6 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) > hdmi_core_av_packet_config(ip_data, repeat_cfg); > } > > -void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) > -{ > -#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ > - hdmi_read_reg(hdmi_pll_base(ip_data), r)) > - > - DUMPPLL(PLLCTRL_PLL_CONTROL); > - DUMPPLL(PLLCTRL_PLL_STATUS); > - DUMPPLL(PLLCTRL_PLL_GO); > - DUMPPLL(PLLCTRL_CFG1); > - DUMPPLL(PLLCTRL_CFG2); > - DUMPPLL(PLLCTRL_CFG3); > - DUMPPLL(PLLCTRL_SSC_CFG1); > - DUMPPLL(PLLCTRL_SSC_CFG2); > - DUMPPLL(PLLCTRL_CFG4); > -} > - > void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) > { > int i; > -- > 1.8.1.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-omap" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 17/09/13 12:38, Mike Turquette wrote: > Quoting Archit Taneja (2013-09-17 00:06:28) >> HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the >> existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file. >> These funcs are called directly from the hdmi driver rather than hdmi_ip_ops >> function pointer calls. >> >> Add the PLL library function declarations to ti_hdmi.h. These will be shared >> amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops >> from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi >> platform drivers and other libraries. >> >> Signed-off-by: Archit Taneja <archit@ti.com> > > Would be cool to see this convert to the common clock framework > implementation (include/linux/clk-provider.h). It appears that this PLL > only needs to support .enable, .disable and .recalc_rate callbacks at > first glance. We've got a (very) long term plan to use CCF for DSS. And, if I'm not mistaken, the PLL used for HDMI and DSI is the same as used elsewhere in OMAP (I don't remember the PLL type name). I think the main issue with DSS clocks is that we require as exact clocks as possible, and there are multiple dividers/multipliers on the clock path. So we iterate over the divider/multiplier ranges, trying to find as good freq match as possible. So if the clock path is composed from "black boxes", and we can only ask for a certain frequency, and get back probably some other frequency, it gets quite difficult to find good clock matches. Well, at least I imagine so, I have not tried to implement that. Tomi
On Tue, Sep 17, 2013 at 3:02 AM, Tomi Valkeinen <tomi.valkeinen@ti.com> wrote: > On 17/09/13 12:38, Mike Turquette wrote: >> Quoting Archit Taneja (2013-09-17 00:06:28) >>> HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the >>> existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file. >>> These funcs are called directly from the hdmi driver rather than hdmi_ip_ops >>> function pointer calls. >>> >>> Add the PLL library function declarations to ti_hdmi.h. These will be shared >>> amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops >>> from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi >>> platform drivers and other libraries. >>> >>> Signed-off-by: Archit Taneja <archit@ti.com> >> >> Would be cool to see this convert to the common clock framework >> implementation (include/linux/clk-provider.h). It appears that this PLL >> only needs to support .enable, .disable and .recalc_rate callbacks at >> first glance. > > We've got a (very) long term plan to use CCF for DSS. And, if I'm not > mistaken, the PLL used for HDMI and DSI is the same as used elsewhere in > OMAP (I don't remember the PLL type name). > > I think the main issue with DSS clocks is that we require as exact > clocks as possible, and there are multiple dividers/multipliers on the > clock path. So we iterate over the divider/multiplier ranges, trying to > find as good freq match as possible. > > So if the clock path is composed from "black boxes", and we can only ask > for a certain frequency, and get back probably some other frequency, it > gets quite difficult to find good clock matches. Well, at least I > imagine so, I have not tried to implement that. I hope that the existing CLK_SET_RATE_PARENT flag could help you get the frequency you need; it causes a call to clk_set_rate(leaf_clk, target_rate) to walk up the chain of parents and configure rates as needed. However I have been looking at standardizing a way to define clock rate tables, possibly in DT. In many cases it is a board-specific issue (e.g. different oscillators or other reference clocks that feed into the SoC) and specifying rate tables in DT is one way to store known-good configurations for complex clock subtrees. Regards, Mike > > Tomi > > -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 17/09/13 22:40, Mike Turquette wrote: > I hope that the existing CLK_SET_RATE_PARENT flag could help you get > the frequency you need; it causes a call to clk_set_rate(leaf_clk, > target_rate) to walk up the chain of parents and configure rates as > needed. Hmm, I'm not quite sure what that does, but it doesn't sound like it would help. The only way I know to find good clocks is to iterate over the dividers for each clock on the path. And to complicate things, clocks from the "middle" of the path are used for some purposes, so it's not only the final clock that we're interested in. For example, DSI: DSI PLL produces a high freq clock that goes to DSI PHY. That one is used for the DSI bus clock. The high freq clock from DSI PLL also goes to two dividers, of which one goes to DSI IP, and the second goes to DISPC. DISPC clock is then divided with two dividers in row, and the resulting clock goes also to DSI IP. All those different clocks have restrictions and dependencies to each other, like this one needs to be higher than that one, or ClkA = N * ClkB, etc. > However I have been looking at standardizing a way to define clock > rate tables, possibly in DT. In many cases it is a board-specific > issue (e.g. different oscillators or other reference clocks that feed > into the SoC) and specifying rate tables in DT is one way to store > known-good configurations for complex clock subtrees. Well, for some boards, which only have an LCD, we could probably get the clock from a table. But if the display used (say, external monitor) supports multiple resolutions, such a table is not feasible. We could have some clocks there in the table, but we'd still need a dynamic way to figure out the dividers for other clock rates. Tomi
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index 56ce6bd..5ea65d3 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -10,5 +10,5 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o -omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o ti_hdmi_4xxx_ip.o +omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o ti_hdmi_4xxx_ip.o ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index db359e8..9ee92e9 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -797,10 +797,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .phy_enable = ti_hdmi_4xxx_phy_enable, .phy_disable = ti_hdmi_4xxx_phy_disable, .read_edid = ti_hdmi_4xxx_read_edid, - .pll_enable = ti_hdmi_4xxx_pll_enable, - .pll_disable = ti_hdmi_4xxx_pll_disable, .dump_core = ti_hdmi_4xxx_core_dump, - .dump_pll = ti_hdmi_4xxx_pll_dump, .dump_phy = ti_hdmi_4xxx_phy_dump, #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) .audio_start = ti_hdmi_4xxx_audio_start, diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index f2475fc..f6a2eba 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -42,7 +42,6 @@ #define HDMI_CORE_SYS 0x400 #define HDMI_CORE_AV 0x900 -#define HDMI_PLLCTRL 0x200 #define HDMI_PHY 0x300 /* HDMI EDID Length move this */ @@ -53,9 +52,6 @@ #define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 #define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 -#define HDMI_DEFAULT_REGN 16 -#define HDMI_DEFAULT_REGM2 1 - static struct { struct mutex lock; struct platform_device *pdev; @@ -428,52 +424,6 @@ end: return cm; } -static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, - struct hdmi_pll_info *pi) -{ - unsigned long clkin, refclk; - u32 mf; - - clkin = clk_get_rate(hdmi.sys_clk) / 10000; - /* - * Input clock is predivided by N + 1 - * out put of which is reference clk - */ - - pi->regn = HDMI_DEFAULT_REGN; - - refclk = clkin / pi->regn; - - pi->regm2 = HDMI_DEFAULT_REGM2; - - /* - * multiplier is pixel_clk/ref_clk - * Multiplying by 100 to avoid fractional part removal - */ - pi->regm = phy * pi->regm2 / refclk; - - /* - * 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; - - /* - * 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; - - /* Set the reference clock to sysclk reference */ - pi->refsel = HDMI_REFSEL_SYSCLK; - - DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); - DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); -} - static int hdmi_power_on_core(struct omap_dss_device *dssdev) { int r; @@ -526,12 +476,12 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) phy = p->pixel_clock; - hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); + hdmi_pll_compute(&hdmi.ip_data.pll, clk_get_rate(hdmi.sys_clk), phy); hdmi_wp_video_stop(&hdmi.ip_data.wp); /* config the PLL and PHY hdmi_set_pll_pwrfirst */ - r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); + r = hdmi_pll_enable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); if (r) { DSSDBG("Failed to lock PLL\n"); goto err_pll_enable; @@ -566,7 +516,7 @@ err_mgr_enable: err_vid_enable: hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); err_phy_enable: - hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); + hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); err_pll_enable: hdmi_power_off_core(dssdev); return -EIO; @@ -580,7 +530,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) hdmi_wp_video_stop(&hdmi.ip_data.wp); hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); - hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); + hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); hdmi_power_off_core(dssdev); } @@ -642,7 +592,7 @@ static void hdmi_dump_regs(struct seq_file *s) } hdmi_wp_dump(&hdmi.ip_data.wp, s); - hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s); + hdmi_pll_dump(&hdmi.ip_data.pll, s); hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s); hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s); @@ -1095,6 +1045,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) if (r) return r; + r = hdmi_pll_init(pdev, &hdmi.ip_data.pll); + if (r) + return r; + hdmi.ip_data.irq = platform_get_irq(pdev, 0); if (hdmi.ip_data.irq < 0) { DSSERR("platform_get_irq failed\n"); @@ -1111,7 +1065,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS; hdmi.ip_data.core_av_offset = HDMI_CORE_AV; - hdmi.ip_data.pll_offset = HDMI_PLLCTRL; hdmi.ip_data.phy_offset = HDMI_PHY; hdmi_init_output(pdev); diff --git a/drivers/video/omap2/dss/hdmi_pll.c b/drivers/video/omap2/dss/hdmi_pll.c new file mode 100644 index 0000000..d53b8e2 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi_pll.c @@ -0,0 +1,243 @@ +/* + * HDMI PLL + * + * Copyright (C) 2013 Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <video/omapdss.h> + +#include "dss.h" +#include "ti_hdmi.h" +#include "ti_hdmi_4xxx_ip.h" + +#define HDMI_DEFAULT_REGN 16 +#define HDMI_DEFAULT_REGM2 1 + +static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, + u32 val) +{ + __raw_writel(val, base_addr + idx); +} + +static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx) +{ + return __raw_readl(base_addr + idx); +} + +#define REG_FLD_MOD(base, idx, val, start, end) \ + hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\ + val, start, end)) +#define REG_GET(base, idx, start, end) \ + FLD_GET(hdmi_read_reg(base, idx), start, end) + +static inline int hdmi_wait_for_bit_change(void __iomem *base_addr, + const u16 idx, int b2, int b1, u32 val) +{ + u32 t = 0; + while (val != REG_GET(base_addr, idx, b2, b1)) { + udelay(1); + if (t++ > 10000) + return !val; + } + return val; +} + +void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) +{ +#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(pll->base, r)) + + DUMPPLL(PLLCTRL_PLL_CONTROL); + DUMPPLL(PLLCTRL_PLL_STATUS); + DUMPPLL(PLLCTRL_PLL_GO); + DUMPPLL(PLLCTRL_CFG1); + DUMPPLL(PLLCTRL_CFG2); + DUMPPLL(PLLCTRL_CFG3); + DUMPPLL(PLLCTRL_SSC_CFG1); + DUMPPLL(PLLCTRL_SSC_CFG2); + DUMPPLL(PLLCTRL_CFG4); +} + +void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy) +{ + struct hdmi_pll_info *pi = &pll->info; + unsigned long refclk; + u32 mf; + + /* use our funky units */ + clkin /= 10000; + + /* + * Input clock is predivided by N + 1 + * out put of which is reference clk + */ + + pi->regn = HDMI_DEFAULT_REGN; + + refclk = clkin / pi->regn; + + pi->regm2 = HDMI_DEFAULT_REGM2; + + /* + * multiplier is pixel_clk/ref_clk + * Multiplying by 100 to avoid fractional part removal + */ + pi->regm = phy * pi->regm2 / refclk; + + /* + * 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; + + /* + * 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; + + /* Set the reference clock to sysclk reference */ + pi->refsel = HDMI_REFSEL_SYSCLK; + + DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); + DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); +} + + +static int hdmi_pll_config(struct hdmi_pll_data *pll) +{ + u32 r; + struct hdmi_pll_info *fmt = &pll->info; + + /* PLL start always use manual mode */ + REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); + + r = hdmi_read_reg(pll->base, PLLCTRL_CFG1); + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ + r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ + hdmi_write_reg(pll->base, PLLCTRL_CFG1, r); + + r = hdmi_read_reg(pll->base, PLLCTRL_CFG2); + + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ + r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ + + if (fmt->dcofreq) { + /* divider programming for frequency beyond 1000Mhz */ + REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10); + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ + } else { + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ + } + + hdmi_write_reg(pll->base, PLLCTRL_CFG2, r); + + r = hdmi_read_reg(pll->base, PLLCTRL_CFG4); + r = FLD_MOD(r, fmt->regm2, 24, 18); + r = FLD_MOD(r, fmt->regmf, 17, 0); + hdmi_write_reg(pll->base, PLLCTRL_CFG4, r); + + /* go now */ + REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0); + + /* wait for bit change */ + if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO, + 0, 0, 1) != 1) { + pr_err("PLL GO bit not set\n"); + return -ETIMEDOUT; + } + + /* Wait till the lock bit is set in PLL status */ + if (hdmi_wait_for_bit_change(pll->base, + PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { + pr_err("cannot lock PLL\n"); + pr_err("CFG1 0x%x\n", + hdmi_read_reg(pll->base, PLLCTRL_CFG1)); + pr_err("CFG2 0x%x\n", + hdmi_read_reg(pll->base, PLLCTRL_CFG2)); + pr_err("CFG4 0x%x\n", + hdmi_read_reg(pll->base, PLLCTRL_CFG4)); + return -ETIMEDOUT; + } + + pr_debug("PLL locked!\n"); + + return 0; +} + +static int hdmi_pll_reset(struct hdmi_pll_data *pll) +{ + /* SYSRESET controlled by power FSM */ + REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 3, 3); + + /* READ 0x0 reset is in progress */ + if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1) + != 1) { + pr_err("Failed to sysreset PLL\n"); + return -ETIMEDOUT; + } + + return 0; +} + +int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) +{ + u16 r = 0; + + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); + if (r) + return r; + + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); + if (r) + return r; + + r = hdmi_pll_reset(pll); + if (r) + return r; + + r = hdmi_pll_config(pll); + if (r) + return r; + + return 0; +} + +void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) +{ + hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); +} + +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_pllctrl"); + if (!res) { + DSSERR("can't get PLL CTRL IORESOURCE_MEM HDMI\n"); + return -EINVAL; + } + + pll->base = devm_request_and_ioremap(&pdev->dev, res); + if (!pll->base) { + DSSERR("can't ioremap PLL ctrl\n"); + return -ENOMEM; + } + + return 0; +} diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index d16f28d..62a83c7 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -155,14 +155,8 @@ struct ti_hdmi_ip_ops { int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len); - int (*pll_enable)(struct hdmi_ip_data *ip_data); - - void (*pll_disable)(struct hdmi_ip_data *ip_data); - void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s); - void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s); - void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) @@ -223,17 +217,22 @@ struct hdmi_wp_data { void __iomem *base; }; +struct hdmi_pll_data { + void __iomem *base; + + struct hdmi_pll_info info; +}; + struct hdmi_ip_data { struct hdmi_wp_data wp; + struct hdmi_pll_data pll; unsigned long core_sys_offset; unsigned long core_av_offset; - unsigned long pll_offset; unsigned long phy_offset; int irq; const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; - struct hdmi_pll_info pll_data; struct hdmi_core_infoframe_avi avi_cfg; /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ @@ -260,13 +259,17 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, struct omap_video_timings *timings, struct hdmi_config *param); int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp); +/* HDMI PLL funcs */ +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); +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); + int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); -int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index d4b8883..8cfb54b 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -75,11 +75,6 @@ static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data) return ip_data->wp.base + ip_data->phy_offset; } -static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data) -{ - return ip_data->wp.base + ip_data->pll_offset; -} - static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data) { return ip_data->wp.base + ip_data->core_av_offset; @@ -90,117 +85,6 @@ static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data) return ip_data->wp.base + ip_data->core_sys_offset; } - -static int hdmi_pll_init(struct hdmi_ip_data *ip_data) -{ - u32 r; - void __iomem *pll_base = hdmi_pll_base(ip_data); - struct hdmi_pll_info *fmt = &ip_data->pll_data; - - /* PLL start always use manual mode */ - REG_FLD_MOD(pll_base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); - - r = hdmi_read_reg(pll_base, PLLCTRL_CFG1); - r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ - r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ - - hdmi_write_reg(pll_base, PLLCTRL_CFG1, r); - - r = hdmi_read_reg(pll_base, PLLCTRL_CFG2); - - r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ - r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ - r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ - r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ - - if (fmt->dcofreq) { - /* divider programming for frequency beyond 1000Mhz */ - REG_FLD_MOD(pll_base, PLLCTRL_CFG3, fmt->regsd, 17, 10); - r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ - } else { - r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ - } - - hdmi_write_reg(pll_base, PLLCTRL_CFG2, r); - - r = hdmi_read_reg(pll_base, PLLCTRL_CFG4); - r = FLD_MOD(r, fmt->regm2, 24, 18); - r = FLD_MOD(r, fmt->regmf, 17, 0); - - hdmi_write_reg(pll_base, PLLCTRL_CFG4, r); - - /* go now */ - REG_FLD_MOD(pll_base, PLLCTRL_PLL_GO, 0x1, 0, 0); - - /* wait for bit change */ - if (hdmi_wait_for_bit_change(pll_base, PLLCTRL_PLL_GO, - 0, 0, 1) != 1) { - pr_err("PLL GO bit not set\n"); - return -ETIMEDOUT; - } - - /* Wait till the lock bit is set in PLL status */ - if (hdmi_wait_for_bit_change(pll_base, - PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { - pr_err("cannot lock PLL\n"); - pr_err("CFG1 0x%x\n", - hdmi_read_reg(pll_base, PLLCTRL_CFG1)); - pr_err("CFG2 0x%x\n", - hdmi_read_reg(pll_base, PLLCTRL_CFG2)); - pr_err("CFG4 0x%x\n", - hdmi_read_reg(pll_base, PLLCTRL_CFG4)); - return -ETIMEDOUT; - } - - pr_debug("PLL locked!\n"); - - return 0; -} - - -static int hdmi_pll_reset(struct hdmi_ip_data *ip_data) -{ - /* SYSRESET controlled by power FSM */ - REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3); - - /* READ 0x0 reset is in progress */ - if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data), - PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) { - pr_err("Failed to sysreset PLL\n"); - return -ETIMEDOUT; - } - - return 0; -} - -int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data) -{ - u16 r = 0; - - r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF); - if (r) - return r; - - r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); - if (r) - return r; - - r = hdmi_pll_reset(ip_data); - if (r) - return r; - - r = hdmi_pll_init(ip_data); - if (r) - return r; - - return 0; -} - -void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) -{ - hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF); -} - static irqreturn_t hdmi_irq_handler(int irq, void *data) { struct hdmi_ip_data *ip_data = data; @@ -717,22 +601,6 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) hdmi_core_av_packet_config(ip_data, repeat_cfg); } -void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) -{ -#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ - hdmi_read_reg(hdmi_pll_base(ip_data), r)) - - DUMPPLL(PLLCTRL_PLL_CONTROL); - DUMPPLL(PLLCTRL_PLL_STATUS); - DUMPPLL(PLLCTRL_PLL_GO); - DUMPPLL(PLLCTRL_CFG1); - DUMPPLL(PLLCTRL_CFG2); - DUMPPLL(PLLCTRL_CFG3); - DUMPPLL(PLLCTRL_SSC_CFG1); - DUMPPLL(PLLCTRL_SSC_CFG2); - DUMPPLL(PLLCTRL_CFG4); -} - void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) { int i;
HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file. These funcs are called directly from the hdmi driver rather than hdmi_ip_ops function pointer calls. Add the PLL library function declarations to ti_hdmi.h. These will be shared amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi platform drivers and other libraries. Signed-off-by: Archit Taneja <archit@ti.com> --- drivers/video/omap2/dss/Makefile | 2 +- drivers/video/omap2/dss/dss_features.c | 3 - drivers/video/omap2/dss/hdmi.c | 65 ++------ drivers/video/omap2/dss/hdmi_pll.c | 243 ++++++++++++++++++++++++++++++ drivers/video/omap2/dss/ti_hdmi.h | 25 +-- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 132 ---------------- 6 files changed, 267 insertions(+), 203 deletions(-) create mode 100644 drivers/video/omap2/dss/hdmi_pll.c