summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJuergen Beisert <jbe@pengutronix.de>2010-12-20 16:05:06 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2010-12-21 08:26:08 +0100
commit7aec19c9b7e8236dc459e9c99c668de55ce16e49 (patch)
treee9c3b11e1ec70bc9dc29804d661060751ec840b9 /arch
parent30aa4efb57475ff768f6d43051ee78160f32ce0f (diff)
downloadbarebox-7aec19c9b7e8236dc459e9c99c668de55ce16e49.tar.gz
barebox-7aec19c9b7e8236dc459e9c99c668de55ce16e49.tar.xz
ARM STM/i.MX: Add a pixel clock calculation routine for i.MX23/i.MX28
In order to support video graphics output on i.MX23/i.MX28 based platforms, a calculation routine for the pixel clock is required. Signed-off-by: Juergen Beisert <jbe@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-stm/Makefile1
-rw-r--r--arch/arm/mach-stm/imx_lcd_clk.c149
-rw-r--r--arch/arm/mach-stm/include/mach/clock-imx23.h2
-rw-r--r--arch/arm/mach-stm/include/mach/clock-imx28.h2
4 files changed, 154 insertions, 0 deletions
diff --git a/arch/arm/mach-stm/Makefile b/arch/arm/mach-stm/Makefile
index 3c7ce099e6..0ff8fd16d6 100644
--- a/arch/arm/mach-stm/Makefile
+++ b/arch/arm/mach-stm/Makefile
@@ -1,3 +1,4 @@
obj-y += imx.o iomux-imx.o reset-imx.o
+obj-$(CONFIG_DRIVER_VIDEO_STM) += imx_lcd_clk.o
obj-$(CONFIG_ARCH_IMX23) += speed-imx23.o clocksource-imx23.o
obj-$(CONFIG_ARCH_IMX28) += speed-imx28.o clocksource-imx28.o
diff --git a/arch/arm/mach-stm/imx_lcd_clk.c b/arch/arm/mach-stm/imx_lcd_clk.c
new file mode 100644
index 0000000000..89386640ad
--- /dev/null
+++ b/arch/arm/mach-stm/imx_lcd_clk.c
@@ -0,0 +1,149 @@
+/*
+ * (C) Copyright 2010 Juergen Beisert - Pengutronix <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <mach/imx-regs.h>
+#include <mach/clock.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_ARCH_IMX23
+
+# define HW_CLKCTRL_DIS_LCDIF 0x060
+# define CLKCTRL_DIS_LCDIF_GATE (1 << 31)
+# define CLKCTRL_DIS_LCDIF_BUSY (1 << 29)
+# define MASK_DIS_LCDIF_DIV 0xfff
+# define SET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV)
+# define GET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV)
+
+# define HW_CLKCTRL_FRAC 0xf0
+# define MASK_PIXFRAC 0x3f
+# define GET_PIXFRAC(x) (((x) >> 16) & MASK_PIXFRAC)
+# define SET_PIXFRAC(x) (((x) & MASK_PIXFRAC) << 16)
+# define CLKCTRL_FRAC_CLKGATEPIX (1 << 23)
+
+# define HW_CLKCTRL_CLKSEQ 0x110
+# define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 1)
+
+#endif
+
+#ifdef CONFIG_ARCH_IMX28
+
+# define HW_CLKCTRL_DIS_LCDIF 0x120
+# define CLKCTRL_DIS_LCDIF_GATE (1 << 31)
+# define CLKCTRL_DIS_LCDIF_BUSY (1 << 29)
+# define MASK_DIS_LCDIF_DIV 0x1fff
+# define SET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV)
+# define GET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV)
+
+/* note: On i.MX28 this is called 'FRAC1' */
+# define HW_CLKCTRL_FRAC 0x1c0
+# define MASK_PIXFRAC 0x3f
+# define GET_PIXFRAC(x) ((x) & MASK_PIXFRAC)
+# define SET_PIXFRAC(x) ((x) & MASK_PIXFRAC)
+# define CLKCTRL_FRAC_CLKGATEPIX (1 << 7)
+
+# define HW_CLKCTRL_CLKSEQ 0x1d0
+# define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 14)
+
+#endif
+
+unsigned imx_get_lcdifclk(void)
+{
+ unsigned rate = (imx_get_mpllclk() / 1000) * 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 * 1000;
+}
+
+/*
+ * The source of the pixel clock can be the external 24 MHz crystal or the
+ * internal PLL running at 480 MHz. In order to support at least VGA sized
+ * displays/resolutions this routine forces the PLL as the clock source.
+ */
+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() / 1000;
+ uint32_t reg;
+
+#define SH_DIV(NOM, DEN, LSH) ((((NOM) / (DEN)) << (LSH)) + \
+ DIV_ROUND_CLOSEST(((NOM) % (DEN)) << (LSH), DEN))
+#define SHIFT 4
+
+ nc /= 1000;
+ nc <<= SHIFT;
+
+ for (frac = 18; frac <= 35; ++frac) {
+ for (div = 1; div <= 255; ++div) {
+ result = DIV_ROUND_CLOSEST(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 kHz\n",
+ best_frac, best_div, 480 * 18 / best_frac,
+ 480000 * 18 / best_frac / best_div);
+
+ reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC) & ~MASK_PIXFRAC;
+ 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) & ~MASK_DIS_LCDIF_DIV;
+ 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 + BIT_CLR);
+
+ return imx_get_lcdifclk();
+}
diff --git a/arch/arm/mach-stm/include/mach/clock-imx23.h b/arch/arm/mach-stm/include/mach/clock-imx23.h
index bb499f2557..723f343566 100644
--- a/arch/arm/mach-stm/include/mach/clock-imx23.h
+++ b/arch/arm/mach-stm/include/mach/clock-imx23.h
@@ -22,5 +22,7 @@ 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 /* MACH_CLOCK_IMX23_H */
diff --git a/arch/arm/mach-stm/include/mach/clock-imx28.h b/arch/arm/mach-stm/include/mach/clock-imx28.h
index afc9fb63e9..45fb043ac4 100644
--- a/arch/arm/mach-stm/include/mach/clock-imx28.h
+++ b/arch/arm/mach-stm/include/mach/clock-imx28.h
@@ -22,6 +22,8 @@ unsigned imx_get_xclk(void);
unsigned imx_get_sspclk(unsigned);
unsigned imx_set_sspclk(unsigned, unsigned, int);
unsigned imx_set_ioclk(unsigned, unsigned);
+unsigned imx_set_lcdifclk(unsigned);
+unsigned imx_get_lcdifclk(void);
unsigned imx_get_fecclk(void);
void imx_enable_enetclk(void);