]> git.sur5r.net Git - u-boot/commitdiff
imx: mx6: add clock api for lcdif
authorPeng Fan <Peng.Fan@freescale.com>
Thu, 29 Oct 2015 07:54:47 +0000 (15:54 +0800)
committerStefano Babic <sbabic@denx.de>
Thu, 12 Nov 2015 16:40:53 +0000 (17:40 +0100)
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>
arch/arm/cpu/armv7/mx6/clock.c
arch/arm/include/asm/arch-mx6/clock.h

index 11efd12c9a7764770f1fbedab2cf47a66f9c4ef3..67e0f3252f0cf15f3a482ea19d1c28555f33dcdd 100644 (file)
@@ -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)
index 2b220d6f8f97d30d2054388351c828a6d7362a74..14505239e856b415f7f0a943a56aea269c089f6c 100644 (file)
@@ -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 */