| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/drivers/video/imxfb.c | 
 | 3 |  * | 
 | 4 |  *  Freescale i.MX Frame Buffer device driver | 
 | 5 |  * | 
 | 6 |  *  Copyright (C) 2004 Sascha Hauer, Pengutronix | 
 | 7 |  *   Based on acornfb.c Copyright (C) Russell King. | 
 | 8 |  * | 
 | 9 |  * This file is subject to the terms and conditions of the GNU General Public | 
 | 10 |  * License.  See the file COPYING in the main directory of this archive for | 
 | 11 |  * more details. | 
 | 12 |  * | 
 | 13 |  * Please direct your questions and comments on this driver to the following | 
 | 14 |  * email address: | 
 | 15 |  * | 
 | 16 |  *	linux-arm-kernel@lists.arm.linux.org.uk | 
 | 17 |  */ | 
 | 18 |  | 
 | 19 | //#define DEBUG 1 | 
 | 20 |  | 
 | 21 | #include <linux/config.h> | 
 | 22 | #include <linux/module.h> | 
 | 23 | #include <linux/kernel.h> | 
 | 24 | #include <linux/sched.h> | 
 | 25 | #include <linux/errno.h> | 
 | 26 | #include <linux/string.h> | 
 | 27 | #include <linux/interrupt.h> | 
 | 28 | #include <linux/slab.h> | 
 | 29 | #include <linux/fb.h> | 
 | 30 | #include <linux/delay.h> | 
 | 31 | #include <linux/init.h> | 
 | 32 | #include <linux/ioport.h> | 
 | 33 | #include <linux/cpufreq.h> | 
| Russell King | d052d1b | 2005-10-29 19:07:23 +0100 | [diff] [blame] | 34 | #include <linux/platform_device.h> | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 35 | #include <linux/dma-mapping.h> | 
 | 36 |  | 
 | 37 | #include <asm/hardware.h> | 
 | 38 | #include <asm/io.h> | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 39 | #include <asm/uaccess.h> | 
 | 40 | #include <asm/arch/imxfb.h> | 
 | 41 |  | 
 | 42 | /* | 
 | 43 |  * Complain if VAR is out of range. | 
 | 44 |  */ | 
 | 45 | #define DEBUG_VAR 1 | 
 | 46 |  | 
 | 47 | #include "imxfb.h" | 
 | 48 |  | 
 | 49 | static struct imxfb_rgb def_rgb_16 = { | 
 | 50 | 	.red	= { .offset = 8,  .length = 4, }, | 
 | 51 | 	.green	= { .offset = 4,  .length = 4, }, | 
 | 52 | 	.blue	= { .offset = 0,  .length = 4, }, | 
 | 53 | 	.transp = { .offset = 0,  .length = 0, }, | 
 | 54 | }; | 
 | 55 |  | 
 | 56 | static struct imxfb_rgb def_rgb_8 = { | 
 | 57 | 	.red	= { .offset = 0,  .length = 8, }, | 
 | 58 | 	.green	= { .offset = 0,  .length = 8, }, | 
 | 59 | 	.blue	= { .offset = 0,  .length = 8, }, | 
 | 60 | 	.transp = { .offset = 0,  .length = 0, }, | 
 | 61 | }; | 
 | 62 |  | 
 | 63 | static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info); | 
 | 64 |  | 
 | 65 | static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) | 
 | 66 | { | 
 | 67 | 	chan &= 0xffff; | 
 | 68 | 	chan >>= 16 - bf->length; | 
 | 69 | 	return chan << bf->offset; | 
 | 70 | } | 
 | 71 |  | 
 | 72 | #define LCDC_PALETTE(x) __REG2(IMX_LCDC_BASE+0x800, (x)<<2) | 
 | 73 | static int | 
 | 74 | imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, | 
 | 75 | 		       u_int trans, struct fb_info *info) | 
 | 76 | { | 
 | 77 | 	struct imxfb_info *fbi = info->par; | 
 | 78 | 	u_int val, ret = 1; | 
 | 79 |  | 
 | 80 | #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) | 
 | 81 | 	if (regno < fbi->palette_size) { | 
 | 82 | 		val = (CNVT_TOHW(red, 4) << 8) | | 
 | 83 | 		      (CNVT_TOHW(green,4) << 4) | | 
 | 84 | 		      CNVT_TOHW(blue,  4); | 
 | 85 |  | 
 | 86 | 		LCDC_PALETTE(regno) = val; | 
 | 87 | 		ret = 0; | 
 | 88 | 	} | 
 | 89 | 	return ret; | 
 | 90 | } | 
 | 91 |  | 
 | 92 | static int | 
 | 93 | imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | 
 | 94 | 		   u_int trans, struct fb_info *info) | 
 | 95 | { | 
 | 96 | 	struct imxfb_info *fbi = info->par; | 
 | 97 | 	unsigned int val; | 
 | 98 | 	int ret = 1; | 
 | 99 |  | 
 | 100 | 	/* | 
 | 101 | 	 * If inverse mode was selected, invert all the colours | 
 | 102 | 	 * rather than the register number.  The register number | 
 | 103 | 	 * is what you poke into the framebuffer to produce the | 
 | 104 | 	 * colour you requested. | 
 | 105 | 	 */ | 
 | 106 | 	if (fbi->cmap_inverse) { | 
 | 107 | 		red   = 0xffff - red; | 
 | 108 | 		green = 0xffff - green; | 
 | 109 | 		blue  = 0xffff - blue; | 
 | 110 | 	} | 
 | 111 |  | 
 | 112 | 	/* | 
 | 113 | 	 * If greyscale is true, then we convert the RGB value | 
 | 114 | 	 * to greyscale no mater what visual we are using. | 
 | 115 | 	 */ | 
 | 116 | 	if (info->var.grayscale) | 
 | 117 | 		red = green = blue = (19595 * red + 38470 * green + | 
 | 118 | 					7471 * blue) >> 16; | 
 | 119 |  | 
 | 120 | 	switch (info->fix.visual) { | 
 | 121 | 	case FB_VISUAL_TRUECOLOR: | 
 | 122 | 		/* | 
 | 123 | 		 * 12 or 16-bit True Colour.  We encode the RGB value | 
 | 124 | 		 * according to the RGB bitfield information. | 
 | 125 | 		 */ | 
 | 126 | 		if (regno < 16) { | 
 | 127 | 			u32 *pal = info->pseudo_palette; | 
 | 128 |  | 
 | 129 | 			val  = chan_to_field(red, &info->var.red); | 
 | 130 | 			val |= chan_to_field(green, &info->var.green); | 
 | 131 | 			val |= chan_to_field(blue, &info->var.blue); | 
 | 132 |  | 
 | 133 | 			pal[regno] = val; | 
 | 134 | 			ret = 0; | 
 | 135 | 		} | 
 | 136 | 		break; | 
 | 137 |  | 
 | 138 | 	case FB_VISUAL_STATIC_PSEUDOCOLOR: | 
 | 139 | 	case FB_VISUAL_PSEUDOCOLOR: | 
 | 140 | 		ret = imxfb_setpalettereg(regno, red, green, blue, trans, info); | 
 | 141 | 		break; | 
 | 142 | 	} | 
 | 143 |  | 
 | 144 | 	return ret; | 
 | 145 | } | 
 | 146 |  | 
 | 147 | /* | 
 | 148 |  *  imxfb_check_var(): | 
 | 149 |  *    Round up in the following order: bits_per_pixel, xres, | 
 | 150 |  *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, | 
 | 151 |  *    bitfields, horizontal timing, vertical timing. | 
 | 152 |  */ | 
 | 153 | static int | 
 | 154 | imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 
 | 155 | { | 
 | 156 | 	struct imxfb_info *fbi = info->par; | 
 | 157 | 	int rgbidx; | 
 | 158 |  | 
 | 159 | 	if (var->xres < MIN_XRES) | 
 | 160 | 		var->xres = MIN_XRES; | 
 | 161 | 	if (var->yres < MIN_YRES) | 
 | 162 | 		var->yres = MIN_YRES; | 
 | 163 | 	if (var->xres > fbi->max_xres) | 
 | 164 | 		var->xres = fbi->max_xres; | 
 | 165 | 	if (var->yres > fbi->max_yres) | 
 | 166 | 		var->yres = fbi->max_yres; | 
 | 167 | 	var->xres_virtual = max(var->xres_virtual, var->xres); | 
 | 168 | 	var->yres_virtual = max(var->yres_virtual, var->yres); | 
 | 169 |  | 
 | 170 | 	pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); | 
 | 171 | 	switch (var->bits_per_pixel) { | 
 | 172 | 	case 16: | 
 | 173 | 		rgbidx = RGB_16; | 
 | 174 | 		break; | 
 | 175 | 	case 8: | 
 | 176 | 		rgbidx = RGB_8; | 
 | 177 | 		break; | 
 | 178 | 	default: | 
 | 179 | 		rgbidx = RGB_16; | 
 | 180 | 	} | 
 | 181 |  | 
 | 182 | 	/* | 
 | 183 | 	 * Copy the RGB parameters for this display | 
 | 184 | 	 * from the machine specific parameters. | 
 | 185 | 	 */ | 
 | 186 | 	var->red    = fbi->rgb[rgbidx]->red; | 
 | 187 | 	var->green  = fbi->rgb[rgbidx]->green; | 
 | 188 | 	var->blue   = fbi->rgb[rgbidx]->blue; | 
 | 189 | 	var->transp = fbi->rgb[rgbidx]->transp; | 
 | 190 |  | 
 | 191 | 	pr_debug("RGBT length = %d:%d:%d:%d\n", | 
 | 192 | 		var->red.length, var->green.length, var->blue.length, | 
 | 193 | 		var->transp.length); | 
 | 194 |  | 
 | 195 | 	pr_debug("RGBT offset = %d:%d:%d:%d\n", | 
 | 196 | 		var->red.offset, var->green.offset, var->blue.offset, | 
 | 197 | 		var->transp.offset); | 
 | 198 |  | 
 | 199 | 	return 0; | 
 | 200 | } | 
 | 201 |  | 
 | 202 | /* | 
 | 203 |  * imxfb_set_par(): | 
 | 204 |  *	Set the user defined part of the display for the specified console | 
 | 205 |  */ | 
 | 206 | static int imxfb_set_par(struct fb_info *info) | 
 | 207 | { | 
 | 208 | 	struct imxfb_info *fbi = info->par; | 
 | 209 | 	struct fb_var_screeninfo *var = &info->var; | 
 | 210 |  | 
 | 211 | 	pr_debug("set_par\n"); | 
 | 212 |  | 
 | 213 | 	if (var->bits_per_pixel == 16) | 
 | 214 | 		info->fix.visual = FB_VISUAL_TRUECOLOR; | 
 | 215 | 	else if (!fbi->cmap_static) | 
 | 216 | 		info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | 
 | 217 | 	else { | 
 | 218 | 		/* | 
 | 219 | 		 * Some people have weird ideas about wanting static | 
 | 220 | 		 * pseudocolor maps.  I suspect their user space | 
 | 221 | 		 * applications are broken. | 
 | 222 | 		 */ | 
 | 223 | 		info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; | 
 | 224 | 	} | 
 | 225 |  | 
 | 226 | 	info->fix.line_length = var->xres_virtual * | 
 | 227 | 				  var->bits_per_pixel / 8; | 
 | 228 | 	fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; | 
 | 229 |  | 
 | 230 | 	imxfb_activate_var(var, info); | 
 | 231 |  | 
 | 232 | 	return 0; | 
 | 233 | } | 
 | 234 |  | 
 | 235 | static void imxfb_enable_controller(struct imxfb_info *fbi) | 
 | 236 | { | 
 | 237 | 	pr_debug("Enabling LCD controller\n"); | 
 | 238 |  | 
 | 239 | 	/* initialize LCDC */ | 
 | 240 | 	LCDC_RMCR &= ~RMCR_LCDC_EN;		/* just to be safe... */ | 
 | 241 |  | 
 | 242 | 	LCDC_SSA	= fbi->screen_dma; | 
 | 243 | 	/* physical screen start address	    */ | 
 | 244 | 	LCDC_VPW	= VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4); | 
 | 245 |  | 
 | 246 | 	LCDC_POS	= 0x00000000;   /* panning offset 0 (0 pixel offset)        */ | 
 | 247 |  | 
 | 248 | 	/* disable hardware cursor */ | 
 | 249 | 	LCDC_CPOS	&= ~(CPOS_CC0 | CPOS_CC1); | 
 | 250 |  | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 251 | 	LCDC_RMCR = RMCR_LCDC_EN; | 
 | 252 |  | 
 | 253 | 	if(fbi->backlight_power) | 
 | 254 | 		fbi->backlight_power(1); | 
 | 255 | 	if(fbi->lcd_power) | 
 | 256 | 		fbi->lcd_power(1); | 
 | 257 | } | 
 | 258 |  | 
 | 259 | static void imxfb_disable_controller(struct imxfb_info *fbi) | 
 | 260 | { | 
 | 261 | 	pr_debug("Disabling LCD controller\n"); | 
 | 262 |  | 
 | 263 | 	if(fbi->backlight_power) | 
 | 264 | 		fbi->backlight_power(0); | 
 | 265 | 	if(fbi->lcd_power) | 
 | 266 | 		fbi->lcd_power(0); | 
 | 267 |  | 
 | 268 | 	LCDC_RMCR = 0; | 
 | 269 | } | 
 | 270 |  | 
 | 271 | static int imxfb_blank(int blank, struct fb_info *info) | 
 | 272 | { | 
 | 273 | 	struct imxfb_info *fbi = info->par; | 
 | 274 |  | 
 | 275 | 	pr_debug("imxfb_blank: blank=%d\n", blank); | 
 | 276 |  | 
 | 277 | 	switch (blank) { | 
 | 278 | 	case FB_BLANK_POWERDOWN: | 
 | 279 | 	case FB_BLANK_VSYNC_SUSPEND: | 
 | 280 | 	case FB_BLANK_HSYNC_SUSPEND: | 
 | 281 | 	case FB_BLANK_NORMAL: | 
 | 282 | 		imxfb_disable_controller(fbi); | 
 | 283 | 		break; | 
 | 284 |  | 
 | 285 | 	case FB_BLANK_UNBLANK: | 
 | 286 | 		imxfb_enable_controller(fbi); | 
 | 287 | 		break; | 
 | 288 | 	} | 
 | 289 | 	return 0; | 
 | 290 | } | 
 | 291 |  | 
 | 292 | static struct fb_ops imxfb_ops = { | 
 | 293 | 	.owner		= THIS_MODULE, | 
 | 294 | 	.fb_check_var	= imxfb_check_var, | 
 | 295 | 	.fb_set_par	= imxfb_set_par, | 
 | 296 | 	.fb_setcolreg	= imxfb_setcolreg, | 
 | 297 | 	.fb_fillrect	= cfb_fillrect, | 
 | 298 | 	.fb_copyarea	= cfb_copyarea, | 
 | 299 | 	.fb_imageblit	= cfb_imageblit, | 
 | 300 | 	.fb_blank	= imxfb_blank, | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 301 | }; | 
 | 302 |  | 
 | 303 | /* | 
 | 304 |  * imxfb_activate_var(): | 
 | 305 |  *	Configures LCD Controller based on entries in var parameter.  Settings are | 
 | 306 |  *	only written to the controller if changes were made. | 
 | 307 |  */ | 
 | 308 | static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) | 
 | 309 | { | 
 | 310 | 	struct imxfb_info *fbi = info->par; | 
 | 311 | 	pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", | 
 | 312 | 		var->xres, var->hsync_len, | 
 | 313 | 		var->left_margin, var->right_margin); | 
 | 314 | 	pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n", | 
 | 315 | 		var->yres, var->vsync_len, | 
 | 316 | 		var->upper_margin, var->lower_margin); | 
 | 317 |  | 
 | 318 | #if DEBUG_VAR | 
 | 319 | 	if (var->xres < 16        || var->xres > 1024) | 
 | 320 | 		printk(KERN_ERR "%s: invalid xres %d\n", | 
 | 321 | 			info->fix.id, var->xres); | 
 | 322 | 	if (var->hsync_len < 1    || var->hsync_len > 64) | 
 | 323 | 		printk(KERN_ERR "%s: invalid hsync_len %d\n", | 
 | 324 | 			info->fix.id, var->hsync_len); | 
 | 325 | 	if (var->left_margin > 255) | 
 | 326 | 		printk(KERN_ERR "%s: invalid left_margin %d\n", | 
 | 327 | 			info->fix.id, var->left_margin); | 
 | 328 | 	if (var->right_margin > 255) | 
 | 329 | 		printk(KERN_ERR "%s: invalid right_margin %d\n", | 
 | 330 | 			info->fix.id, var->right_margin); | 
 | 331 | 	if (var->yres < 1 || var->yres > 511) | 
 | 332 | 		printk(KERN_ERR "%s: invalid yres %d\n", | 
 | 333 | 			info->fix.id, var->yres); | 
 | 334 | 	if (var->vsync_len > 100) | 
 | 335 | 		printk(KERN_ERR "%s: invalid vsync_len %d\n", | 
 | 336 | 			info->fix.id, var->vsync_len); | 
 | 337 | 	if (var->upper_margin > 63) | 
 | 338 | 		printk(KERN_ERR "%s: invalid upper_margin %d\n", | 
 | 339 | 			info->fix.id, var->upper_margin); | 
 | 340 | 	if (var->lower_margin > 255) | 
 | 341 | 		printk(KERN_ERR "%s: invalid lower_margin %d\n", | 
 | 342 | 			info->fix.id, var->lower_margin); | 
 | 343 | #endif | 
 | 344 |  | 
 | 345 | 	LCDC_HCR	= HCR_H_WIDTH(var->hsync_len) | | 
 | 346 | 	                  HCR_H_WAIT_1(var->left_margin) | | 
 | 347 | 			  HCR_H_WAIT_2(var->right_margin); | 
 | 348 |  | 
 | 349 | 	LCDC_VCR	= VCR_V_WIDTH(var->vsync_len) | | 
 | 350 | 	                  VCR_V_WAIT_1(var->upper_margin) | | 
 | 351 | 			  VCR_V_WAIT_2(var->lower_margin); | 
 | 352 |  | 
 | 353 | 	LCDC_SIZE	= SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres); | 
 | 354 | 	LCDC_PCR	= fbi->pcr; | 
 | 355 | 	LCDC_PWMR	= fbi->pwmr; | 
 | 356 | 	LCDC_LSCR1	= fbi->lscr1; | 
| Sascha Hauer | 772a9e6 | 2005-07-17 20:15:36 +0100 | [diff] [blame] | 357 | 	LCDC_DMACR	= fbi->dmacr; | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 358 |  | 
 | 359 | 	return 0; | 
 | 360 | } | 
 | 361 |  | 
 | 362 | static void imxfb_setup_gpio(struct imxfb_info *fbi) | 
 | 363 | { | 
 | 364 | 	int width; | 
 | 365 |  | 
 | 366 | 	LCDC_RMCR	&= ~(RMCR_LCDC_EN | RMCR_SELF_REF); | 
 | 367 |  | 
 | 368 | 	if( fbi->pcr & PCR_TFT ) | 
 | 369 | 		width = 16; | 
 | 370 | 	else | 
 | 371 | 		width = 1 << ((fbi->pcr >> 28) & 0x3); | 
 | 372 |  | 
 | 373 | 	switch(width) { | 
 | 374 | 	case 16: | 
 | 375 | 		imx_gpio_mode(PD30_PF_LD15); | 
 | 376 | 		imx_gpio_mode(PD29_PF_LD14); | 
 | 377 | 		imx_gpio_mode(PD28_PF_LD13); | 
 | 378 | 		imx_gpio_mode(PD27_PF_LD12); | 
 | 379 | 		imx_gpio_mode(PD26_PF_LD11); | 
 | 380 | 		imx_gpio_mode(PD25_PF_LD10); | 
 | 381 | 		imx_gpio_mode(PD24_PF_LD9); | 
 | 382 | 		imx_gpio_mode(PD23_PF_LD8); | 
 | 383 | 	case 8: | 
 | 384 | 		imx_gpio_mode(PD22_PF_LD7); | 
 | 385 | 		imx_gpio_mode(PD21_PF_LD6); | 
 | 386 | 		imx_gpio_mode(PD20_PF_LD5); | 
 | 387 | 		imx_gpio_mode(PD19_PF_LD4); | 
 | 388 | 	case 4: | 
 | 389 | 		imx_gpio_mode(PD18_PF_LD3); | 
 | 390 | 		imx_gpio_mode(PD17_PF_LD2); | 
 | 391 | 	case 2: | 
 | 392 | 		imx_gpio_mode(PD16_PF_LD1); | 
 | 393 | 	case 1: | 
 | 394 | 		imx_gpio_mode(PD15_PF_LD0); | 
 | 395 | 	} | 
 | 396 |  | 
 | 397 | 	/* initialize GPIOs */ | 
 | 398 | 	imx_gpio_mode(PD6_PF_LSCLK); | 
 | 399 | 	imx_gpio_mode(PD10_PF_SPL_SPR); | 
 | 400 | 	imx_gpio_mode(PD11_PF_CONTRAST); | 
 | 401 | 	imx_gpio_mode(PD14_PF_FLM_VSYNC); | 
 | 402 | 	imx_gpio_mode(PD13_PF_LP_HSYNC); | 
 | 403 | 	imx_gpio_mode(PD7_PF_REV); | 
 | 404 | 	imx_gpio_mode(PD8_PF_CLS); | 
 | 405 |  | 
 | 406 | #ifndef CONFIG_MACH_PIMX1 | 
 | 407 | 	/* on PiMX1 used as buffers enable signal | 
 | 408 | 	 */ | 
 | 409 | 	imx_gpio_mode(PD9_PF_PS); | 
 | 410 | #endif | 
 | 411 |  | 
 | 412 | #ifndef CONFIG_MACH_MX1FS2 | 
 | 413 | 	/* on mx1fs2 this pin is used to (de)activate the display, so we need | 
 | 414 | 	 * it as a normal gpio | 
 | 415 | 	 */ | 
 | 416 | 	imx_gpio_mode(PD12_PF_ACD_OE); | 
 | 417 | #endif | 
 | 418 |  | 
 | 419 | } | 
 | 420 |  | 
 | 421 | #ifdef CONFIG_PM | 
 | 422 | /* | 
 | 423 |  * Power management hooks.  Note that we won't be called from IRQ context, | 
 | 424 |  * unlike the blank functions above, so we may sleep. | 
 | 425 |  */ | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 426 | static int imxfb_suspend(struct platform_device *dev, pm_message_t state) | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 427 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 428 | 	struct imxfb_info *fbi = platform_get_drvdata(dev); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 429 | 	pr_debug("%s\n",__FUNCTION__); | 
 | 430 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 431 | 	imxfb_disable_controller(fbi); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 432 | 	return 0; | 
 | 433 | } | 
 | 434 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 435 | static int imxfb_resume(struct platform_device *dev) | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 436 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 437 | 	struct imxfb_info *fbi = platform_get_drvdata(dev); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 438 | 	pr_debug("%s\n",__FUNCTION__); | 
 | 439 |  | 
| Russell King | 9480e30 | 2005-10-28 09:52:56 -0700 | [diff] [blame] | 440 | 	imxfb_enable_controller(fbi); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 441 | 	return 0; | 
 | 442 | } | 
 | 443 | #else | 
 | 444 | #define imxfb_suspend	NULL | 
 | 445 | #define imxfb_resume	NULL | 
 | 446 | #endif | 
 | 447 |  | 
 | 448 | static int __init imxfb_init_fbinfo(struct device *dev) | 
 | 449 | { | 
 | 450 | 	struct imxfb_mach_info *inf = dev->platform_data; | 
 | 451 | 	struct fb_info *info = dev_get_drvdata(dev); | 
 | 452 | 	struct imxfb_info *fbi = info->par; | 
 | 453 |  | 
 | 454 | 	pr_debug("%s\n",__FUNCTION__); | 
 | 455 |  | 
 | 456 | 	info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL); | 
 | 457 | 	if (!info->pseudo_palette) | 
 | 458 | 		return -ENOMEM; | 
 | 459 |  | 
 | 460 | 	memset(fbi, 0, sizeof(struct imxfb_info)); | 
 | 461 | 	fbi->dev = dev; | 
 | 462 |  | 
 | 463 | 	strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id)); | 
 | 464 |  | 
 | 465 | 	info->fix.type	= FB_TYPE_PACKED_PIXELS; | 
 | 466 | 	info->fix.type_aux		= 0; | 
 | 467 | 	info->fix.xpanstep		= 0; | 
 | 468 | 	info->fix.ypanstep		= 0; | 
 | 469 | 	info->fix.ywrapstep		= 0; | 
 | 470 | 	info->fix.accel	= FB_ACCEL_NONE; | 
 | 471 |  | 
 | 472 | 	info->var.nonstd		= 0; | 
 | 473 | 	info->var.activate		= FB_ACTIVATE_NOW; | 
 | 474 | 	info->var.height		= -1; | 
 | 475 | 	info->var.width	= -1; | 
 | 476 | 	info->var.accel_flags		= 0; | 
 | 477 | 	info->var.vmode	= FB_VMODE_NONINTERLACED; | 
 | 478 |  | 
 | 479 | 	info->fbops			= &imxfb_ops; | 
 | 480 | 	info->flags			= FBINFO_FLAG_DEFAULT; | 
 | 481 | 	info->pseudo_palette		= (fbi + 1); | 
 | 482 |  | 
 | 483 | 	fbi->rgb[RGB_16]		= &def_rgb_16; | 
 | 484 | 	fbi->rgb[RGB_8]			= &def_rgb_8; | 
 | 485 |  | 
 | 486 | 	fbi->max_xres			= inf->xres; | 
 | 487 | 	info->var.xres			= inf->xres; | 
 | 488 | 	info->var.xres_virtual		= inf->xres; | 
 | 489 | 	fbi->max_yres			= inf->yres; | 
 | 490 | 	info->var.yres			= inf->yres; | 
 | 491 | 	info->var.yres_virtual		= inf->yres; | 
 | 492 | 	fbi->max_bpp			= inf->bpp; | 
 | 493 | 	info->var.bits_per_pixel	= inf->bpp; | 
 | 494 | 	info->var.pixclock		= inf->pixclock; | 
 | 495 | 	info->var.hsync_len		= inf->hsync_len; | 
 | 496 | 	info->var.left_margin		= inf->left_margin; | 
 | 497 | 	info->var.right_margin		= inf->right_margin; | 
 | 498 | 	info->var.vsync_len		= inf->vsync_len; | 
 | 499 | 	info->var.upper_margin		= inf->upper_margin; | 
 | 500 | 	info->var.lower_margin		= inf->lower_margin; | 
 | 501 | 	info->var.sync			= inf->sync; | 
 | 502 | 	info->var.grayscale		= inf->cmap_greyscale; | 
 | 503 | 	fbi->cmap_inverse		= inf->cmap_inverse; | 
 | 504 | 	fbi->pcr			= inf->pcr; | 
 | 505 | 	fbi->lscr1			= inf->lscr1; | 
| Sascha Hauer | 772a9e6 | 2005-07-17 20:15:36 +0100 | [diff] [blame] | 506 | 	fbi->dmacr			= inf->dmacr; | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 507 | 	fbi->pwmr			= inf->pwmr; | 
 | 508 | 	fbi->lcd_power			= inf->lcd_power; | 
 | 509 | 	fbi->backlight_power		= inf->backlight_power; | 
 | 510 | 	info->fix.smem_len		= fbi->max_xres * fbi->max_yres * | 
 | 511 | 					  fbi->max_bpp / 8; | 
 | 512 |  | 
 | 513 | 	return 0; | 
 | 514 | } | 
 | 515 |  | 
 | 516 | /* | 
 | 517 |  *      Allocates the DRAM memory for the frame buffer.  This buffer is | 
 | 518 |  *	remapped into a non-cached, non-buffered, memory region to | 
 | 519 |  *      allow pixel writes to occur without flushing the cache. | 
 | 520 |  *      Once this area is remapped, all virtual memory access to the | 
 | 521 |  *      video memory should occur at the new region. | 
 | 522 |  */ | 
 | 523 | static int __init imxfb_map_video_memory(struct fb_info *info) | 
 | 524 | { | 
 | 525 | 	struct imxfb_info *fbi = info->par; | 
 | 526 |  | 
 | 527 | 	fbi->map_size = PAGE_ALIGN(info->fix.smem_len); | 
 | 528 | 	fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, | 
 | 529 | 					&fbi->map_dma,GFP_KERNEL); | 
 | 530 |  | 
 | 531 | 	if (fbi->map_cpu) { | 
 | 532 | 		info->screen_base = fbi->map_cpu; | 
 | 533 | 		fbi->screen_cpu = fbi->map_cpu; | 
 | 534 | 		fbi->screen_dma = fbi->map_dma; | 
 | 535 | 		info->fix.smem_start = fbi->screen_dma; | 
 | 536 | 	} | 
 | 537 |  | 
 | 538 | 	return fbi->map_cpu ? 0 : -ENOMEM; | 
 | 539 | } | 
 | 540 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 541 | static int __init imxfb_probe(struct platform_device *pdev) | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 542 | { | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 543 | 	struct imxfb_info *fbi; | 
 | 544 | 	struct fb_info *info; | 
 | 545 | 	struct imxfb_mach_info *inf; | 
 | 546 | 	struct resource *res; | 
 | 547 | 	int ret; | 
 | 548 |  | 
 | 549 | 	printk("i.MX Framebuffer driver\n"); | 
 | 550 |  | 
 | 551 | 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 552 | 	if(!res) | 
 | 553 | 		return -ENODEV; | 
 | 554 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 555 | 	inf = pdev->dev.platform_data; | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 556 | 	if(!inf) { | 
| Pavel Pisa | f99c892 | 2006-01-07 10:44:32 +0000 | [diff] [blame] | 557 | 		dev_err(&pdev->dev,"No platform_data available\n"); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 558 | 		return -ENOMEM; | 
 | 559 | 	} | 
 | 560 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 561 | 	info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 562 | 	if(!info) | 
 | 563 | 		return -ENOMEM; | 
 | 564 |  | 
 | 565 | 	fbi = info->par; | 
 | 566 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 567 | 	platform_set_drvdata(pdev, info); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 568 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 569 | 	ret = imxfb_init_fbinfo(&pdev->dev); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 570 | 	if( ret < 0 ) | 
 | 571 | 		goto failed_init; | 
 | 572 |  | 
 | 573 | 	res = request_mem_region(res->start, res->end - res->start + 1, "IMXFB"); | 
 | 574 | 	if (!res) { | 
 | 575 | 		ret = -EBUSY; | 
 | 576 | 		goto failed_regs; | 
 | 577 | 	} | 
 | 578 |  | 
 | 579 | 	if (!inf->fixed_screen_cpu) { | 
 | 580 | 		ret = imxfb_map_video_memory(info); | 
 | 581 | 		if (ret) { | 
| Pavel Pisa | f99c892 | 2006-01-07 10:44:32 +0000 | [diff] [blame] | 582 | 			dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 583 | 			ret = -ENOMEM; | 
 | 584 | 			goto failed_map; | 
 | 585 | 		} | 
 | 586 | 	} else { | 
 | 587 | 		/* Fixed framebuffer mapping enables location of the screen in eSRAM */ | 
 | 588 | 		fbi->map_cpu = inf->fixed_screen_cpu; | 
 | 589 | 		fbi->map_dma = inf->fixed_screen_dma; | 
 | 590 | 		info->screen_base = fbi->map_cpu; | 
 | 591 | 		fbi->screen_cpu = fbi->map_cpu; | 
 | 592 | 		fbi->screen_dma = fbi->map_dma; | 
 | 593 | 		info->fix.smem_start = fbi->screen_dma; | 
 | 594 | 	} | 
 | 595 |  | 
 | 596 | 	/* | 
 | 597 | 	 * This makes sure that our colour bitfield | 
 | 598 | 	 * descriptors are correctly initialised. | 
 | 599 | 	 */ | 
 | 600 | 	imxfb_check_var(&info->var, info); | 
 | 601 |  | 
 | 602 | 	ret = fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0); | 
 | 603 | 	if (ret < 0) | 
 | 604 | 		goto failed_cmap; | 
 | 605 |  | 
 | 606 | 	imxfb_setup_gpio(fbi); | 
 | 607 |  | 
 | 608 | 	imxfb_set_par(info); | 
 | 609 | 	ret = register_framebuffer(info); | 
 | 610 | 	if (ret < 0) { | 
| Pavel Pisa | f99c892 | 2006-01-07 10:44:32 +0000 | [diff] [blame] | 611 | 		dev_err(&pdev->dev, "failed to register framebuffer\n"); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 612 | 		goto failed_register; | 
 | 613 | 	} | 
 | 614 |  | 
 | 615 | 	imxfb_enable_controller(fbi); | 
 | 616 |  | 
 | 617 | 	return 0; | 
 | 618 |  | 
 | 619 | failed_register: | 
 | 620 | 	fb_dealloc_cmap(&info->cmap); | 
 | 621 | failed_cmap: | 
 | 622 | 	if (!inf->fixed_screen_cpu) | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 623 | 		dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu, | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 624 | 		           fbi->map_dma); | 
 | 625 | failed_map: | 
 | 626 | 	kfree(info->pseudo_palette); | 
 | 627 | failed_regs: | 
 | 628 | 	release_mem_region(res->start, res->end - res->start); | 
 | 629 | failed_init: | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 630 | 	platform_set_drvdata(pdev, NULL); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 631 | 	framebuffer_release(info); | 
 | 632 | 	return ret; | 
 | 633 | } | 
 | 634 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 635 | static int imxfb_remove(struct platform_device *pdev) | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 636 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 637 | 	struct fb_info *info = platform_get_drvdata(pdev); | 
| Sascha Hauer | 772a9e6 | 2005-07-17 20:15:36 +0100 | [diff] [blame] | 638 | 	struct imxfb_info *fbi = info->par; | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 639 | 	struct resource *res; | 
 | 640 |  | 
 | 641 | 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 642 |  | 
| Sascha Hauer | 772a9e6 | 2005-07-17 20:15:36 +0100 | [diff] [blame] | 643 | 	imxfb_disable_controller(fbi); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 644 |  | 
 | 645 | 	unregister_framebuffer(info); | 
 | 646 |  | 
 | 647 | 	fb_dealloc_cmap(&info->cmap); | 
 | 648 | 	kfree(info->pseudo_palette); | 
 | 649 | 	framebuffer_release(info); | 
 | 650 |  | 
 | 651 | 	release_mem_region(res->start, res->end - res->start + 1); | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 652 | 	platform_set_drvdata(pdev, NULL); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 653 |  | 
 | 654 | 	return 0; | 
 | 655 | } | 
 | 656 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 657 | void  imxfb_shutdown(struct platform_device * dev) | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 658 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 659 | 	struct fb_info *info = platform_get_drvdata(dev); | 
| Sascha Hauer | 772a9e6 | 2005-07-17 20:15:36 +0100 | [diff] [blame] | 660 | 	struct imxfb_info *fbi = info->par; | 
 | 661 | 	imxfb_disable_controller(fbi); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 662 | } | 
 | 663 |  | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 664 | static struct platform_driver imxfb_driver = { | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 665 | 	.probe		= imxfb_probe, | 
 | 666 | 	.suspend	= imxfb_suspend, | 
 | 667 | 	.resume		= imxfb_resume, | 
 | 668 | 	.remove		= imxfb_remove, | 
 | 669 | 	.shutdown	= imxfb_shutdown, | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 670 | 	.driver		= { | 
 | 671 | 		.name	= "imx-fb", | 
 | 672 | 	}, | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 673 | }; | 
 | 674 |  | 
 | 675 | int __init imxfb_init(void) | 
 | 676 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 677 | 	return platform_driver_register(&imxfb_driver); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 678 | } | 
 | 679 |  | 
 | 680 | static void __exit imxfb_cleanup(void) | 
 | 681 | { | 
| Russell King | 3ae5eae | 2005-11-09 22:32:44 +0000 | [diff] [blame] | 682 | 	platform_driver_unregister(&imxfb_driver); | 
| Sascha Hauer | 7c2f891c | 2005-05-01 08:59:24 -0700 | [diff] [blame] | 683 | } | 
 | 684 |  | 
 | 685 | module_init(imxfb_init); | 
 | 686 | module_exit(imxfb_cleanup); | 
 | 687 |  | 
 | 688 | MODULE_DESCRIPTION("Motorola i.MX framebuffer driver"); | 
 | 689 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | 
 | 690 | MODULE_LICENSE("GPL"); |