ep93xx video driver platform support
authorRyan Mallon <ryan@bluewatersys.com>
Tue, 22 Sep 2009 23:47:09 +0000 (16:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Sep 2009 14:39:51 +0000 (07:39 -0700)
Signed-off-by: Ryan Mallon <ryan@bluewatersys.com>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Daniele Venzano <linux@brownhat.org>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Krzysztof Helt <krzysztof.h1@poczta.fm>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/arm/mach-ep93xx/clock.c
arch/arm/mach-ep93xx/core.c
arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
arch/arm/mach-ep93xx/include/mach/fb.h [new file with mode: 0644]
arch/arm/mach-ep93xx/include/mach/platform.h

index 3dd0e2a23095c8c6b57ed493baf5e2655c7a6d63..dda19cd76194fd4334785dea7af869713d6db467 100644 (file)
@@ -37,7 +37,7 @@ struct clk {
 static unsigned long get_uart_rate(struct clk *clk);
 
 static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
-
+static int set_div_rate(struct clk *clk, unsigned long rate);
 
 static struct clk clk_uart1 = {
        .sw_locked      = 1,
@@ -76,6 +76,13 @@ static struct clk clk_pwm = {
        .rate           = EP93XX_EXT_CLK_RATE,
 };
 
+static struct clk clk_video = {
+       .sw_locked      = 1,
+       .enable_reg     = EP93XX_SYSCON_VIDCLKDIV,
+       .enable_mask    = EP93XX_SYSCON_CLKDIV_ENABLE,
+       .set_rate       = set_div_rate,
+};
+
 /* DMA Clocks */
 static struct clk clk_m2p0 = {
        .enable_reg     = EP93XX_SYSCON_PWRCNT,
@@ -140,6 +147,7 @@ static struct clk_lookup clocks[] = {
        INIT_CK(NULL,                   "pll2",         &clk_pll2),
        INIT_CK("ep93xx-ohci",          NULL,           &clk_usb_host),
        INIT_CK("ep93xx-keypad",        NULL,           &clk_keypad),
+       INIT_CK("ep93xx-fb",            NULL,           &clk_video),
        INIT_CK(NULL,                   "pwm_clk",      &clk_pwm),
        INIT_CK(NULL,                   "m2p0",         &clk_m2p0),
        INIT_CK(NULL,                   "m2p1",         &clk_m2p1),
@@ -236,6 +244,84 @@ static int set_keytchclk_rate(struct clk *clk, unsigned long rate)
        return 0;
 }
 
+static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel,
+                                 int *pdiv, int *div)
+{
+       unsigned long max_rate, best_rate = 0,
+               actual_rate = 0, mclk_rate = 0, rate_err = -1;
+       int i, found = 0, __div = 0, __pdiv = 0;
+
+       /* Don't exceed the maximum rate */
+       max_rate = max(max(clk_pll1.rate / 4, clk_pll2.rate / 4),
+                      (unsigned long)EP93XX_EXT_CLK_RATE / 4);
+       rate = min(rate, max_rate);
+
+       /*
+        * Try the two pll's and the external clock
+        * Because the valid predividers are 2, 2.5 and 3, we multiply
+        * all the clocks by 2 to avoid floating point math.
+        *
+        * This is based on the algorithm in the ep93xx raster guide:
+        * http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf
+        *
+        */
+       for (i = 0; i < 3; i++) {
+               if (i == 0)
+                       mclk_rate = EP93XX_EXT_CLK_RATE * 2;
+               else if (i == 1)
+                       mclk_rate = clk_pll1.rate * 2;
+               else if (i == 2)
+                       mclk_rate = clk_pll2.rate * 2;
+
+               /* Try each predivider value */
+               for (__pdiv = 4; __pdiv <= 6; __pdiv++) {
+                       __div = mclk_rate / (rate * __pdiv);
+                       if (__div < 2 || __div > 127)
+                               continue;
+
+                       actual_rate = mclk_rate / (__pdiv * __div);
+
+                       if (!found || abs(actual_rate - rate) < rate_err) {
+                               *pdiv = __pdiv - 3;
+                               *div = __div;
+                               *psel = (i == 2);
+                               *esel = (i != 0);
+                               best_rate = actual_rate;
+                               rate_err = abs(actual_rate - rate);
+                               found = 1;
+                       }
+               }
+       }
+
+       if (!found)
+               return 0;
+
+       return best_rate;
+}
+
+static int set_div_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long actual_rate;
+       int psel = 0, esel = 0, pdiv = 0, div = 0;
+       u32 val;
+
+       actual_rate = calc_clk_div(rate, &psel, &esel, &pdiv, &div);
+       if (actual_rate == 0)
+               return -EINVAL;
+       clk->rate = actual_rate;
+
+       /* Clear the esel, psel, pdiv and div bits */
+       val = __raw_readl(clk->enable_reg);
+       val &= ~0x7fff;
+
+       /* Set the new esel, psel, pdiv and div bits for the new clock rate */
+       val |= (esel ? EP93XX_SYSCON_CLKDIV_ESEL : 0) |
+               (psel ? EP93XX_SYSCON_CLKDIV_PSEL : 0) |
+               (pdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | div;
+       ep93xx_syscon_swlocked_write(val, clk->enable_reg);
+       return 0;
+}
+
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
        if (clk->set_rate)
index 16b92c37ec99a7107ec29ef51103a67ba0c4c191..f7ebed942f662fda227c002a4998091f02616de4 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/i2c-gpio.h>
 
 #include <mach/hardware.h>
+#include <mach/fb.h>
 
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
@@ -682,6 +683,37 @@ void ep93xx_pwm_release_gpio(struct platform_device *pdev)
 EXPORT_SYMBOL(ep93xx_pwm_release_gpio);
 
 
+/*************************************************************************
+ * EP93xx video peripheral handling
+ *************************************************************************/
+static struct ep93xxfb_mach_info ep93xxfb_data;
+
+static struct resource ep93xx_fb_resource[] = {
+       {
+               .start          = EP93XX_RASTER_PHYS_BASE,
+               .end            = EP93XX_RASTER_PHYS_BASE + 0x800 - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device ep93xx_fb_device = {
+       .name                   = "ep93xx-fb",
+       .id                     = -1,
+       .dev                    = {
+               .platform_data  = &ep93xxfb_data,
+               .coherent_dma_mask      = DMA_BIT_MASK(32),
+               .dma_mask               = &ep93xx_fb_device.dev.coherent_dma_mask,
+       },
+       .num_resources          = ARRAY_SIZE(ep93xx_fb_resource),
+       .resource               = ep93xx_fb_resource,
+};
+
+void __init ep93xx_register_fb(struct ep93xxfb_mach_info *data)
+{
+       ep93xxfb_data = *data;
+       platform_device_register(&ep93xx_fb_device);
+}
+
 extern void ep93xx_gpio_init(void);
 
 void __init ep93xx_init_devices(void)
index ea78e908fc82cecc9bf7c32ccbf2fe597785bf2e..0fbf87b163385296ee932644d54a6875cfd7bdc3 100644 (file)
@@ -70,6 +70,7 @@
 #define EP93XX_USB_PHYS_BASE           (EP93XX_AHB_PHYS_BASE + 0x00020000)
 #define EP93XX_USB_BASE                        EP93XX_AHB_IOMEM(0x00020000)
 
+#define EP93XX_RASTER_PHYS_BASE                (EP93XX_AHB_PHYS_BASE + 0x00030000)
 #define EP93XX_RASTER_BASE             EP93XX_AHB_IOMEM(0x00030000)
 
 #define EP93XX_GRAPHICS_ACCEL_BASE     EP93XX_AHB_IOMEM(0x00040000)
 #define EP93XX_SYSCON_DEVCFG_ADCPD     (1<<2)
 #define EP93XX_SYSCON_DEVCFG_KEYS      (1<<1)
 #define EP93XX_SYSCON_DEVCFG_SHENA     (1<<0)
+#define EP93XX_SYSCON_VIDCLKDIV                EP93XX_SYSCON_REG(0x84)
+#define EP93XX_SYSCON_CLKDIV_ENABLE    (1<<15)
+#define EP93XX_SYSCON_CLKDIV_ESEL      (1<<14)
+#define EP93XX_SYSCON_CLKDIV_PSEL      (1<<13)
+#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT        8
 #define EP93XX_SYSCON_KEYTCHCLKDIV     EP93XX_SYSCON_REG(0x90)
 #define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN        (1<<31)
 #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV        (1<<16)
diff --git a/arch/arm/mach-ep93xx/include/mach/fb.h b/arch/arm/mach-ep93xx/include/mach/fb.h
new file mode 100644 (file)
index 0000000..d5ae11d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * arch/arm/mach-ep93xx/include/mach/fb.h
+ */
+
+#ifndef __ASM_ARCH_EP93XXFB_H
+#define __ASM_ARCH_EP93XXFB_H
+
+struct platform_device;
+struct fb_videomode;
+struct fb_info;
+
+#define EP93XXFB_USE_MODEDB            0
+
+/* VideoAttributes flags */
+#define EP93XXFB_STATE_MACHINE_ENABLE  (1 << 0)
+#define EP93XXFB_PIXEL_CLOCK_ENABLE    (1 << 1)
+#define EP93XXFB_VSYNC_ENABLE          (1 << 2)
+#define EP93XXFB_PIXEL_DATA_ENABLE     (1 << 3)
+#define EP93XXFB_COMPOSITE_SYNC                (1 << 4)
+#define EP93XXFB_SYNC_VERT_HIGH                (1 << 5)
+#define EP93XXFB_SYNC_HORIZ_HIGH       (1 << 6)
+#define EP93XXFB_SYNC_BLANK_HIGH       (1 << 7)
+#define EP93XXFB_PCLK_FALLING          (1 << 8)
+#define EP93XXFB_ENABLE_AC             (1 << 9)
+#define EP93XXFB_ENABLE_LCD            (1 << 10)
+#define EP93XXFB_ENABLE_CCIR           (1 << 12)
+#define EP93XXFB_USE_PARALLEL_INTERFACE        (1 << 13)
+#define EP93XXFB_ENABLE_INTERRUPT      (1 << 14)
+#define EP93XXFB_USB_INTERLACE         (1 << 16)
+#define EP93XXFB_USE_EQUALIZATION      (1 << 17)
+#define EP93XXFB_USE_DOUBLE_HORZ       (1 << 18)
+#define EP93XXFB_USE_DOUBLE_VERT       (1 << 19)
+#define EP93XXFB_USE_BLANK_PIXEL       (1 << 20)
+#define EP93XXFB_USE_SDCSN0            (0 << 21)
+#define EP93XXFB_USE_SDCSN1            (1 << 21)
+#define EP93XXFB_USE_SDCSN2            (2 << 21)
+#define EP93XXFB_USE_SDCSN3            (3 << 21)
+
+#define EP93XXFB_ENABLE                        (EP93XXFB_STATE_MACHINE_ENABLE  | \
+                                        EP93XXFB_PIXEL_CLOCK_ENABLE    | \
+                                        EP93XXFB_VSYNC_ENABLE          | \
+                                        EP93XXFB_PIXEL_DATA_ENABLE)
+
+struct ep93xxfb_mach_info {
+       unsigned int                    num_modes;
+       const struct fb_videomode       *modes;
+       const struct fb_videomode       *default_mode;
+       int                             bpp;
+       unsigned int                    flags;
+
+       int     (*setup)(struct platform_device *pdev);
+       void    (*teardown)(struct platform_device *pdev);
+       void    (*blank)(int blank_mode, struct fb_info *info);
+};
+
+#endif /* __ASM_ARCH_EP93XXFB_H */
index 5f5fa6574d342c28eacf06dde9a4550575358a46..01a0f0838e5b3d012a1c7aa1e94e4a70011464ae 100644 (file)
@@ -6,6 +6,7 @@
 
 struct i2c_board_info;
 struct platform_device;
+struct ep93xxfb_mach_info;
 
 struct ep93xx_eth_data
 {
@@ -33,6 +34,7 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits)
 
 void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
 void ep93xx_register_i2c(struct i2c_board_info *devices, int num);
+void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
 void ep93xx_register_pwm(int pwm0, int pwm1);
 int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
 void ep93xx_pwm_release_gpio(struct platform_device *pdev);