Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ep93xx video driver

EP93xx video driver plus documentation.

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>

authored by

Ryan Mallon and committed by
Linus Torvalds
88017bda c6012189

+793
+135
Documentation/fb/ep93xx-fb.txt
··· 1 + ================================ 2 + Driver for EP93xx LCD controller 3 + ================================ 4 + 5 + The EP93xx LCD controller can drive both standard desktop monitors and 6 + embedded LCD displays. If you have a standard desktop monitor then you 7 + can use the standard Linux video mode database. In your board file: 8 + 9 + static struct ep93xxfb_mach_info some_board_fb_info = { 10 + .num_modes = EP93XXFB_USE_MODEDB, 11 + .bpp = 16, 12 + }; 13 + 14 + If you have an embedded LCD display then you need to define a video 15 + mode for it as follows: 16 + 17 + static struct fb_videomode some_board_video_modes[] = { 18 + { 19 + .name = "some_lcd_name", 20 + /* Pixel clock, porches, etc */ 21 + }, 22 + }; 23 + 24 + Note that the pixel clock value is in pico-seconds. You can use the 25 + KHZ2PICOS macro to convert the pixel clock value. Most other values 26 + are in pixel clocks. See Documentation/fb/framebuffer.txt for further 27 + details. 28 + 29 + The ep93xxfb_mach_info structure for your board should look like the 30 + following: 31 + 32 + static struct ep93xxfb_mach_info some_board_fb_info = { 33 + .num_modes = ARRAY_SIZE(some_board_video_modes), 34 + .modes = some_board_video_modes, 35 + .default_mode = &some_board_video_modes[0], 36 + .bpp = 16, 37 + }; 38 + 39 + The framebuffer device can be registered by adding the following to 40 + your board initialisation function: 41 + 42 + ep93xx_register_fb(&some_board_fb_info); 43 + 44 + ===================== 45 + Video Attribute Flags 46 + ===================== 47 + 48 + The ep93xxfb_mach_info structure has a flags field which can be used 49 + to configure the controller. The video attributes flags are fully 50 + documented in section 7 of the EP93xx users' guide. The following 51 + flags are available: 52 + 53 + EP93XXFB_PCLK_FALLING Clock data on the falling edge of the 54 + pixel clock. The default is to clock 55 + data on the rising edge. 56 + 57 + EP93XXFB_SYNC_BLANK_HIGH Blank signal is active high. By 58 + default the blank signal is active low. 59 + 60 + EP93XXFB_SYNC_HORIZ_HIGH Horizontal sync is active high. By 61 + default the horizontal sync is active low. 62 + 63 + EP93XXFB_SYNC_VERT_HIGH Vertical sync is active high. By 64 + default the vertical sync is active high. 65 + 66 + The physical address of the framebuffer can be controlled using the 67 + following flags: 68 + 69 + EP93XXFB_USE_SDCSN0 Use SDCSn[0] for the framebuffer. This 70 + is the default setting. 71 + 72 + EP93XXFB_USE_SDCSN1 Use SDCSn[1] for the framebuffer. 73 + 74 + EP93XXFB_USE_SDCSN2 Use SDCSn[2] for the framebuffer. 75 + 76 + EP93XXFB_USE_SDCSN3 Use SDCSn[3] for the framebuffer. 77 + 78 + ================== 79 + Platform callbacks 80 + ================== 81 + 82 + The EP93xx framebuffer driver supports three optional platform 83 + callbacks: setup, teardown and blank. The setup and teardown functions 84 + are called when the framebuffer driver is installed and removed 85 + respectively. The blank function is called whenever the display is 86 + blanked or unblanked. 87 + 88 + The setup and teardown devices pass the platform_device structure as 89 + an argument. The fb_info and ep93xxfb_mach_info structures can be 90 + obtained as follows: 91 + 92 + static int some_board_fb_setup(struct platform_device *pdev) 93 + { 94 + struct ep93xxfb_mach_info *mach_info = pdev->dev.platform_data; 95 + struct fb_info *fb_info = platform_get_drvdata(pdev); 96 + 97 + /* Board specific framebuffer setup */ 98 + } 99 + 100 + ====================== 101 + Setting the video mode 102 + ====================== 103 + 104 + The video mode is set using the following syntax: 105 + 106 + video=XRESxYRES[-BPP][@REFRESH] 107 + 108 + If the EP93xx video driver is built-in then the video mode is set on 109 + the Linux kernel command line, for example: 110 + 111 + video=ep93xx-fb:800x600-16@60 112 + 113 + If the EP93xx video driver is built as a module then the video mode is 114 + set when the module is installed: 115 + 116 + modprobe ep93xx-fb video=320x240 117 + 118 + ============== 119 + Screenpage bug 120 + ============== 121 + 122 + At least on the EP9315 there is a silicon bug which causes bit 27 of 123 + the VIDSCRNPAGE (framebuffer physical offset) to be tied low. There is 124 + an unofficial errata for this bug at: 125 + http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 126 + 127 + By default the EP93xx framebuffer driver checks if the allocated physical 128 + address has bit 27 set. If it does, then the memory is freed and an 129 + error is returned. The check can be disabled by adding the following 130 + option when loading the driver: 131 + 132 + ep93xx-fb.check_screenpage_bug=0 133 + 134 + In some cases it may be possible to reconfigure your SDRAM layout to 135 + avoid this bug. See section 13 of the EP93xx users' guide for details.
+11
drivers/video/Kconfig
··· 2128 2128 ---help--- 2129 2129 Framebuffer support for Fujitsu Lime GDC on host CPU bus. 2130 2130 2131 + config FB_EP93XX 2132 + tristate "EP93XX frame buffer support" 2133 + depends on FB && ARCH_EP93XX 2134 + select FB_CFB_FILLRECT 2135 + select FB_CFB_COPYAREA 2136 + select FB_CFB_IMAGEBLIT 2137 + ---help--- 2138 + Framebuffer driver for the Cirrus Logic EP93XX series of processors. 2139 + This driver is also available as a module. The module will be called 2140 + ep93xx-fb. 2141 + 2131 2142 config FB_PRE_INIT_FB 2132 2143 bool "Don't reinitialize, use bootloader's GDC/Display configuration" 2133 2144 depends on FB_MB862XX_LIME
+1
drivers/video/Makefile
··· 85 85 obj-$(CONFIG_FB_TGA) += tgafb.o 86 86 obj-$(CONFIG_FB_HP300) += hpfb.o 87 87 obj-$(CONFIG_FB_G364) += g364fb.o 88 + obj-$(CONFIG_FB_EP93XX) += ep93xx-fb.o 88 89 obj-$(CONFIG_FB_SA1100) += sa1100fb.o 89 90 obj-$(CONFIG_FB_HIT) += hitfb.o 90 91 obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o
+646
drivers/video/ep93xx-fb.c
··· 1 + /* 2 + * linux/drivers/video/ep93xx-fb.c 3 + * 4 + * Framebuffer support for the EP93xx series. 5 + * 6 + * Copyright (C) 2007 Bluewater Systems Ltd 7 + * Author: Ryan Mallon <ryan@bluewatersys.com> 8 + * 9 + * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> 10 + * 11 + * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb 12 + * drivers. 13 + * 14 + * This program is free software; you can redistribute it and/or modify 15 + * it under the terms of the GNU General Public License version 2 as 16 + * published by the Free Software Foundation. 17 + * 18 + */ 19 + 20 + #include <linux/platform_device.h> 21 + #include <linux/dma-mapping.h> 22 + #include <linux/clk.h> 23 + #include <linux/fb.h> 24 + 25 + #include <mach/fb.h> 26 + 27 + /* Vertical Frame Timing Registers */ 28 + #define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ 29 + #define EP93XXFB_VSYNC 0x0004 /* SW locked */ 30 + #define EP93XXFB_VACTIVE 0x0008 /* SW locked */ 31 + #define EP93XXFB_VBLANK 0x0228 /* SW locked */ 32 + #define EP93XXFB_VCLK 0x000c /* SW locked */ 33 + 34 + /* Horizontal Frame Timing Registers */ 35 + #define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ 36 + #define EP93XXFB_HSYNC 0x0014 /* SW locked */ 37 + #define EP93XXFB_HACTIVE 0x0018 /* SW locked */ 38 + #define EP93XXFB_HBLANK 0x022c /* SW locked */ 39 + #define EP93XXFB_HCLK 0x001c /* SW locked */ 40 + 41 + /* Frame Buffer Memory Configuration Registers */ 42 + #define EP93XXFB_SCREEN_PAGE 0x0028 43 + #define EP93XXFB_SCREEN_HPAGE 0x002c 44 + #define EP93XXFB_SCREEN_LINES 0x0030 45 + #define EP93XXFB_LINE_LENGTH 0x0034 46 + #define EP93XXFB_VLINE_STEP 0x0038 47 + #define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ 48 + #define EP93XXFB_EOL_OFFSET 0x0230 49 + 50 + /* Other Video Registers */ 51 + #define EP93XXFB_BRIGHTNESS 0x0020 52 + #define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ 53 + #define EP93XXFB_SWLOCK 0x007c /* SW locked */ 54 + #define EP93XXFB_AC_RATE 0x0214 55 + #define EP93XXFB_FIFO_LEVEL 0x0234 56 + #define EP93XXFB_PIXELMODE 0x0054 57 + #define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) 58 + #define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) 59 + #define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) 60 + #define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) 61 + #define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) 62 + #define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) 63 + #define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) 64 + #define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) 65 + #define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) 66 + #define EP93XXFB_PARL_IF_OUT 0x0058 67 + #define EP93XXFB_PARL_IF_IN 0x005c 68 + 69 + /* Blink Control Registers */ 70 + #define EP93XXFB_BLINK_RATE 0x0040 71 + #define EP93XXFB_BLINK_MASK 0x0044 72 + #define EP93XXFB_BLINK_PATTRN 0x0048 73 + #define EP93XXFB_PATTRN_MASK 0x004c 74 + #define EP93XXFB_BKGRND_OFFSET 0x0050 75 + 76 + /* Hardware Cursor Registers */ 77 + #define EP93XXFB_CURSOR_ADR_START 0x0060 78 + #define EP93XXFB_CURSOR_ADR_RESET 0x0064 79 + #define EP93XXFB_CURSOR_SIZE 0x0068 80 + #define EP93XXFB_CURSOR_COLOR1 0x006c 81 + #define EP93XXFB_CURSOR_COLOR2 0x0070 82 + #define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c 83 + #define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 84 + #define EP93XXFB_CURSOR_XY_LOC 0x0074 85 + #define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 86 + #define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 87 + 88 + /* LUT Registers */ 89 + #define EP93XXFB_GRY_SCL_LUTR 0x0080 90 + #define EP93XXFB_GRY_SCL_LUTG 0x0280 91 + #define EP93XXFB_GRY_SCL_LUTB 0x0300 92 + #define EP93XXFB_LUT_SW_CONTROL 0x0218 93 + #define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) 94 + #define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) 95 + #define EP93XXFB_COLOR_LUT 0x0400 96 + 97 + /* Video Signature Registers */ 98 + #define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 99 + #define EP93XXFB_VID_SIG_CTRL 0x0204 100 + #define EP93XXFB_VSIG 0x0208 101 + #define EP93XXFB_HSIG 0x020c 102 + #define EP93XXFB_SIG_CLR_STR 0x0210 103 + 104 + /* Minimum / Maximum resolutions supported */ 105 + #define EP93XXFB_MIN_XRES 64 106 + #define EP93XXFB_MIN_YRES 64 107 + #define EP93XXFB_MAX_XRES 1024 108 + #define EP93XXFB_MAX_YRES 768 109 + 110 + struct ep93xx_fbi { 111 + struct ep93xxfb_mach_info *mach_info; 112 + struct clk *clk; 113 + struct resource *res; 114 + void __iomem *mmio_base; 115 + unsigned int pseudo_palette[256]; 116 + }; 117 + 118 + static int check_screenpage_bug = 1; 119 + module_param(check_screenpage_bug, int, 0644); 120 + MODULE_PARM_DESC(check_screenpage_bug, 121 + "Check for bit 27 screen page bug. Default = 1"); 122 + 123 + static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, 124 + unsigned int off) 125 + { 126 + return __raw_readl(fbi->mmio_base + off); 127 + } 128 + 129 + static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, 130 + unsigned int val, unsigned int off) 131 + { 132 + __raw_writel(val, fbi->mmio_base + off); 133 + } 134 + 135 + /* 136 + * Write to one of the locked raster registers. 137 + */ 138 + static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, 139 + unsigned int val, unsigned int reg) 140 + { 141 + /* 142 + * We don't need a lock or delay here since the raster register 143 + * block will remain unlocked until the next access. 144 + */ 145 + ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); 146 + ep93xxfb_writel(fbi, val, reg); 147 + } 148 + 149 + static void ep93xxfb_set_video_attribs(struct fb_info *info) 150 + { 151 + struct ep93xx_fbi *fbi = info->par; 152 + unsigned int attribs; 153 + 154 + attribs = EP93XXFB_ENABLE; 155 + attribs |= fbi->mach_info->flags; 156 + ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); 157 + } 158 + 159 + static int ep93xxfb_set_pixelmode(struct fb_info *info) 160 + { 161 + struct ep93xx_fbi *fbi = info->par; 162 + unsigned int val; 163 + 164 + info->var.transp.offset = 0; 165 + info->var.transp.length = 0; 166 + 167 + switch (info->var.bits_per_pixel) { 168 + case 8: 169 + val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | 170 + EP93XXFB_PIXELMODE_SHIFT_1P_18B; 171 + 172 + info->var.red.offset = 0; 173 + info->var.red.length = 8; 174 + info->var.green.offset = 0; 175 + info->var.green.length = 8; 176 + info->var.blue.offset = 0; 177 + info->var.blue.length = 8; 178 + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 179 + break; 180 + 181 + case 16: 182 + val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | 183 + EP93XXFB_PIXELMODE_SHIFT_1P_18B; 184 + 185 + info->var.red.offset = 11; 186 + info->var.red.length = 5; 187 + info->var.green.offset = 5; 188 + info->var.green.length = 6; 189 + info->var.blue.offset = 0; 190 + info->var.blue.length = 5; 191 + info->fix.visual = FB_VISUAL_TRUECOLOR; 192 + break; 193 + 194 + case 24: 195 + val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | 196 + EP93XXFB_PIXELMODE_SHIFT_1P_24B; 197 + 198 + info->var.red.offset = 16; 199 + info->var.red.length = 8; 200 + info->var.green.offset = 8; 201 + info->var.green.length = 8; 202 + info->var.blue.offset = 0; 203 + info->var.blue.length = 8; 204 + info->fix.visual = FB_VISUAL_TRUECOLOR; 205 + break; 206 + 207 + case 32: 208 + val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | 209 + EP93XXFB_PIXELMODE_SHIFT_1P_24B; 210 + 211 + info->var.red.offset = 16; 212 + info->var.red.length = 8; 213 + info->var.green.offset = 8; 214 + info->var.green.length = 8; 215 + info->var.blue.offset = 0; 216 + info->var.blue.length = 8; 217 + info->fix.visual = FB_VISUAL_TRUECOLOR; 218 + break; 219 + 220 + default: 221 + return -EINVAL; 222 + } 223 + 224 + ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); 225 + return 0; 226 + } 227 + 228 + static void ep93xxfb_set_timing(struct fb_info *info) 229 + { 230 + struct ep93xx_fbi *fbi = info->par; 231 + unsigned int vlines_total, hclks_total, start, stop; 232 + 233 + vlines_total = info->var.yres + info->var.upper_margin + 234 + info->var.lower_margin + info->var.vsync_len - 1; 235 + 236 + hclks_total = info->var.xres + info->var.left_margin + 237 + info->var.right_margin + info->var.hsync_len - 1; 238 + 239 + ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); 240 + ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); 241 + 242 + start = vlines_total; 243 + stop = vlines_total - info->var.vsync_len; 244 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); 245 + 246 + start = vlines_total - info->var.vsync_len - info->var.upper_margin; 247 + stop = info->var.lower_margin - 1; 248 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); 249 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); 250 + 251 + start = vlines_total; 252 + stop = vlines_total + 1; 253 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); 254 + 255 + start = hclks_total; 256 + stop = hclks_total - info->var.hsync_len; 257 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); 258 + 259 + start = hclks_total - info->var.hsync_len - info->var.left_margin; 260 + stop = info->var.right_margin - 1; 261 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); 262 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); 263 + 264 + start = hclks_total; 265 + stop = hclks_total; 266 + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); 267 + 268 + ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); 269 + } 270 + 271 + static int ep93xxfb_set_par(struct fb_info *info) 272 + { 273 + struct ep93xx_fbi *fbi = info->par; 274 + 275 + clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); 276 + 277 + ep93xxfb_set_timing(info); 278 + 279 + info->fix.line_length = info->var.xres_virtual * 280 + info->var.bits_per_pixel / 8; 281 + 282 + ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); 283 + ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); 284 + ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) 285 + / 32) - 1, EP93XXFB_LINE_LENGTH); 286 + ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); 287 + ep93xxfb_set_video_attribs(info); 288 + return 0; 289 + } 290 + 291 + static int ep93xxfb_check_var(struct fb_var_screeninfo *var, 292 + struct fb_info *info) 293 + { 294 + int err; 295 + 296 + err = ep93xxfb_set_pixelmode(info); 297 + if (err) 298 + return err; 299 + 300 + var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); 301 + var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); 302 + var->xres_virtual = max(var->xres_virtual, var->xres); 303 + 304 + var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); 305 + var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); 306 + var->yres_virtual = max(var->yres_virtual, var->yres); 307 + 308 + return 0; 309 + } 310 + 311 + static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 312 + { 313 + unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; 314 + 315 + if (offset < info->fix.smem_len) { 316 + return dma_mmap_writecombine(info->dev, vma, info->screen_base, 317 + info->fix.smem_start, 318 + info->fix.smem_len); 319 + } 320 + 321 + return -EINVAL; 322 + } 323 + 324 + static int ep93xxfb_blank(int blank_mode, struct fb_info *info) 325 + { 326 + struct ep93xx_fbi *fbi = info->par; 327 + unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); 328 + 329 + if (blank_mode) { 330 + if (fbi->mach_info->blank) 331 + fbi->mach_info->blank(blank_mode, info); 332 + ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, 333 + EP93XXFB_ATTRIBS); 334 + clk_disable(fbi->clk); 335 + } else { 336 + clk_enable(fbi->clk); 337 + ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, 338 + EP93XXFB_ATTRIBS); 339 + if (fbi->mach_info->blank) 340 + fbi->mach_info->blank(blank_mode, info); 341 + } 342 + 343 + return 0; 344 + } 345 + 346 + static inline int ep93xxfb_convert_color(int val, int width) 347 + { 348 + return ((val << width) + 0x7fff - val) >> 16; 349 + } 350 + 351 + static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, 352 + unsigned int green, unsigned int blue, 353 + unsigned int transp, struct fb_info *info) 354 + { 355 + struct ep93xx_fbi *fbi = info->par; 356 + unsigned int *pal = info->pseudo_palette; 357 + unsigned int ctrl, i, rgb, lut_current, lut_stat; 358 + 359 + switch (info->fix.visual) { 360 + case FB_VISUAL_PSEUDOCOLOR: 361 + rgb = ((red & 0xff00) << 8) | (green & 0xff00) | 362 + ((blue & 0xff00) >> 8); 363 + 364 + pal[regno] = rgb; 365 + ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); 366 + ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); 367 + lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); 368 + lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); 369 + 370 + if (lut_stat == lut_current) { 371 + for (i = 0; i < 256; i++) { 372 + ep93xxfb_writel(fbi, pal[i], 373 + EP93XXFB_COLOR_LUT + (i << 2)); 374 + } 375 + 376 + ep93xxfb_writel(fbi, 377 + ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, 378 + EP93XXFB_LUT_SW_CONTROL); 379 + } 380 + break; 381 + 382 + case FB_VISUAL_TRUECOLOR: 383 + if (regno > 16) 384 + return 1; 385 + 386 + red = ep93xxfb_convert_color(red, info->var.red.length); 387 + green = ep93xxfb_convert_color(green, info->var.green.length); 388 + blue = ep93xxfb_convert_color(blue, info->var.blue.length); 389 + transp = ep93xxfb_convert_color(transp, 390 + info->var.transp.length); 391 + 392 + pal[regno] = (red << info->var.red.offset) | 393 + (green << info->var.green.offset) | 394 + (blue << info->var.blue.offset) | 395 + (transp << info->var.transp.offset); 396 + break; 397 + 398 + default: 399 + return 1; 400 + } 401 + 402 + return 0; 403 + } 404 + 405 + static struct fb_ops ep93xxfb_ops = { 406 + .owner = THIS_MODULE, 407 + .fb_check_var = ep93xxfb_check_var, 408 + .fb_set_par = ep93xxfb_set_par, 409 + .fb_blank = ep93xxfb_blank, 410 + .fb_fillrect = cfb_fillrect, 411 + .fb_copyarea = cfb_copyarea, 412 + .fb_imageblit = cfb_imageblit, 413 + .fb_setcolreg = ep93xxfb_setcolreg, 414 + .fb_mmap = ep93xxfb_mmap, 415 + }; 416 + 417 + static int __init ep93xxfb_calc_fbsize(struct ep93xxfb_mach_info *mach_info) 418 + { 419 + int i, fb_size = 0; 420 + 421 + if (mach_info->num_modes == EP93XXFB_USE_MODEDB) { 422 + fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 423 + mach_info->bpp / 8; 424 + } else { 425 + for (i = 0; i < mach_info->num_modes; i++) { 426 + const struct fb_videomode *mode; 427 + int size; 428 + 429 + mode = &mach_info->modes[i]; 430 + size = mode->xres * mode->yres * mach_info->bpp / 8; 431 + if (size > fb_size) 432 + fb_size = size; 433 + } 434 + } 435 + 436 + return fb_size; 437 + } 438 + 439 + static int __init ep93xxfb_alloc_videomem(struct fb_info *info) 440 + { 441 + struct ep93xx_fbi *fbi = info->par; 442 + char __iomem *virt_addr; 443 + dma_addr_t phys_addr; 444 + unsigned int fb_size; 445 + 446 + fb_size = ep93xxfb_calc_fbsize(fbi->mach_info); 447 + virt_addr = dma_alloc_writecombine(info->dev, fb_size, 448 + &phys_addr, GFP_KERNEL); 449 + if (!virt_addr) 450 + return -ENOMEM; 451 + 452 + /* 453 + * There is a bug in the ep93xx framebuffer which causes problems 454 + * if bit 27 of the physical address is set. 455 + * See: http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 456 + * There does not seem to be any offical errata for this, but I 457 + * have confirmed the problem exists on my hardware (ep9315) at 458 + * least. 459 + */ 460 + if (check_screenpage_bug && phys_addr & (1 << 27)) { 461 + dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " 462 + "has bit 27 set: cannot init framebuffer\n", 463 + phys_addr); 464 + 465 + dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); 466 + return -ENOMEM; 467 + } 468 + 469 + info->fix.smem_start = phys_addr; 470 + info->fix.smem_len = fb_size; 471 + info->screen_base = virt_addr; 472 + 473 + return 0; 474 + } 475 + 476 + static void ep93xxfb_dealloc_videomem(struct fb_info *info) 477 + { 478 + if (info->screen_base) 479 + dma_free_coherent(info->dev, info->fix.smem_len, 480 + info->screen_base, info->fix.smem_start); 481 + } 482 + 483 + static int __init ep93xxfb_probe(struct platform_device *pdev) 484 + { 485 + struct ep93xxfb_mach_info *mach_info = pdev->dev.platform_data; 486 + struct fb_info *info; 487 + struct ep93xx_fbi *fbi; 488 + struct resource *res; 489 + char *video_mode; 490 + int err; 491 + 492 + if (!mach_info) 493 + return -EINVAL; 494 + 495 + info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); 496 + if (!info) 497 + return -ENOMEM; 498 + 499 + info->dev = &pdev->dev; 500 + platform_set_drvdata(pdev, info); 501 + fbi = info->par; 502 + fbi->mach_info = mach_info; 503 + 504 + err = fb_alloc_cmap(&info->cmap, 256, 0); 505 + if (err) 506 + goto failed; 507 + 508 + err = ep93xxfb_alloc_videomem(info); 509 + if (err) 510 + goto failed; 511 + 512 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 513 + if (!res) { 514 + err = -ENXIO; 515 + goto failed; 516 + } 517 + 518 + res = request_mem_region(res->start, resource_size(res), pdev->name); 519 + if (!res) { 520 + err = -EBUSY; 521 + goto failed; 522 + } 523 + 524 + fbi->res = res; 525 + fbi->mmio_base = ioremap(res->start, resource_size(res)); 526 + if (!fbi->mmio_base) { 527 + err = -ENXIO; 528 + goto failed; 529 + } 530 + 531 + strcpy(info->fix.id, pdev->name); 532 + info->fbops = &ep93xxfb_ops; 533 + info->fix.type = FB_TYPE_PACKED_PIXELS; 534 + info->fix.accel = FB_ACCEL_NONE; 535 + info->var.activate = FB_ACTIVATE_NOW; 536 + info->var.vmode = FB_VMODE_NONINTERLACED; 537 + info->flags = FBINFO_DEFAULT; 538 + info->node = -1; 539 + info->state = FBINFO_STATE_RUNNING; 540 + info->pseudo_palette = &fbi->pseudo_palette; 541 + 542 + fb_get_options("ep93xx-fb", &video_mode); 543 + err = fb_find_mode(&info->var, info, video_mode, 544 + fbi->mach_info->modes, fbi->mach_info->num_modes, 545 + fbi->mach_info->default_mode, fbi->mach_info->bpp); 546 + if (err == 0) { 547 + dev_err(info->dev, "No suitable video mode found\n"); 548 + err = -EINVAL; 549 + goto failed; 550 + } 551 + 552 + if (mach_info->setup) { 553 + err = mach_info->setup(pdev); 554 + if (err) 555 + return err; 556 + } 557 + 558 + err = ep93xxfb_check_var(&info->var, info); 559 + if (err) 560 + goto failed; 561 + 562 + fbi->clk = clk_get(info->dev, NULL); 563 + if (IS_ERR(fbi->clk)) { 564 + err = PTR_ERR(fbi->clk); 565 + fbi->clk = NULL; 566 + goto failed; 567 + } 568 + 569 + ep93xxfb_set_par(info); 570 + clk_enable(fbi->clk); 571 + 572 + err = register_framebuffer(info); 573 + if (err) 574 + goto failed; 575 + 576 + dev_info(info->dev, "registered. Mode = %dx%d-%d\n", 577 + info->var.xres, info->var.yres, info->var.bits_per_pixel); 578 + return 0; 579 + 580 + failed: 581 + if (fbi->clk) 582 + clk_put(fbi->clk); 583 + if (fbi->mmio_base) 584 + iounmap(fbi->mmio_base); 585 + if (fbi->res) 586 + release_mem_region(fbi->res->start, resource_size(fbi->res)); 587 + ep93xxfb_dealloc_videomem(info); 588 + if (&info->cmap) 589 + fb_dealloc_cmap(&info->cmap); 590 + if (fbi->mach_info->teardown) 591 + fbi->mach_info->teardown(pdev); 592 + kfree(info); 593 + platform_set_drvdata(pdev, NULL); 594 + 595 + return err; 596 + } 597 + 598 + static int ep93xxfb_remove(struct platform_device *pdev) 599 + { 600 + struct fb_info *info = platform_get_drvdata(pdev); 601 + struct ep93xx_fbi *fbi = info->par; 602 + 603 + unregister_framebuffer(info); 604 + clk_disable(fbi->clk); 605 + clk_put(fbi->clk); 606 + iounmap(fbi->mmio_base); 607 + release_mem_region(fbi->res->start, resource_size(fbi->res)); 608 + ep93xxfb_dealloc_videomem(info); 609 + fb_dealloc_cmap(&info->cmap); 610 + 611 + if (fbi->mach_info->teardown) 612 + fbi->mach_info->teardown(pdev); 613 + 614 + kfree(info); 615 + platform_set_drvdata(pdev, NULL); 616 + 617 + return 0; 618 + } 619 + 620 + static struct platform_driver ep93xxfb_driver = { 621 + .probe = ep93xxfb_probe, 622 + .remove = ep93xxfb_remove, 623 + .driver = { 624 + .name = "ep93xx-fb", 625 + .owner = THIS_MODULE, 626 + }, 627 + }; 628 + 629 + static int __devinit ep93xxfb_init(void) 630 + { 631 + return platform_driver_register(&ep93xxfb_driver); 632 + } 633 + 634 + static void __exit ep93xxfb_exit(void) 635 + { 636 + platform_driver_unregister(&ep93xxfb_driver); 637 + } 638 + 639 + module_init(ep93xxfb_init); 640 + module_exit(ep93xxfb_exit); 641 + 642 + MODULE_DESCRIPTION("EP93XX Framebuffer Driver"); 643 + MODULE_ALIAS("platform:ep93xx-fb"); 644 + MODULE_AUTHOR("Ryan Mallon <ryan&bluewatersys.com>, " 645 + "H Hartley Sweeten <hsweeten@visionengravers.com"); 646 + MODULE_LICENSE("GPL");