[PATCH 4/6] i.MX23: Add pixel clock calculation routine for framebuffer support

Juergen Beisert jbe at pengutronix.de
Mon Nov 22 07:24:23 EST 2010


Signed-off-by: Juergen Beisert <jbe at pengutronix.de>
---
 arch/arm/mach-stm/include/mach/clock.h |    2 +
 arch/arm/mach-stm/speed-imx23.c        |  100 +++++++++++++++++++++++++++++++-
 2 files changed, 101 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-stm/include/mach/clock.h b/arch/arm/mach-stm/include/mach/clock.h
index 0e1a6d6..99b0d99 100644
--- a/arch/arm/mach-stm/include/mach/clock.h
+++ b/arch/arm/mach-stm/include/mach/clock.h
@@ -29,6 +29,8 @@ unsigned imx_get_xclk(void);
 unsigned imx_get_sspclk(unsigned);
 unsigned imx_set_sspclk(unsigned, unsigned, int);
 unsigned imx_set_ioclk(unsigned);
+unsigned imx_set_lcdifclk(unsigned);
+unsigned imx_get_lcdifclk(void);
 
 #endif /* ASM_ARCH_CLOCK_IMX23_H */
 
diff --git a/arch/arm/mach-stm/speed-imx23.c b/arch/arm/mach-stm/speed-imx23.c
index 7418ad5..0f68f7d 100644
--- a/arch/arm/mach-stm/speed-imx23.c
+++ b/arch/arm/mach-stm/speed-imx23.c
@@ -39,7 +39,11 @@
 #define HW_CLKCTRL_HBUS 0x30
 #define HW_CLKCTRL_XBUS 0x40
 #define HW_CLKCTRL_XTAL 0x050
-#define HW_CLKCTRL_PIX 0x060
+#define HW_CLKCTRL_DIS_LCDIF 0x060
+# define CLKCTRL_DIS_LCDIF_GATE (1 << 31)
+# define CLKCTRL_DIS_LCDIF_BUSY (1 << 29)
+# define SET_DIS_LCDIF_DIV(x) ((x) & 0xfff)
+# define GET_DIS_LCDIF_DIV(x) ((x) & 0xfff)
 /* note: no set/clear register! */
 #define HW_CLKCTRL_SSP 0x070
 /* note: no set/clear register! */
@@ -66,6 +70,7 @@
 # define SET_IOFRAC(x) (((x) & 0x3f) << 24)
 # define CLKCTRL_FRAC_CLKGATEPIX (1 << 23)
 # define GET_PIXFRAC(x) (((x) >> 16) & 0x3f)
+# define SET_PIXFRAC(x) (((x) & 0x3f) << 16)
 # define CLKCTRL_FRAC_CLKGATEEMI (1 << 15)
 # define GET_EMIFRAC(x) (((x) >> 8) & 0x3f)
 # define CLKCTRL_FRAC_CLKGATECPU (1 << 7)
@@ -77,6 +82,7 @@
 # define CLKCTRL_CLKSEQ_BYPASS_EMI (1 << 6)
 # define CLKCTRL_CLKSEQ_BYPASS_SSP (1 << 5)
 # define CLKCTRL_CLKSEQ_BYPASS_GPMI (1 << 4)
+# define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 1)
 #define HW_CLKCTRL_RESET 0x120
 #define HW_CLKCTRL_STATUS 0x130
 #define HW_CLKCTRL_VERSION 0x140
@@ -268,6 +274,97 @@ unsigned imx_set_sspclk(unsigned index, unsigned nc, int high)
 	return imx_get_sspclk(index);
 }
 
+unsigned imx_get_lcdifclk(void)
+{
+	unsigned rate = imx_get_mpllclk() * 18U;
+	unsigned div;
+
+	div = GET_PIXFRAC(readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC));
+	if (div != 0U) {
+		rate /= div;
+		div = GET_DIS_LCDIF_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF));
+		if (div != 0U)
+			rate /= div;
+		else
+			pr_debug("LCDIF clock has divisor 0!\n");
+	} else
+		pr_debug("LCDIF clock has frac divisor 0!\n");
+
+	return rate;
+}
+
+/**
+ * @param nc Pixel clock in [kHz]
+ *
+ * Calculate the best settings for the fractional and integer divider to match
+ * the requested pixel clock as close as possible.
+ *
+ * pixel clock = 480 MHz * 18 / frac_div / int_div
+ */
+unsigned imx_set_lcdifclk(unsigned nc)
+{
+	unsigned frac, best_frac = 0, div, best_div = 0, result;
+	int delta, best_delta = 0xffffff;
+	unsigned i, parent_rate = imx_get_mpllclk();
+	uint32_t reg;
+
+#define DIV(NOM, DEN) (((NOM) + (DEN) / 2) / (DEN))
+#define SH_DIV(NOM, DEN, LSH) \
+	((((NOM) / (DEN)) << (LSH)) + DIV(((NOM) % (DEN)) << (LSH), DEN))
+#define ABS(x) (((x) < 0) ? (-(x)) : (x))
+#define SHIFT 4
+
+	nc <<= SHIFT;
+
+	for (frac = 18; frac <= 35; ++frac) {
+		for (div = 1; div <= 255; ++div) {
+			result = DIV(parent_rate * SH_DIV(18U, frac, SHIFT), div);
+			delta = nc - result;
+			if (ABS(delta) < ABS(best_delta)) {
+				best_delta = delta;
+				best_frac = frac;
+				best_div = div;
+			}
+		}
+	}
+
+	if (best_delta == 0xffffff) {
+		pr_debug("Unable to match the pixelclock\n");
+		return 0;
+	}
+
+	pr_debug("Programming PFD=%u,DIV=%u ref_pix=%u MHz PIXCLK=%u MHz\n",
+			best_frac, best_div, 480 * 18 / best_frac,
+			480 * 18 / best_frac / best_div);
+
+	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC) & ~0x003f0000;
+	reg |= SET_PIXFRAC(best_frac);
+	writel(reg, IMX_CCM_BASE + HW_CLKCTRL_FRAC);
+	writel(reg & ~CLKCTRL_FRAC_CLKGATEPIX, IMX_CCM_BASE + HW_CLKCTRL_FRAC);
+
+	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF) & ~0x1fff;
+	reg &= ~CLKCTRL_DIS_LCDIF_GATE;
+	reg |= SET_DIS_LCDIF_DIV(best_div);
+	writel(reg, IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF);
+
+	/* Wait for divider update */
+	for (i = 0; i < 10000; i++) {
+		if (!(readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF) &
+						CLKCTRL_DIS_LCDIF_BUSY))
+			break;
+	}
+
+	if (i >= 10000) {
+		pr_debug("Setting LCD clock failed\n");
+		return 0;
+	}
+
+	writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF,
+				IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + 8);
+
+	return imx_get_lcdifclk();
+}
+
 void imx_dump_clocks(void)
 {
 	printf("mpll:    %10u kHz\n", imx_get_mpllclk());
@@ -277,4 +374,5 @@ void imx_dump_clocks(void)
 	printf("hclk:    %10u kHz\n", imx_get_hclk());
 	printf("xclk:    %10u kHz\n", imx_get_xclk());
 	printf("ssp:     %10u kHz\n", imx_get_sspclk(0));
+	printf("lcdif:   %10u kHz\n", imx_get_lcdifclk());
 }
-- 
1.7.2.3




More information about the barebox mailing list