| Ben Dooks | 6a0e4ec | 2008-05-23 13:04:56 -0700 | [diff] [blame] | 1 | /* linux/drivers/video/s3c2410fb.c | 
|  | 2 | *	Copyright (c) 2004,2005 Arnaud Patard | 
|  | 3 | *	Copyright (c) 2004-2008 Ben Dooks | 
|  | 4 | * | 
|  | 5 | * S3C2410 LCD Framebuffer Driver | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 6 | * | 
|  | 7 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 8 | * License.  See the file COPYING in the main directory of this archive for | 
|  | 9 | * more details. | 
|  | 10 | * | 
| Ben Dooks | 6a0e4ec | 2008-05-23 13:04:56 -0700 | [diff] [blame] | 11 | * Driver based on skeletonfb.c, sa1100fb.c and others. | 
|  | 12 | */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 13 |  | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <linux/kernel.h> | 
|  | 16 | #include <linux/errno.h> | 
|  | 17 | #include <linux/string.h> | 
|  | 18 | #include <linux/mm.h> | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 19 | #include <linux/slab.h> | 
|  | 20 | #include <linux/delay.h> | 
|  | 21 | #include <linux/fb.h> | 
|  | 22 | #include <linux/init.h> | 
|  | 23 | #include <linux/dma-mapping.h> | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 24 | #include <linux/interrupt.h> | 
| Russell King | d052d1b | 2005-10-29 19:07:23 +0100 | [diff] [blame] | 25 | #include <linux/platform_device.h> | 
| Russell King | f8ce254 | 2006-01-07 16:15:52 +0000 | [diff] [blame] | 26 | #include <linux/clk.h> | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 27 |  | 
|  | 28 | #include <asm/io.h> | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 29 | #include <asm/div64.h> | 
|  | 30 |  | 
|  | 31 | #include <asm/mach/map.h> | 
| Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 32 | #include <mach/regs-lcd.h> | 
|  | 33 | #include <mach/regs-gpio.h> | 
|  | 34 | #include <mach/fb.h> | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 35 |  | 
|  | 36 | #ifdef CONFIG_PM | 
|  | 37 | #include <linux/pm.h> | 
|  | 38 | #endif | 
|  | 39 |  | 
|  | 40 | #include "s3c2410fb.h" | 
|  | 41 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 42 | /* Debugging stuff */ | 
|  | 43 | #ifdef CONFIG_FB_S3C2410_DEBUG | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 44 | static int debug	= 1; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 45 | #else | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 46 | static int debug	= 0; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 47 | #endif | 
|  | 48 |  | 
|  | 49 | #define dprintk(msg...)	if (debug) { printk(KERN_DEBUG "s3c2410fb: " msg); } | 
|  | 50 |  | 
|  | 51 | /* useful functions */ | 
|  | 52 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 53 | static int is_s3c2412(struct s3c2410fb_info *fbi) | 
|  | 54 | { | 
|  | 55 | return (fbi->drv_type == DRV_S3C2412); | 
|  | 56 | } | 
|  | 57 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 58 | /* s3c2410fb_set_lcdaddr | 
|  | 59 | * | 
|  | 60 | * initialise lcd controller address pointers | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 61 | */ | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 62 | static void s3c2410fb_set_lcdaddr(struct fb_info *info) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 63 | { | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 64 | unsigned long saddr1, saddr2, saddr3; | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 65 | struct s3c2410fb_info *fbi = info->par; | 
|  | 66 | void __iomem *regs = fbi->io; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 67 |  | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 68 | saddr1  = info->fix.smem_start >> 1; | 
|  | 69 | saddr2  = info->fix.smem_start; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 70 | saddr2 += info->fix.line_length * info->var.yres; | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 71 | saddr2 >>= 1; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 72 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 73 | saddr3 = S3C2410_OFFSIZE(0) | | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 74 | S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 75 |  | 
|  | 76 | dprintk("LCDSADDR1 = 0x%08lx\n", saddr1); | 
|  | 77 | dprintk("LCDSADDR2 = 0x%08lx\n", saddr2); | 
|  | 78 | dprintk("LCDSADDR3 = 0x%08lx\n", saddr3); | 
|  | 79 |  | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 80 | writel(saddr1, regs + S3C2410_LCDSADDR1); | 
|  | 81 | writel(saddr2, regs + S3C2410_LCDSADDR2); | 
|  | 82 | writel(saddr3, regs + S3C2410_LCDSADDR3); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 83 | } | 
|  | 84 |  | 
|  | 85 | /* s3c2410fb_calc_pixclk() | 
|  | 86 | * | 
|  | 87 | * calculate divisor for clk->pixclk | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 88 | */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 89 | static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi, | 
|  | 90 | unsigned long pixclk) | 
|  | 91 | { | 
|  | 92 | unsigned long clk = clk_get_rate(fbi->clk); | 
|  | 93 | unsigned long long div; | 
|  | 94 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 95 | /* pixclk is in picoseconds, our clock is in Hz | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 96 | * | 
|  | 97 | * Hz -> picoseconds is / 10^-12 | 
|  | 98 | */ | 
|  | 99 |  | 
|  | 100 | div = (unsigned long long)clk * pixclk; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 101 | div >>= 12;			/* div / 2^12 */ | 
|  | 102 | do_div(div, 625 * 625UL * 625); /* div / 5^12 */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 103 |  | 
|  | 104 | dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div); | 
|  | 105 | return div; | 
|  | 106 | } | 
|  | 107 |  | 
|  | 108 | /* | 
|  | 109 | *	s3c2410fb_check_var(): | 
|  | 110 | *	Get the video params out of 'var'. If a value doesn't fit, round it up, | 
|  | 111 | *	if it's too big, return -EINVAL. | 
|  | 112 | * | 
|  | 113 | */ | 
|  | 114 | static int s3c2410fb_check_var(struct fb_var_screeninfo *var, | 
|  | 115 | struct fb_info *info) | 
|  | 116 | { | 
|  | 117 | struct s3c2410fb_info *fbi = info->par; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 118 | struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data; | 
| Krzysztof Helt | 09fe75f | 2007-10-16 01:28:56 -0700 | [diff] [blame] | 119 | struct s3c2410fb_display *display = NULL; | 
| Krzysztof Helt | e707638 | 2007-10-16 01:29:08 -0700 | [diff] [blame] | 120 | struct s3c2410fb_display *default_display = mach_info->displays + | 
|  | 121 | mach_info->default_display; | 
|  | 122 | int type = default_display->type; | 
| Krzysztof Helt | 09fe75f | 2007-10-16 01:28:56 -0700 | [diff] [blame] | 123 | unsigned i; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 124 |  | 
|  | 125 | dprintk("check_var(var=%p, info=%p)\n", var, info); | 
|  | 126 |  | 
|  | 127 | /* validate x/y resolution */ | 
| Krzysztof Helt | e707638 | 2007-10-16 01:29:08 -0700 | [diff] [blame] | 128 | /* choose default mode if possible */ | 
|  | 129 | if (var->yres == default_display->yres && | 
|  | 130 | var->xres == default_display->xres && | 
|  | 131 | var->bits_per_pixel == default_display->bpp) | 
|  | 132 | display = default_display; | 
|  | 133 | else | 
|  | 134 | for (i = 0; i < mach_info->num_displays; i++) | 
|  | 135 | if (type == mach_info->displays[i].type && | 
|  | 136 | var->yres == mach_info->displays[i].yres && | 
|  | 137 | var->xres == mach_info->displays[i].xres && | 
|  | 138 | var->bits_per_pixel == mach_info->displays[i].bpp) { | 
|  | 139 | display = mach_info->displays + i; | 
|  | 140 | break; | 
|  | 141 | } | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 142 |  | 
| Krzysztof Helt | 09fe75f | 2007-10-16 01:28:56 -0700 | [diff] [blame] | 143 | if (!display) { | 
|  | 144 | dprintk("wrong resolution or depth %dx%d at %d bpp\n", | 
|  | 145 | var->xres, var->yres, var->bits_per_pixel); | 
|  | 146 | return -EINVAL; | 
|  | 147 | } | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 148 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 149 | /* it is always the size as the display */ | 
|  | 150 | var->xres_virtual = display->xres; | 
|  | 151 | var->yres_virtual = display->yres; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 152 | var->height = display->height; | 
|  | 153 | var->width = display->width; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 154 |  | 
|  | 155 | /* copy lcd settings */ | 
| Krzysztof Helt | 6981669 | 2007-10-16 01:29:06 -0700 | [diff] [blame] | 156 | var->pixclock = display->pixclock; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 157 | var->left_margin = display->left_margin; | 
|  | 158 | var->right_margin = display->right_margin; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 159 | var->upper_margin = display->upper_margin; | 
|  | 160 | var->lower_margin = display->lower_margin; | 
|  | 161 | var->vsync_len = display->vsync_len; | 
|  | 162 | var->hsync_len = display->hsync_len; | 
|  | 163 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 164 | fbi->regs.lcdcon5 = display->lcdcon5; | 
|  | 165 | /* set display type */ | 
| Krzysztof Helt | 36f31a7 | 2007-10-16 01:29:07 -0700 | [diff] [blame] | 166 | fbi->regs.lcdcon1 = display->type; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 167 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 168 | var->transp.offset = 0; | 
|  | 169 | var->transp.length = 0; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 170 | /* set r/g/b positions */ | 
| Arnaud Patard (Rtp | 357b819 | 2006-12-08 02:40:23 -0800 | [diff] [blame] | 171 | switch (var->bits_per_pixel) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 172 | case 1: | 
|  | 173 | case 2: | 
|  | 174 | case 4: | 
|  | 175 | var->red.offset	= 0; | 
|  | 176 | var->red.length	= var->bits_per_pixel; | 
|  | 177 | var->green	= var->red; | 
|  | 178 | var->blue	= var->red; | 
|  | 179 | break; | 
|  | 180 | case 8: | 
| Krzysztof Helt | 09fe75f | 2007-10-16 01:28:56 -0700 | [diff] [blame] | 181 | if (display->type != S3C2410_LCDCON1_TFT) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 182 | /* 8 bpp 332 */ | 
|  | 183 | var->red.length		= 3; | 
|  | 184 | var->red.offset		= 5; | 
|  | 185 | var->green.length	= 3; | 
|  | 186 | var->green.offset	= 2; | 
|  | 187 | var->blue.length	= 2; | 
| Arnaud Patard (Rtp | 357b819 | 2006-12-08 02:40:23 -0800 | [diff] [blame] | 188 | var->blue.offset	= 0; | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 189 | } else { | 
|  | 190 | var->red.offset		= 0; | 
| Arnaud Patard (Rtp | 357b819 | 2006-12-08 02:40:23 -0800 | [diff] [blame] | 191 | var->red.length		= 8; | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 192 | var->green		= var->red; | 
|  | 193 | var->blue		= var->red; | 
|  | 194 | } | 
|  | 195 | break; | 
|  | 196 | case 12: | 
|  | 197 | /* 12 bpp 444 */ | 
|  | 198 | var->red.length		= 4; | 
|  | 199 | var->red.offset		= 8; | 
|  | 200 | var->green.length	= 4; | 
|  | 201 | var->green.offset	= 4; | 
|  | 202 | var->blue.length	= 4; | 
|  | 203 | var->blue.offset	= 0; | 
|  | 204 | break; | 
|  | 205 |  | 
|  | 206 | default: | 
|  | 207 | case 16: | 
| Krzysztof Helt | f28ef57 | 2007-10-16 01:28:58 -0700 | [diff] [blame] | 208 | if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 209 | /* 16 bpp, 565 format */ | 
|  | 210 | var->red.offset		= 11; | 
|  | 211 | var->green.offset	= 5; | 
| Arnaud Patard (Rtp | 357b819 | 2006-12-08 02:40:23 -0800 | [diff] [blame] | 212 | var->blue.offset	= 0; | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 213 | var->red.length		= 5; | 
|  | 214 | var->green.length	= 6; | 
|  | 215 | var->blue.length	= 5; | 
|  | 216 | } else { | 
|  | 217 | /* 16 bpp, 5551 format */ | 
|  | 218 | var->red.offset		= 11; | 
|  | 219 | var->green.offset	= 6; | 
|  | 220 | var->blue.offset	= 1; | 
|  | 221 | var->red.length		= 5; | 
|  | 222 | var->green.length	= 5; | 
|  | 223 | var->blue.length	= 5; | 
|  | 224 | } | 
|  | 225 | break; | 
| Krzysztof Helt | 93613b9 | 2007-10-16 01:29:02 -0700 | [diff] [blame] | 226 | case 32: | 
|  | 227 | /* 24 bpp 888 and 8 dummy */ | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 228 | var->red.length		= 8; | 
|  | 229 | var->red.offset		= 16; | 
|  | 230 | var->green.length	= 8; | 
|  | 231 | var->green.offset	= 8; | 
|  | 232 | var->blue.length	= 8; | 
|  | 233 | var->blue.offset	= 0; | 
|  | 234 | break; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 235 | } | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 236 | return 0; | 
|  | 237 | } | 
|  | 238 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 239 | /* s3c2410fb_calculate_stn_lcd_regs | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 240 | * | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 241 | * calculate register values from var settings | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 242 | */ | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 243 | static void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info, | 
|  | 244 | struct s3c2410fb_hw *regs) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 245 | { | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 246 | const struct s3c2410fb_info *fbi = info->par; | 
|  | 247 | const struct fb_var_screeninfo *var = &info->var; | 
|  | 248 | int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT; | 
|  | 249 | int hs = var->xres >> 2; | 
|  | 250 | unsigned wdly = (var->left_margin >> 4) - 1; | 
| Krzysztof Helt | 93d11f5 | 2007-10-16 01:29:00 -0700 | [diff] [blame] | 251 | unsigned wlh = (var->hsync_len >> 4) - 1; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 252 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 253 | if (type != S3C2410_LCDCON1_STN4) | 
|  | 254 | hs >>= 1; | 
|  | 255 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 256 | switch (var->bits_per_pixel) { | 
|  | 257 | case 1: | 
|  | 258 | regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP; | 
|  | 259 | break; | 
|  | 260 | case 2: | 
|  | 261 | regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY; | 
|  | 262 | break; | 
|  | 263 | case 4: | 
|  | 264 | regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY; | 
|  | 265 | break; | 
|  | 266 | case 8: | 
|  | 267 | regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP; | 
|  | 268 | hs *= 3; | 
|  | 269 | break; | 
|  | 270 | case 12: | 
|  | 271 | regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP; | 
|  | 272 | hs *= 3; | 
|  | 273 | break; | 
|  | 274 |  | 
|  | 275 | default: | 
|  | 276 | /* invalid pixel depth */ | 
|  | 277 | dev_err(fbi->dev, "invalid bpp %d\n", | 
|  | 278 | var->bits_per_pixel); | 
|  | 279 | } | 
|  | 280 | /* update X/Y info */ | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 281 | dprintk("setting horz: lft=%d, rt=%d, sync=%d\n", | 
|  | 282 | var->left_margin, var->right_margin, var->hsync_len); | 
|  | 283 |  | 
| Krzysztof Helt | 3c9ffd0 | 2007-10-16 01:28:59 -0700 | [diff] [blame] | 284 | regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1); | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 285 |  | 
|  | 286 | if (wdly > 3) | 
|  | 287 | wdly = 3; | 
|  | 288 |  | 
| Krzysztof Helt | 93d11f5 | 2007-10-16 01:29:00 -0700 | [diff] [blame] | 289 | if (wlh > 3) | 
|  | 290 | wlh = 3; | 
|  | 291 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 292 | regs->lcdcon3 =	S3C2410_LCDCON3_WDLY(wdly) | | 
|  | 293 | S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) | | 
|  | 294 | S3C2410_LCDCON3_HOZVAL(hs - 1); | 
| Krzysztof Helt | 93d11f5 | 2007-10-16 01:29:00 -0700 | [diff] [blame] | 295 |  | 
| Krzysztof Helt | e92e739 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 296 | regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh); | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 297 | } | 
|  | 298 |  | 
|  | 299 | /* s3c2410fb_calculate_tft_lcd_regs | 
|  | 300 | * | 
|  | 301 | * calculate register values from var settings | 
|  | 302 | */ | 
|  | 303 | static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info, | 
|  | 304 | struct s3c2410fb_hw *regs) | 
|  | 305 | { | 
|  | 306 | const struct s3c2410fb_info *fbi = info->par; | 
|  | 307 | const struct fb_var_screeninfo *var = &info->var; | 
|  | 308 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 309 | switch (var->bits_per_pixel) { | 
|  | 310 | case 1: | 
|  | 311 | regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP; | 
|  | 312 | break; | 
|  | 313 | case 2: | 
|  | 314 | regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP; | 
|  | 315 | break; | 
|  | 316 | case 4: | 
|  | 317 | regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP; | 
|  | 318 | break; | 
|  | 319 | case 8: | 
|  | 320 | regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP; | 
| Krzysztof Helt | 93613b9 | 2007-10-16 01:29:02 -0700 | [diff] [blame] | 321 | regs->lcdcon5 |= S3C2410_LCDCON5_BSWP | | 
|  | 322 | S3C2410_LCDCON5_FRM565; | 
|  | 323 | regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 324 | break; | 
|  | 325 | case 16: | 
|  | 326 | regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP; | 
| Krzysztof Helt | 93613b9 | 2007-10-16 01:29:02 -0700 | [diff] [blame] | 327 | regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP; | 
|  | 328 | regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 329 | break; | 
| Krzysztof Helt | 93613b9 | 2007-10-16 01:29:02 -0700 | [diff] [blame] | 330 | case 32: | 
|  | 331 | regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP; | 
|  | 332 | regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP | | 
|  | 333 | S3C2410_LCDCON5_HWSWP | | 
|  | 334 | S3C2410_LCDCON5_BPP24BL); | 
|  | 335 | break; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 336 | default: | 
|  | 337 | /* invalid pixel depth */ | 
|  | 338 | dev_err(fbi->dev, "invalid bpp %d\n", | 
|  | 339 | var->bits_per_pixel); | 
|  | 340 | } | 
|  | 341 | /* update X/Y info */ | 
|  | 342 | dprintk("setting vert: up=%d, low=%d, sync=%d\n", | 
|  | 343 | var->upper_margin, var->lower_margin, var->vsync_len); | 
|  | 344 |  | 
|  | 345 | dprintk("setting horz: lft=%d, rt=%d, sync=%d\n", | 
|  | 346 | var->left_margin, var->right_margin, var->hsync_len); | 
|  | 347 |  | 
| Krzysztof Helt | 93d11f5 | 2007-10-16 01:29:00 -0700 | [diff] [blame] | 348 | regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) | | 
|  | 349 | S3C2410_LCDCON2_VBPD(var->upper_margin - 1) | | 
|  | 350 | S3C2410_LCDCON2_VFPD(var->lower_margin - 1) | | 
|  | 351 | S3C2410_LCDCON2_VSPW(var->vsync_len - 1); | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 352 |  | 
|  | 353 | regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) | | 
|  | 354 | S3C2410_LCDCON3_HFPD(var->left_margin - 1) | | 
|  | 355 | S3C2410_LCDCON3_HOZVAL(var->xres - 1); | 
| Krzysztof Helt | 93d11f5 | 2007-10-16 01:29:00 -0700 | [diff] [blame] | 356 |  | 
| Krzysztof Helt | e92e739 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 357 | regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1); | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 358 | } | 
|  | 359 |  | 
|  | 360 | /* s3c2410fb_activate_var | 
|  | 361 | * | 
|  | 362 | * activate (set) the controller from the given framebuffer | 
|  | 363 | * information | 
|  | 364 | */ | 
|  | 365 | static void s3c2410fb_activate_var(struct fb_info *info) | 
|  | 366 | { | 
|  | 367 | struct s3c2410fb_info *fbi = info->par; | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 368 | void __iomem *regs = fbi->io; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 369 | int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 370 | struct fb_var_screeninfo *var = &info->var; | 
| Krzysztof Helt | 6981669 | 2007-10-16 01:29:06 -0700 | [diff] [blame] | 371 | int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2; | 
| Arnaud Patard (Rtp | 357b819 | 2006-12-08 02:40:23 -0800 | [diff] [blame] | 372 |  | 
| Harvey Harrison | 5ae1217 | 2008-04-28 02:15:47 -0700 | [diff] [blame] | 373 | dprintk("%s: var->xres  = %d\n", __func__, var->xres); | 
|  | 374 | dprintk("%s: var->yres  = %d\n", __func__, var->yres); | 
|  | 375 | dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 376 |  | 
| Krzysztof Helt | 6981669 | 2007-10-16 01:29:06 -0700 | [diff] [blame] | 377 | if (type == S3C2410_LCDCON1_TFT) { | 
|  | 378 | s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs); | 
|  | 379 | --clkdiv; | 
|  | 380 | if (clkdiv < 0) | 
|  | 381 | clkdiv = 0; | 
|  | 382 | } else { | 
|  | 383 | s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs); | 
|  | 384 | if (clkdiv < 2) | 
|  | 385 | clkdiv = 2; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 386 | } | 
|  | 387 |  | 
| Krzysztof Helt | 6981669 | 2007-10-16 01:29:06 -0700 | [diff] [blame] | 388 | fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv); | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 389 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 390 | /* write new registers */ | 
|  | 391 |  | 
|  | 392 | dprintk("new register set:\n"); | 
|  | 393 | dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1); | 
|  | 394 | dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2); | 
|  | 395 | dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3); | 
|  | 396 | dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4); | 
|  | 397 | dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5); | 
|  | 398 |  | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 399 | writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, | 
|  | 400 | regs + S3C2410_LCDCON1); | 
|  | 401 | writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2); | 
|  | 402 | writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3); | 
|  | 403 | writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4); | 
|  | 404 | writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 405 |  | 
|  | 406 | /* set lcd address pointers */ | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 407 | s3c2410fb_set_lcdaddr(info); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 408 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 409 | fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID, | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 410 | writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 411 | } | 
|  | 412 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 413 | /* | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 414 | *      s3c2410fb_set_par - Alters the hardware state. | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 415 | *      @info: frame buffer structure that represents a single frame buffer | 
|  | 416 | * | 
|  | 417 | */ | 
|  | 418 | static int s3c2410fb_set_par(struct fb_info *info) | 
|  | 419 | { | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 420 | struct fb_var_screeninfo *var = &info->var; | 
|  | 421 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 422 | switch (var->bits_per_pixel) { | 
| Krzysztof Helt | 93613b9 | 2007-10-16 01:29:02 -0700 | [diff] [blame] | 423 | case 32: | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 424 | case 16: | 
| Krzysztof Helt | 93613b9 | 2007-10-16 01:29:02 -0700 | [diff] [blame] | 425 | case 12: | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 426 | info->fix.visual = FB_VISUAL_TRUECOLOR; | 
|  | 427 | break; | 
|  | 428 | case 1: | 
|  | 429 | info->fix.visual = FB_VISUAL_MONO01; | 
|  | 430 | break; | 
|  | 431 | default: | 
|  | 432 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | 
|  | 433 | break; | 
| Arnaud Patard (Rtp | 357b819 | 2006-12-08 02:40:23 -0800 | [diff] [blame] | 434 | } | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 435 |  | 
| Stefan Schmidt | a103360 | 2008-01-21 17:18:27 -0800 | [diff] [blame] | 436 | info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 437 |  | 
|  | 438 | /* activate this new configuration */ | 
|  | 439 |  | 
| Krzysztof Helt | 9939a48 | 2007-10-16 01:28:57 -0700 | [diff] [blame] | 440 | s3c2410fb_activate_var(info); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 441 | return 0; | 
|  | 442 | } | 
|  | 443 |  | 
|  | 444 | static void schedule_palette_update(struct s3c2410fb_info *fbi, | 
|  | 445 | unsigned int regno, unsigned int val) | 
|  | 446 | { | 
|  | 447 | unsigned long flags; | 
|  | 448 | unsigned long irqen; | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 449 | void __iomem *irq_base = fbi->irq_base; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 450 |  | 
|  | 451 | local_irq_save(flags); | 
|  | 452 |  | 
|  | 453 | fbi->palette_buffer[regno] = val; | 
|  | 454 |  | 
|  | 455 | if (!fbi->palette_ready) { | 
|  | 456 | fbi->palette_ready = 1; | 
|  | 457 |  | 
|  | 458 | /* enable IRQ */ | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 459 | irqen = readl(irq_base + S3C24XX_LCDINTMSK); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 460 | irqen &= ~S3C2410_LCDINT_FRSYNC; | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 461 | writel(irqen, irq_base + S3C24XX_LCDINTMSK); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 462 | } | 
|  | 463 |  | 
|  | 464 | local_irq_restore(flags); | 
|  | 465 | } | 
|  | 466 |  | 
|  | 467 | /* from pxafb.c */ | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 468 | static inline unsigned int chan_to_field(unsigned int chan, | 
|  | 469 | struct fb_bitfield *bf) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 470 | { | 
|  | 471 | chan &= 0xffff; | 
|  | 472 | chan >>= 16 - bf->length; | 
|  | 473 | return chan << bf->offset; | 
|  | 474 | } | 
|  | 475 |  | 
|  | 476 | static int s3c2410fb_setcolreg(unsigned regno, | 
|  | 477 | unsigned red, unsigned green, unsigned blue, | 
|  | 478 | unsigned transp, struct fb_info *info) | 
|  | 479 | { | 
|  | 480 | struct s3c2410fb_info *fbi = info->par; | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 481 | void __iomem *regs = fbi->io; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 482 | unsigned int val; | 
|  | 483 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 484 | /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", | 
|  | 485 | regno, red, green, blue); */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 486 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 487 | switch (info->fix.visual) { | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 488 | case FB_VISUAL_TRUECOLOR: | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 489 | /* true-colour, use pseudo-palette */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 490 |  | 
|  | 491 | if (regno < 16) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 492 | u32 *pal = info->pseudo_palette; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 493 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 494 | val  = chan_to_field(red,   &info->var.red); | 
|  | 495 | val |= chan_to_field(green, &info->var.green); | 
|  | 496 | val |= chan_to_field(blue,  &info->var.blue); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 497 |  | 
|  | 498 | pal[regno] = val; | 
|  | 499 | } | 
|  | 500 | break; | 
|  | 501 |  | 
|  | 502 | case FB_VISUAL_PSEUDOCOLOR: | 
|  | 503 | if (regno < 256) { | 
|  | 504 | /* currently assume RGB 5-6-5 mode */ | 
|  | 505 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 506 | val  = (red   >>  0) & 0xf800; | 
|  | 507 | val |= (green >>  5) & 0x07e0; | 
|  | 508 | val |= (blue  >> 11) & 0x001f; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 509 |  | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 510 | writel(val, regs + S3C2410_TFTPAL(regno)); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 511 | schedule_palette_update(fbi, regno, val); | 
|  | 512 | } | 
|  | 513 |  | 
|  | 514 | break; | 
|  | 515 |  | 
|  | 516 | default: | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 517 | return 1;	/* unknown type */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 518 | } | 
|  | 519 |  | 
|  | 520 | return 0; | 
|  | 521 | } | 
|  | 522 |  | 
| Ben Dooks | 673b460 | 2008-05-23 13:04:55 -0700 | [diff] [blame] | 523 | /* s3c2410fb_lcd_enable | 
|  | 524 | * | 
|  | 525 | * shutdown the lcd controller | 
|  | 526 | */ | 
|  | 527 | static void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable) | 
|  | 528 | { | 
|  | 529 | unsigned long flags; | 
|  | 530 |  | 
|  | 531 | local_irq_save(flags); | 
|  | 532 |  | 
|  | 533 | if (enable) | 
|  | 534 | fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID; | 
|  | 535 | else | 
|  | 536 | fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID; | 
|  | 537 |  | 
|  | 538 | writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1); | 
|  | 539 |  | 
|  | 540 | local_irq_restore(flags); | 
|  | 541 | } | 
|  | 542 |  | 
|  | 543 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 544 | /* | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 545 | *      s3c2410fb_blank | 
|  | 546 | *	@blank_mode: the blank mode we want. | 
|  | 547 | *	@info: frame buffer structure that represents a single frame buffer | 
|  | 548 | * | 
|  | 549 | *	Blank the screen if blank_mode != 0, else unblank. Return 0 if | 
|  | 550 | *	blanking succeeded, != 0 if un-/blanking failed due to e.g. a | 
|  | 551 | *	video mode which doesn't support it. Implements VESA suspend | 
|  | 552 | *	and powerdown modes on hardware that supports disabling hsync/vsync: | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 553 | * | 
|  | 554 | *	Returns negative errno on error, or zero on success. | 
|  | 555 | * | 
|  | 556 | */ | 
|  | 557 | static int s3c2410fb_blank(int blank_mode, struct fb_info *info) | 
|  | 558 | { | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 559 | struct s3c2410fb_info *fbi = info->par; | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 560 | void __iomem *tpal_reg = fbi->io; | 
| Krzysztof Helt | 7ee0fe4 | 2007-10-16 01:29:01 -0700 | [diff] [blame] | 561 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 562 | dprintk("blank(mode=%d, info=%p)\n", blank_mode, info); | 
|  | 563 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 564 | tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL; | 
|  | 565 |  | 
| Ben Dooks | 673b460 | 2008-05-23 13:04:55 -0700 | [diff] [blame] | 566 | if (blank_mode == FB_BLANK_POWERDOWN) { | 
|  | 567 | s3c2410fb_lcd_enable(fbi, 0); | 
|  | 568 | } else { | 
|  | 569 | s3c2410fb_lcd_enable(fbi, 1); | 
|  | 570 | } | 
|  | 571 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 572 | if (blank_mode == FB_BLANK_UNBLANK) | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 573 | writel(0x0, tpal_reg); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 574 | else { | 
|  | 575 | dprintk("setting TPAL to output 0x000000\n"); | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 576 | writel(S3C2410_TPAL_EN, tpal_reg); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 577 | } | 
|  | 578 |  | 
|  | 579 | return 0; | 
|  | 580 | } | 
|  | 581 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 582 | static int s3c2410fb_debug_show(struct device *dev, | 
|  | 583 | struct device_attribute *attr, char *buf) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 584 | { | 
|  | 585 | return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off"); | 
|  | 586 | } | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 587 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 588 | static int s3c2410fb_debug_store(struct device *dev, | 
|  | 589 | struct device_attribute *attr, | 
|  | 590 | const char *buf, size_t len) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 591 | { | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 592 | if (len < 1) | 
|  | 593 | return -EINVAL; | 
|  | 594 |  | 
|  | 595 | if (strnicmp(buf, "on", 2) == 0 || | 
|  | 596 | strnicmp(buf, "1", 1) == 0) { | 
|  | 597 | debug = 1; | 
|  | 598 | printk(KERN_DEBUG "s3c2410fb: Debug On"); | 
|  | 599 | } else if (strnicmp(buf, "off", 3) == 0 || | 
|  | 600 | strnicmp(buf, "0", 1) == 0) { | 
|  | 601 | debug = 0; | 
|  | 602 | printk(KERN_DEBUG "s3c2410fb: Debug Off"); | 
|  | 603 | } else { | 
|  | 604 | return -EINVAL; | 
|  | 605 | } | 
|  | 606 |  | 
|  | 607 | return len; | 
|  | 608 | } | 
|  | 609 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 610 | static DEVICE_ATTR(debug, 0666, s3c2410fb_debug_show, s3c2410fb_debug_store); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 611 |  | 
|  | 612 | static struct fb_ops s3c2410fb_ops = { | 
|  | 613 | .owner		= THIS_MODULE, | 
|  | 614 | .fb_check_var	= s3c2410fb_check_var, | 
|  | 615 | .fb_set_par	= s3c2410fb_set_par, | 
|  | 616 | .fb_blank	= s3c2410fb_blank, | 
|  | 617 | .fb_setcolreg	= s3c2410fb_setcolreg, | 
|  | 618 | .fb_fillrect	= cfb_fillrect, | 
|  | 619 | .fb_copyarea	= cfb_copyarea, | 
|  | 620 | .fb_imageblit	= cfb_imageblit, | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 621 | }; | 
|  | 622 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 623 | /* | 
|  | 624 | * s3c2410fb_map_video_memory(): | 
|  | 625 | *	Allocates the DRAM memory for the frame buffer.  This buffer is | 
|  | 626 | *	remapped into a non-cached, non-buffered, memory region to | 
|  | 627 | *	allow palette and pixel writes to occur without flushing the | 
|  | 628 | *	cache.  Once this area is remapped, all virtual memory | 
|  | 629 | *	access to the video memory should occur at the new region. | 
|  | 630 | */ | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 631 | static int __init s3c2410fb_map_video_memory(struct fb_info *info) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 632 | { | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 633 | struct s3c2410fb_info *fbi = info->par; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 634 | dma_addr_t map_dma; | 
|  | 635 | unsigned map_size = PAGE_ALIGN(info->fix.smem_len); | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 636 |  | 
| Ben Dooks | 38a02f56 | 2008-02-06 01:39:42 -0800 | [diff] [blame] | 637 | dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 638 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 639 | info->screen_base = dma_alloc_writecombine(fbi->dev, map_size, | 
|  | 640 | &map_dma, GFP_KERNEL); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 641 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 642 | if (info->screen_base) { | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 643 | /* prevent initial garbage on screen */ | 
|  | 644 | dprintk("map_video_memory: clear %p:%08x\n", | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 645 | info->screen_base, map_size); | 
| Ben Dooks | c0d4033 | 2008-02-06 01:39:43 -0800 | [diff] [blame] | 646 | memset(info->screen_base, 0x00, map_size); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 647 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 648 | info->fix.smem_start = map_dma; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 649 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 650 | dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n", | 
|  | 651 | info->fix.smem_start, info->screen_base, map_size); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 652 | } | 
|  | 653 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 654 | return info->screen_base ? 0 : -ENOMEM; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 655 | } | 
|  | 656 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 657 | static inline void s3c2410fb_unmap_video_memory(struct fb_info *info) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 658 | { | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 659 | struct s3c2410fb_info *fbi = info->par; | 
|  | 660 |  | 
|  | 661 | dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len), | 
|  | 662 | info->screen_base, info->fix.smem_start); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 663 | } | 
|  | 664 |  | 
|  | 665 | static inline void modify_gpio(void __iomem *reg, | 
|  | 666 | unsigned long set, unsigned long mask) | 
|  | 667 | { | 
|  | 668 | unsigned long tmp; | 
|  | 669 |  | 
|  | 670 | tmp = readl(reg) & ~mask; | 
|  | 671 | writel(tmp | set, reg); | 
|  | 672 | } | 
|  | 673 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 674 | /* | 
|  | 675 | * s3c2410fb_init_registers - Initialise all LCD-related registers | 
|  | 676 | */ | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 677 | static int s3c2410fb_init_registers(struct fb_info *info) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 678 | { | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 679 | struct s3c2410fb_info *fbi = info->par; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 680 | struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 681 | unsigned long flags; | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 682 | void __iomem *regs = fbi->io; | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 683 | void __iomem *tpal; | 
|  | 684 | void __iomem *lpcsel; | 
|  | 685 |  | 
|  | 686 | if (is_s3c2412(fbi)) { | 
|  | 687 | tpal = regs + S3C2412_TPAL; | 
|  | 688 | lpcsel = regs + S3C2412_TCONSEL; | 
|  | 689 | } else { | 
|  | 690 | tpal = regs + S3C2410_TPAL; | 
|  | 691 | lpcsel = regs + S3C2410_LPCSEL; | 
|  | 692 | } | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 693 |  | 
|  | 694 | /* Initialise LCD with values from haret */ | 
|  | 695 |  | 
|  | 696 | local_irq_save(flags); | 
|  | 697 |  | 
|  | 698 | /* modify the gpio(s) with interrupts set (bjd) */ | 
|  | 699 |  | 
|  | 700 | modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask); | 
|  | 701 | modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask); | 
|  | 702 | modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask); | 
|  | 703 | modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask); | 
|  | 704 |  | 
|  | 705 | local_irq_restore(flags); | 
|  | 706 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 707 | dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel); | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 708 | writel(mach_info->lpcsel, lpcsel); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 709 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 710 | dprintk("replacing TPAL %08x\n", readl(tpal)); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 711 |  | 
|  | 712 | /* ensure temporary palette disabled */ | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 713 | writel(0x00, tpal); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 714 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 715 | return 0; | 
|  | 716 | } | 
|  | 717 |  | 
|  | 718 | static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi) | 
|  | 719 | { | 
|  | 720 | unsigned int i; | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 721 | void __iomem *regs = fbi->io; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 722 |  | 
|  | 723 | fbi->palette_ready = 0; | 
|  | 724 |  | 
|  | 725 | for (i = 0; i < 256; i++) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 726 | unsigned long ent = fbi->palette_buffer[i]; | 
|  | 727 | if (ent == PALETTE_BUFF_CLEAR) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 728 | continue; | 
|  | 729 |  | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 730 | writel(ent, regs + S3C2410_TFTPAL(i)); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 731 |  | 
|  | 732 | /* it seems the only way to know exactly | 
|  | 733 | * if the palette wrote ok, is to check | 
|  | 734 | * to see if the value verifies ok | 
|  | 735 | */ | 
|  | 736 |  | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 737 | if (readw(regs + S3C2410_TFTPAL(i)) == ent) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 738 | fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR; | 
|  | 739 | else | 
|  | 740 | fbi->palette_ready = 1;   /* retry */ | 
|  | 741 | } | 
|  | 742 | } | 
|  | 743 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 744 | static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 745 | { | 
|  | 746 | struct s3c2410fb_info *fbi = dev_id; | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 747 | void __iomem *irq_base = fbi->irq_base; | 
|  | 748 | unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 749 |  | 
|  | 750 | if (lcdirq & S3C2410_LCDINT_FRSYNC) { | 
|  | 751 | if (fbi->palette_ready) | 
|  | 752 | s3c2410fb_write_palette(fbi); | 
|  | 753 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 754 | writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); | 
|  | 755 | writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 756 | } | 
|  | 757 |  | 
|  | 758 | return IRQ_HANDLED; | 
|  | 759 | } | 
|  | 760 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 761 | static char driver_name[] = "s3c2410fb"; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 762 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 763 | static int __init s3c24xxfb_probe(struct platform_device *pdev, | 
|  | 764 | enum s3c_drv_type drv_type) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 765 | { | 
|  | 766 | struct s3c2410fb_info *info; | 
| Krzysztof Helt | 09fe75f | 2007-10-16 01:28:56 -0700 | [diff] [blame] | 767 | struct s3c2410fb_display *display; | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 768 | struct fb_info *fbinfo; | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 769 | struct s3c2410fb_mach_info *mach_info; | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 770 | struct resource *res; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 771 | int ret; | 
|  | 772 | int irq; | 
|  | 773 | int i; | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 774 | int size; | 
| Arnaud Patard | 6931a76 | 2006-06-26 00:26:45 -0700 | [diff] [blame] | 775 | u32 lcdcon1; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 776 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 777 | mach_info = pdev->dev.platform_data; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 778 | if (mach_info == NULL) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 779 | dev_err(&pdev->dev, | 
|  | 780 | "no platform data for lcd, cannot attach\n"); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 781 | return -EINVAL; | 
|  | 782 | } | 
|  | 783 |  | 
| Ben Dooks | e897363 | 2008-02-06 01:39:44 -0800 | [diff] [blame] | 784 | if (mach_info->default_display >= mach_info->num_displays) { | 
|  | 785 | dev_err(&pdev->dev, "default is %d but only %d displays\n", | 
|  | 786 | mach_info->default_display, mach_info->num_displays); | 
|  | 787 | return -EINVAL; | 
|  | 788 | } | 
|  | 789 |  | 
| Krzysztof Helt | 09fe75f | 2007-10-16 01:28:56 -0700 | [diff] [blame] | 790 | display = mach_info->displays + mach_info->default_display; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 791 |  | 
|  | 792 | irq = platform_get_irq(pdev, 0); | 
|  | 793 | if (irq < 0) { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 794 | dev_err(&pdev->dev, "no irq for device\n"); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 795 | return -ENOENT; | 
|  | 796 | } | 
|  | 797 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 798 | fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 799 | if (!fbinfo) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 800 | return -ENOMEM; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 801 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 802 | platform_set_drvdata(pdev, fbinfo); | 
|  | 803 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 804 | info = fbinfo->par; | 
| Ben Dooks | 0187f22 | 2007-02-16 01:28:42 -0800 | [diff] [blame] | 805 | info->dev = &pdev->dev; | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 806 | info->drv_type = drv_type; | 
| Ben Dooks | 0187f22 | 2007-02-16 01:28:42 -0800 | [diff] [blame] | 807 |  | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 808 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 809 | if (res == NULL) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 810 | dev_err(&pdev->dev, "failed to get memory registers\n"); | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 811 | ret = -ENXIO; | 
|  | 812 | goto dealloc_fb; | 
|  | 813 | } | 
|  | 814 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 815 | size = (res->end - res->start) + 1; | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 816 | info->mem = request_mem_region(res->start, size, pdev->name); | 
|  | 817 | if (info->mem == NULL) { | 
|  | 818 | dev_err(&pdev->dev, "failed to get memory region\n"); | 
|  | 819 | ret = -ENOENT; | 
|  | 820 | goto dealloc_fb; | 
|  | 821 | } | 
|  | 822 |  | 
|  | 823 | info->io = ioremap(res->start, size); | 
|  | 824 | if (info->io == NULL) { | 
|  | 825 | dev_err(&pdev->dev, "ioremap() of registers failed\n"); | 
|  | 826 | ret = -ENXIO; | 
|  | 827 | goto release_mem; | 
|  | 828 | } | 
|  | 829 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 830 | info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); | 
|  | 831 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 832 | dprintk("devinit\n"); | 
|  | 833 |  | 
|  | 834 | strcpy(fbinfo->fix.id, driver_name); | 
|  | 835 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 836 | /* Stop the video */ | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 837 | lcdcon1 = readl(info->io + S3C2410_LCDCON1); | 
|  | 838 | writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); | 
| Arnaud Patard | 6931a76 | 2006-06-26 00:26:45 -0700 | [diff] [blame] | 839 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 840 | fbinfo->fix.type	    = FB_TYPE_PACKED_PIXELS; | 
|  | 841 | fbinfo->fix.type_aux	    = 0; | 
|  | 842 | fbinfo->fix.xpanstep	    = 0; | 
|  | 843 | fbinfo->fix.ypanstep	    = 0; | 
|  | 844 | fbinfo->fix.ywrapstep	    = 0; | 
|  | 845 | fbinfo->fix.accel	    = FB_ACCEL_NONE; | 
|  | 846 |  | 
|  | 847 | fbinfo->var.nonstd	    = 0; | 
|  | 848 | fbinfo->var.activate	    = FB_ACTIVATE_NOW; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 849 | fbinfo->var.accel_flags     = 0; | 
|  | 850 | fbinfo->var.vmode	    = FB_VMODE_NONINTERLACED; | 
|  | 851 |  | 
|  | 852 | fbinfo->fbops		    = &s3c2410fb_ops; | 
|  | 853 | fbinfo->flags		    = FBINFO_FLAG_DEFAULT; | 
|  | 854 | fbinfo->pseudo_palette      = &info->pseudo_pal; | 
|  | 855 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 856 | for (i = 0; i < 256; i++) | 
|  | 857 | info->palette_buffer[i] = PALETTE_BUFF_CLEAR; | 
|  | 858 |  | 
| Thomas Gleixner | 63a4339 | 2006-07-01 19:29:45 -0700 | [diff] [blame] | 859 | ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 860 | if (ret) { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 861 | dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 862 | ret = -EBUSY; | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 863 | goto release_regs; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 864 | } | 
|  | 865 |  | 
|  | 866 | info->clk = clk_get(NULL, "lcd"); | 
|  | 867 | if (!info->clk || IS_ERR(info->clk)) { | 
|  | 868 | printk(KERN_ERR "failed to get lcd clock source\n"); | 
|  | 869 | ret = -ENOENT; | 
|  | 870 | goto release_irq; | 
|  | 871 | } | 
|  | 872 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 873 | clk_enable(info->clk); | 
|  | 874 | dprintk("got and enabled clock\n"); | 
|  | 875 |  | 
|  | 876 | msleep(1); | 
|  | 877 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 878 | /* find maximum required memory size for display */ | 
|  | 879 | for (i = 0; i < mach_info->num_displays; i++) { | 
|  | 880 | unsigned long smem_len = mach_info->displays[i].xres; | 
|  | 881 |  | 
|  | 882 | smem_len *= mach_info->displays[i].yres; | 
|  | 883 | smem_len *= mach_info->displays[i].bpp; | 
|  | 884 | smem_len >>= 3; | 
|  | 885 | if (fbinfo->fix.smem_len < smem_len) | 
|  | 886 | fbinfo->fix.smem_len = smem_len; | 
|  | 887 | } | 
|  | 888 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 889 | /* Initialize video memory */ | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 890 | ret = s3c2410fb_map_video_memory(fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 891 | if (ret) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 892 | printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 893 | ret = -ENOMEM; | 
|  | 894 | goto release_clock; | 
|  | 895 | } | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 896 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 897 | dprintk("got video memory\n"); | 
|  | 898 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 899 | fbinfo->var.xres = display->xres; | 
|  | 900 | fbinfo->var.yres = display->yres; | 
|  | 901 | fbinfo->var.bits_per_pixel = display->bpp; | 
|  | 902 |  | 
| Krzysztof Helt | 110c1fa | 2007-10-16 01:28:55 -0700 | [diff] [blame] | 903 | s3c2410fb_init_registers(fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 904 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 905 | s3c2410fb_check_var(&fbinfo->var, fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 906 |  | 
|  | 907 | ret = register_framebuffer(fbinfo); | 
|  | 908 | if (ret < 0) { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 909 | printk(KERN_ERR "Failed to register framebuffer device: %d\n", | 
|  | 910 | ret); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 911 | goto free_video_memory; | 
|  | 912 | } | 
|  | 913 |  | 
|  | 914 | /* create device files */ | 
| Ben Dooks | d585dfe | 2008-05-23 13:04:56 -0700 | [diff] [blame] | 915 | ret = device_create_file(&pdev->dev, &dev_attr_debug); | 
|  | 916 | if (ret) { | 
|  | 917 | printk(KERN_ERR "failed to add debug attribute\n"); | 
|  | 918 | } | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 919 |  | 
|  | 920 | printk(KERN_INFO "fb%d: %s frame buffer device\n", | 
|  | 921 | fbinfo->node, fbinfo->fix.id); | 
|  | 922 |  | 
|  | 923 | return 0; | 
|  | 924 |  | 
|  | 925 | free_video_memory: | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 926 | s3c2410fb_unmap_video_memory(fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 927 | release_clock: | 
|  | 928 | clk_disable(info->clk); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 929 | clk_put(info->clk); | 
|  | 930 | release_irq: | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 931 | free_irq(irq, info); | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 932 | release_regs: | 
|  | 933 | iounmap(info->io); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 934 | release_mem: | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 935 | release_resource(info->mem); | 
|  | 936 | kfree(info->mem); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 937 | dealloc_fb: | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 938 | platform_set_drvdata(pdev, NULL); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 939 | framebuffer_release(fbinfo); | 
|  | 940 | return ret; | 
|  | 941 | } | 
|  | 942 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 943 | static int __init s3c2410fb_probe(struct platform_device *pdev) | 
|  | 944 | { | 
|  | 945 | return s3c24xxfb_probe(pdev, DRV_S3C2410); | 
|  | 946 | } | 
|  | 947 |  | 
|  | 948 | static int __init s3c2412fb_probe(struct platform_device *pdev) | 
|  | 949 | { | 
|  | 950 | return s3c24xxfb_probe(pdev, DRV_S3C2412); | 
|  | 951 | } | 
|  | 952 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 953 |  | 
|  | 954 | /* | 
|  | 955 | *  Cleanup | 
|  | 956 | */ | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 957 | static int s3c2410fb_remove(struct platform_device *pdev) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 958 | { | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 959 | struct fb_info *fbinfo = platform_get_drvdata(pdev); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 960 | struct s3c2410fb_info *info = fbinfo->par; | 
|  | 961 | int irq; | 
|  | 962 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 963 | unregister_framebuffer(fbinfo); | 
|  | 964 |  | 
| Ben Dooks | 673b460 | 2008-05-23 13:04:55 -0700 | [diff] [blame] | 965 | s3c2410fb_lcd_enable(info, 0); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 966 | msleep(1); | 
|  | 967 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 968 | s3c2410fb_unmap_video_memory(fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 969 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 970 | if (info->clk) { | 
|  | 971 | clk_disable(info->clk); | 
|  | 972 | clk_put(info->clk); | 
|  | 973 | info->clk = NULL; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 974 | } | 
|  | 975 |  | 
|  | 976 | irq = platform_get_irq(pdev, 0); | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 977 | free_irq(irq, info); | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 978 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 979 | iounmap(info->io); | 
|  | 980 |  | 
| Ben Dooks | aff39a8 | 2007-07-31 00:37:37 -0700 | [diff] [blame] | 981 | release_resource(info->mem); | 
|  | 982 | kfree(info->mem); | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 983 |  | 
|  | 984 | platform_set_drvdata(pdev, NULL); | 
|  | 985 | framebuffer_release(fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 986 |  | 
|  | 987 | return 0; | 
|  | 988 | } | 
|  | 989 |  | 
|  | 990 | #ifdef CONFIG_PM | 
|  | 991 |  | 
|  | 992 | /* suspend and resume support for the lcd controller */ | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 993 | static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 994 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 995 | struct fb_info	   *fbinfo = platform_get_drvdata(dev); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 996 | struct s3c2410fb_info *info = fbinfo->par; | 
|  | 997 |  | 
| Ben Dooks | 673b460 | 2008-05-23 13:04:55 -0700 | [diff] [blame] | 998 | s3c2410fb_lcd_enable(info, 0); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 999 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 1000 | /* sleep before disabling the clock, we need to ensure | 
|  | 1001 | * the LCD DMA engine is not going to get back on the bus | 
|  | 1002 | * before the clock goes off again (bjd) */ | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1003 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 1004 | msleep(1); | 
|  | 1005 | clk_disable(info->clk); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1006 |  | 
|  | 1007 | return 0; | 
|  | 1008 | } | 
|  | 1009 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 1010 | static int s3c2410fb_resume(struct platform_device *dev) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1011 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 1012 | struct fb_info	   *fbinfo = platform_get_drvdata(dev); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1013 | struct s3c2410fb_info *info = fbinfo->par; | 
|  | 1014 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 1015 | clk_enable(info->clk); | 
|  | 1016 | msleep(1); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1017 |  | 
| Krzysztof Helt | f046644 | 2008-01-14 00:55:20 -0800 | [diff] [blame] | 1018 | s3c2410fb_init_registers(fbinfo); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1019 |  | 
| Daniel Silverstone | 60f793d | 2009-02-10 13:40:38 +0100 | [diff] [blame] | 1020 | /* re-activate our display after resume */ | 
|  | 1021 | s3c2410fb_activate_var(fbinfo); | 
|  | 1022 | s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo); | 
|  | 1023 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1024 | return 0; | 
|  | 1025 | } | 
|  | 1026 |  | 
|  | 1027 | #else | 
|  | 1028 | #define s3c2410fb_suspend NULL | 
|  | 1029 | #define s3c2410fb_resume  NULL | 
|  | 1030 | #endif | 
|  | 1031 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 1032 | static struct platform_driver s3c2410fb_driver = { | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1033 | .probe		= s3c2410fb_probe, | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 1034 | .remove		= s3c2410fb_remove, | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1035 | .suspend	= s3c2410fb_suspend, | 
|  | 1036 | .resume		= s3c2410fb_resume, | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 1037 | .driver		= { | 
|  | 1038 | .name	= "s3c2410-lcd", | 
|  | 1039 | .owner	= THIS_MODULE, | 
|  | 1040 | }, | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1041 | }; | 
|  | 1042 |  | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 1043 | static struct platform_driver s3c2412fb_driver = { | 
|  | 1044 | .probe		= s3c2412fb_probe, | 
|  | 1045 | .remove		= s3c2410fb_remove, | 
|  | 1046 | .suspend	= s3c2410fb_suspend, | 
|  | 1047 | .resume		= s3c2410fb_resume, | 
|  | 1048 | .driver		= { | 
|  | 1049 | .name	= "s3c2412-lcd", | 
|  | 1050 | .owner	= THIS_MODULE, | 
|  | 1051 | }, | 
|  | 1052 | }; | 
|  | 1053 |  | 
| Krzysztof Helt | 9fa7bc0 | 2007-10-16 01:29:05 -0700 | [diff] [blame] | 1054 | int __init s3c2410fb_init(void) | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1055 | { | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 1056 | int ret = platform_driver_register(&s3c2410fb_driver); | 
|  | 1057 |  | 
|  | 1058 | if (ret == 0) | 
|  | 1059 | ret = platform_driver_register(&s3c2412fb_driver);; | 
|  | 1060 |  | 
|  | 1061 | return ret; | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1062 | } | 
|  | 1063 |  | 
|  | 1064 | static void __exit s3c2410fb_cleanup(void) | 
|  | 1065 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 1066 | platform_driver_unregister(&s3c2410fb_driver); | 
| Ben Dooks | f62e770 | 2008-02-06 01:39:41 -0800 | [diff] [blame] | 1067 | platform_driver_unregister(&s3c2412fb_driver); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1068 | } | 
|  | 1069 |  | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1070 | module_init(s3c2410fb_init); | 
|  | 1071 | module_exit(s3c2410fb_cleanup); | 
|  | 1072 |  | 
| Krzysztof Helt | b083194 | 2007-10-16 01:28:54 -0700 | [diff] [blame] | 1073 | MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, " | 
|  | 1074 | "Ben Dooks <ben-linux@fluff.org>"); | 
| Arnaud Patard | 20fd576 | 2005-09-09 13:10:07 -0700 | [diff] [blame] | 1075 | MODULE_DESCRIPTION("Framebuffer driver for the s3c2410"); | 
|  | 1076 | MODULE_LICENSE("GPL"); | 
| Ben Dooks | ee29420 | 2008-05-23 13:04:57 -0700 | [diff] [blame] | 1077 | MODULE_ALIAS("platform:s3c2410-lcd"); | 
|  | 1078 | MODULE_ALIAS("platform:s3c2412-lcd"); |