imx: mx6: add clock api for lcdif
Implement mxs_set_lcdclk, enable_lcdif_clock and enable_pll_video. The three API can be used to configure lcdif related clock when CONFIG_VIDEO_MXS enabled. Signed-off-by: Peng Fan <Peng.Fan@freescale.com> Cc: Stefano Babic <sbabic@denx.de>
This commit is contained in:
parent
64ffef05e0
commit
ad153782e0
@ -473,6 +473,251 @@ static u32 get_mmdc_ch0_clk(void)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_VIDEO_MXS)
|
||||
static int enable_pll_video(u32 pll_div, u32 pll_num, u32 pll_denom,
|
||||
u32 post_div)
|
||||
{
|
||||
u32 reg = 0;
|
||||
ulong start;
|
||||
|
||||
debug("pll5 div = %d, num = %d, denom = %d\n",
|
||||
pll_div, pll_num, pll_denom);
|
||||
|
||||
/* Power up PLL5 video */
|
||||
writel(BM_ANADIG_PLL_VIDEO_POWERDOWN |
|
||||
BM_ANADIG_PLL_VIDEO_BYPASS |
|
||||
BM_ANADIG_PLL_VIDEO_DIV_SELECT |
|
||||
BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT,
|
||||
&imx_ccm->analog_pll_video_clr);
|
||||
|
||||
/* Set div, num and denom */
|
||||
switch (post_div) {
|
||||
case 1:
|
||||
writel(BF_ANADIG_PLL_VIDEO_DIV_SELECT(pll_div) |
|
||||
BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0x2),
|
||||
&imx_ccm->analog_pll_video_set);
|
||||
break;
|
||||
case 2:
|
||||
writel(BF_ANADIG_PLL_VIDEO_DIV_SELECT(pll_div) |
|
||||
BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0x1),
|
||||
&imx_ccm->analog_pll_video_set);
|
||||
break;
|
||||
case 4:
|
||||
writel(BF_ANADIG_PLL_VIDEO_DIV_SELECT(pll_div) |
|
||||
BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0x0),
|
||||
&imx_ccm->analog_pll_video_set);
|
||||
break;
|
||||
default:
|
||||
puts("Wrong test_div!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(BF_ANADIG_PLL_VIDEO_NUM_A(pll_num),
|
||||
&imx_ccm->analog_pll_video_num);
|
||||
writel(BF_ANADIG_PLL_VIDEO_DENOM_B(pll_denom),
|
||||
&imx_ccm->analog_pll_video_denom);
|
||||
|
||||
/* Wait PLL5 lock */
|
||||
start = get_timer(0); /* Get current timestamp */
|
||||
|
||||
do {
|
||||
reg = readl(&imx_ccm->analog_pll_video);
|
||||
if (reg & BM_ANADIG_PLL_VIDEO_LOCK) {
|
||||
/* Enable PLL out */
|
||||
writel(BM_ANADIG_PLL_VIDEO_ENABLE,
|
||||
&imx_ccm->analog_pll_video_set);
|
||||
return 0;
|
||||
}
|
||||
} while (get_timer(0) < (start + 10)); /* Wait 10ms */
|
||||
|
||||
puts("Lock PLL5 timeout\n");
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* 24M--> PLL_VIDEO -> LCDIFx_PRED -> LCDIFx_PODF -> LCD
|
||||
*
|
||||
* 'freq' using KHz as unit, see driver/video/mxsfb.c.
|
||||
*/
|
||||
void mxs_set_lcdclk(u32 base_addr, u32 freq)
|
||||
{
|
||||
u32 reg = 0;
|
||||
u32 hck = MXC_HCLK / 1000;
|
||||
/* DIV_SELECT ranges from 27 to 54 */
|
||||
u32 min = hck * 27;
|
||||
u32 max = hck * 54;
|
||||
u32 temp, best = 0;
|
||||
u32 i, j, max_pred = 8, max_postd = 8, pred = 1, postd = 1;
|
||||
u32 pll_div, pll_num, pll_denom, post_div = 1;
|
||||
|
||||
debug("mxs_set_lcdclk, freq = %dKHz\n", freq);
|
||||
|
||||
if ((!is_cpu_type(MXC_CPU_MX6SX)) && !is_cpu_type(MXC_CPU_MX6UL)) {
|
||||
debug("This chip not support lcd!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (base_addr == LCDIF1_BASE_ADDR) {
|
||||
reg = readl(&imx_ccm->cscdr2);
|
||||
/* Can't change clocks when clock not from pre-mux */
|
||||
if ((reg & MXC_CCM_CSCDR2_LCDIF1_CLK_SEL_MASK) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_cpu_type(MXC_CPU_MX6SX)) {
|
||||
reg = readl(&imx_ccm->cscdr2);
|
||||
/* Can't change clocks when clock not from pre-mux */
|
||||
if ((reg & MXC_CCM_CSCDR2_LCDIF2_CLK_SEL_MASK) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
temp = freq * max_pred * max_postd;
|
||||
if (temp > max) {
|
||||
puts("Please decrease freq, too large!\n");
|
||||
return;
|
||||
}
|
||||
if (temp < min) {
|
||||
/*
|
||||
* Register: PLL_VIDEO
|
||||
* Bit Field: POST_DIV_SELECT
|
||||
* 00 — Divide by 4.
|
||||
* 01 — Divide by 2.
|
||||
* 10 — Divide by 1.
|
||||
* 11 — Reserved
|
||||
* No need to check post_div(1)
|
||||
*/
|
||||
for (post_div = 2; post_div <= 4; post_div <<= 1) {
|
||||
if ((temp * post_div) > min) {
|
||||
freq *= post_div;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (post_div > 4) {
|
||||
printf("Fail to set rate to %dkhz", freq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Choose the best pred and postd to match freq for lcd */
|
||||
for (i = 1; i <= max_pred; i++) {
|
||||
for (j = 1; j <= max_postd; j++) {
|
||||
temp = freq * i * j;
|
||||
if (temp > max || temp < min)
|
||||
continue;
|
||||
if (best == 0 || temp < best) {
|
||||
best = temp;
|
||||
pred = i;
|
||||
postd = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == 0) {
|
||||
printf("Fail to set rate to %dKHz", freq);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("best %d, pred = %d, postd = %d\n", best, pred, postd);
|
||||
|
||||
pll_div = best / hck;
|
||||
pll_denom = 1000000;
|
||||
pll_num = (best - hck * pll_div) * pll_denom / hck;
|
||||
|
||||
/*
|
||||
* pll_num
|
||||
* (24MHz * (pll_div + --------- ))
|
||||
* pll_denom
|
||||
*freq KHz = --------------------------------
|
||||
* post_div * pred * postd * 1000
|
||||
*/
|
||||
|
||||
if (base_addr == LCDIF1_BASE_ADDR) {
|
||||
if (enable_pll_video(pll_div, pll_num, pll_denom, post_div))
|
||||
return;
|
||||
|
||||
/* Select pre-lcd clock to PLL5 and set pre divider */
|
||||
clrsetbits_le32(&imx_ccm->cscdr2,
|
||||
MXC_CCM_CSCDR2_LCDIF1_PRED_SEL_MASK |
|
||||
MXC_CCM_CSCDR2_LCDIF1_PRE_DIV_MASK,
|
||||
(0x2 << MXC_CCM_CSCDR2_LCDIF1_PRED_SEL_OFFSET) |
|
||||
((pred - 1) <<
|
||||
MXC_CCM_CSCDR2_LCDIF1_PRE_DIV_OFFSET));
|
||||
|
||||
/* Set the post divider */
|
||||
clrsetbits_le32(&imx_ccm->cbcmr,
|
||||
MXC_CCM_CBCMR_LCDIF1_PODF_MASK,
|
||||
((postd - 1) <<
|
||||
MXC_CCM_CBCMR_LCDIF1_PODF_OFFSET));
|
||||
} else if (is_cpu_type(MXC_CPU_MX6SX)) {
|
||||
/* Setting LCDIF2 for i.MX6SX */
|
||||
if (enable_pll_video(pll_div, pll_num, pll_denom, post_div))
|
||||
return;
|
||||
|
||||
/* Select pre-lcd clock to PLL5 and set pre divider */
|
||||
clrsetbits_le32(&imx_ccm->cscdr2,
|
||||
MXC_CCM_CSCDR2_LCDIF2_PRED_SEL_MASK |
|
||||
MXC_CCM_CSCDR2_LCDIF2_PRE_DIV_MASK,
|
||||
(0x2 << MXC_CCM_CSCDR2_LCDIF2_PRED_SEL_OFFSET) |
|
||||
((pred - 1) <<
|
||||
MXC_CCM_CSCDR2_LCDIF2_PRE_DIV_OFFSET));
|
||||
|
||||
/* Set the post divider */
|
||||
clrsetbits_le32(&imx_ccm->cscmr1,
|
||||
MXC_CCM_CSCMR1_LCDIF2_PODF_MASK,
|
||||
((postd - 1) <<
|
||||
MXC_CCM_CSCMR1_LCDIF2_PODF_OFFSET));
|
||||
}
|
||||
}
|
||||
|
||||
int enable_lcdif_clock(u32 base_addr)
|
||||
{
|
||||
u32 reg = 0;
|
||||
u32 lcdif_clk_sel_mask, lcdif_ccgr3_mask;
|
||||
|
||||
if (is_cpu_type(MXC_CPU_MX6SX)) {
|
||||
if ((base_addr == LCDIF1_BASE_ADDR) ||
|
||||
(base_addr == LCDIF2_BASE_ADDR)) {
|
||||
puts("Wrong LCD interface!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Set to pre-mux clock at default */
|
||||
lcdif_clk_sel_mask = (base_addr == LCDIF2_BASE_ADDR) ?
|
||||
MXC_CCM_CSCDR2_LCDIF2_CLK_SEL_MASK :
|
||||
MXC_CCM_CSCDR2_LCDIF1_CLK_SEL_MASK;
|
||||
lcdif_ccgr3_mask = (base_addr == LCDIF2_BASE_ADDR) ?
|
||||
(MXC_CCM_CCGR3_LCDIF2_PIX_MASK |
|
||||
MXC_CCM_CCGR3_DISP_AXI_MASK) :
|
||||
(MXC_CCM_CCGR3_LCDIF1_PIX_MASK |
|
||||
MXC_CCM_CCGR3_DISP_AXI_MASK);
|
||||
} else if (is_cpu_type(MXC_CPU_MX6UL)) {
|
||||
if (base_addr != LCDIF1_BASE_ADDR) {
|
||||
puts("Wrong LCD interface!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Set to pre-mux clock at default */
|
||||
lcdif_clk_sel_mask = MXC_CCM_CSCDR2_LCDIF1_CLK_SEL_MASK;
|
||||
lcdif_ccgr3_mask = MXC_CCM_CCGR3_LCDIF1_PIX_MASK;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
reg = readl(&imx_ccm->cscdr2);
|
||||
reg &= ~lcdif_clk_sel_mask;
|
||||
writel(reg, &imx_ccm->cscdr2);
|
||||
|
||||
/* Enable the LCDIF pix clock */
|
||||
reg = readl(&imx_ccm->CCGR3);
|
||||
reg |= lcdif_ccgr3_mask;
|
||||
writel(reg, &imx_ccm->CCGR3);
|
||||
|
||||
reg = readl(&imx_ccm->CCGR2);
|
||||
reg |= MXC_CCM_CCGR2_LCD_MASK;
|
||||
writel(reg, &imx_ccm->CCGR2);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSL_QSPI
|
||||
/* qspi_num can be from 0 - 1 */
|
||||
void enable_qspi_clk(int qspi_num)
|
||||
|
@ -66,6 +66,8 @@ int enable_spi_clk(unsigned char enable, unsigned spi_num);
|
||||
void enable_ipu_clock(void);
|
||||
int enable_fec_anatop_clock(int fec_id, enum enet_freq freq);
|
||||
void enable_enet_clk(unsigned char enable);
|
||||
int enable_lcdif_clock(u32 base_addr);
|
||||
void enable_qspi_clk(int qspi_num);
|
||||
void enable_thermal_clk(void);
|
||||
void mxs_set_lcdclk(u32 base_addr, u32 freq);
|
||||
#endif /* __ASM_ARCH_CLOCK_H */
|
||||
|
Loading…
Reference in New Issue
Block a user