1226 lines · 34.5 KB · C
RawDownload
/* *  linux/drivers/video/sa1100fb.c * *  Copyright (C) 1999 Eric A. Thomas *   Based on acornfb.c Copyright (C) Russell King. * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file COPYING in the main directory of this archive for * more details. * *	        StrongARM 1100 LCD Controller Frame Buffer Driver * * Please direct your questions and comments on this driver to the following * email address: * *	linux-arm-kernel@lists.arm.linux.org.uk * * Clean patches should be sent to the ARM Linux Patch System.  Please see the * following web page for more information: * *	https://www.arm.linux.org.uk/developer/patches/info.shtml * * Thank you. * * Known problems: *	- With the Neponset plugged into an Assabet, LCD powerdown *	  doesn't work (LCD stays powered up).  Therefore we shouldn't *	  blank the screen. *	- We don't limit the CPU clock rate nor the mode selection *	  according to the available SDRAM bandwidth. * * Other notes: *	- Linear grayscale palettes and the kernel. *	  Such code does not belong in the kernel.  The kernel frame buffer *	  drivers do not expect a linear colourmap, but a colourmap based on *	  the VT100 standard mapping. * *	  If your _userspace_ requires a linear colourmap, then the setup of *	  such a colourmap belongs _in userspace_, not in the kernel.  Code *	  to set the colourmap correctly from user space has been sent to *	  David Neuer.  It's around 8 lines of C code, plus another 4 to *	  detect if we are using grayscale. * *	- The following must never be specified in a panel definition: *	     LCCR0_LtlEnd, LCCR3_PixClkDiv, LCCR3_VrtSnchL, LCCR3_HorSnchL * *	- The following should be specified: *	     either LCCR0_Color or LCCR0_Mono *	     either LCCR0_Sngl or LCCR0_Dual *	     either LCCR0_Act or LCCR0_Pas *	     either LCCR3_OutEnH or LCCD3_OutEnL *	     either LCCR3_PixRsEdg or LCCR3_PixFlEdg *	     either LCCR3_ACBsDiv or LCCR3_ACBsCntOff * * Code Status: * 1999/04/01: *	- Driver appears to be working for Brutus 320x200x8bpp mode.  Other *	  resolutions are working, but only the 8bpp mode is supported. *	  Changes need to be made to the palette encode and decode routines *	  to support 4 and 16 bpp modes. *	  Driver is not designed to be a module.  The FrameBuffer is statically *	  allocated since dynamic allocation of a 300k buffer cannot be *	  guaranteed. * * 1999/06/17: *	- FrameBuffer memory is now allocated at run-time when the *	  driver is initialized. * * 2000/04/10: Nicolas Pitre <nico@fluxnic.net> *	- Big cleanup for dynamic selection of machine type at run time. * * 2000/07/19: Jamey Hicks <jamey@crl.dec.com> *	- Support for Bitsy aka Compaq iPAQ H3600 added. * * 2000/08/07: Tak-Shing Chan <tchan.rd@idthk.com> *	       Jeff Sutherland <jsutherland@accelent.com> *	- Resolved an issue caused by a change made to the Assabet's PLD *	  earlier this year which broke the framebuffer driver for newer *	  Phase 4 Assabets.  Some other parameters were changed to optimize *	  for the Sharp display. * * 2000/08/09: Kunihiko IMAI <imai@vasara.co.jp> *	- XP860 support added * * 2000/08/19: Mark Huang <mhuang@livetoy.com> *	- Allows standard options to be passed on the kernel command line *	  for most common passive displays. * * 2000/08/29: *	- s/save_flags_cli/local_irq_save/ *	- remove unneeded extra save_flags_cli in sa1100fb_enable_lcd_controller * * 2000/10/10: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> *	- Updated LART stuff. Fixed some minor bugs. * * 2000/10/30: Murphy Chen <murphy@mail.dialogue.com.tw> *	- Pangolin support added * * 2000/10/31: Roman Jordan <jor@hoeft-wessel.de> *	- Huw Webpanel support added * * 2000/11/23: Eric Peng <ericpeng@coventive.com> *	- Freebird add * * 2001/02/07: Jamey Hicks <jamey.hicks@compaq.com> *	       Cliff Brake <cbrake@accelent.com> *	- Added PM callback * * 2001/05/26: <rmk@arm.linux.org.uk> *	- Fix 16bpp so that (a) we use the right colours rather than some *	  totally random colour depending on what was in page 0, and (b) *	  we don't de-reference a NULL pointer. *	- remove duplicated implementation of consistent_alloc() *	- convert dma address types to dma_addr_t *	- remove unused 'montype' stuff *	- remove redundant zero inits of init_var after the initial *	  memset. *	- remove allow_modeset (acornfb idea does not belong here) * * 2001/05/28: <rmk@arm.linux.org.uk> *	- massive cleanup - move machine dependent data into structures *	- I've left various #warnings in - if you see one, and know *	  the hardware concerned, please get in contact with me. * * 2001/05/31: <rmk@arm.linux.org.uk> *	- Fix LCCR1 HSW value, fix all machine type specifications to *	  keep values in line.  (Please check your machine type specs) * * 2001/06/10: <rmk@arm.linux.org.uk> *	- Fiddle with the LCD controller from task context only; mainly *	  so that we can run with interrupts on, and sleep. *	- Convert #warnings into #errors.  No pain, no gain. ;) * * 2001/06/14: <rmk@arm.linux.org.uk> *	- Make the palette BPS value for 12bpp come out correctly. *	- Take notice of "greyscale" on any colour depth. *	- Make truecolor visuals use the RGB channel encoding information. * * 2001/07/02: <rmk@arm.linux.org.uk> *	- Fix colourmap problems. * * 2001/07/13: <abraham@2d3d.co.za> *	- Added support for the ICP LCD-Kit01 on LART. This LCD is *	  manufactured by Prime View, model no V16C6448AB * * 2001/07/23: <rmk@arm.linux.org.uk> *	- Hand merge version from handhelds.org CVS tree.  See patch *	  notes for 595/1 for more information. *	- Drop 12bpp (it's 16bpp with different colour register mappings). *	- This hardware can not do direct colour.  Therefore we don't *	  support it. * * 2001/07/27: <rmk@arm.linux.org.uk> *	- Halve YRES on dual scan LCDs. * * 2001/08/22: <rmk@arm.linux.org.uk> *	- Add b/w iPAQ pixclock value. * * 2001/10/12: <rmk@arm.linux.org.uk> *	- Add patch 681/1 and clean up stork definitions. */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/cpufreq.h>#include <linux/gpio/consumer.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/mutex.h>#include <linux/io.h>#include <linux/clk.h> #include <video/sa1100fb.h> #include <mach/hardware.h>#include <asm/mach-types.h> /* * Complain if VAR is out of range. */#define DEBUG_VAR 1 #include "sa1100fb.h" static const struct sa1100fb_rgb rgb_4 = {	.red	= { .offset = 0,  .length = 4, },	.green	= { .offset = 0,  .length = 4, },	.blue	= { .offset = 0,  .length = 4, },	.transp	= { .offset = 0,  .length = 0, },}; static const struct sa1100fb_rgb rgb_8 = {	.red	= { .offset = 0,  .length = 8, },	.green	= { .offset = 0,  .length = 8, },	.blue	= { .offset = 0,  .length = 8, },	.transp	= { .offset = 0,  .length = 0, },}; static const struct sa1100fb_rgb def_rgb_16 = {	.red	= { .offset = 11, .length = 5, },	.green	= { .offset = 5,  .length = 6, },	.blue	= { .offset = 0,  .length = 5, },	.transp	= { .offset = 0,  .length = 0, },};   static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *);static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state); static inline void sa1100fb_schedule_work(struct sa1100fb_info *fbi, u_int state){	unsigned long flags; 	local_irq_save(flags);	/*	 * We need to handle two requests being made at the same time.	 * There are two important cases:	 *  1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)	 *     We must perform the unblanking, which will do our REENABLE for us.	 *  2. When we are blanking, but immediately unblank before we have	 *     blanked.  We do the "REENABLE" thing here as well, just to be sure.	 */	if (fbi->task_state == C_ENABLE && state == C_REENABLE)		state = (u_int) -1;	if (fbi->task_state == C_DISABLE && state == C_ENABLE)		state = C_REENABLE; 	if (state != (u_int)-1) {		fbi->task_state = state;		schedule_work(&fbi->task);	}	local_irq_restore(flags);} static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf){	chan &= 0xffff;	chan >>= 16 - bf->length;	return chan << bf->offset;} /* * Convert bits-per-pixel to a hardware palette PBS value. */static inline u_int palette_pbs(struct fb_var_screeninfo *var){	int ret = 0;	switch (var->bits_per_pixel) {	case 4:  ret = 0 << 12;	break;	case 8:  ret = 1 << 12; break;	case 16: ret = 2 << 12; break;	}	return ret;} static intsa1100fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,		       u_int trans, struct fb_info *info){	struct sa1100fb_info *fbi =		container_of(info, struct sa1100fb_info, fb);	u_int val, ret = 1; 	if (regno < fbi->palette_size) {		val = ((red >> 4) & 0xf00);		val |= ((green >> 8) & 0x0f0);		val |= ((blue >> 12) & 0x00f); 		if (regno == 0)			val |= palette_pbs(&fbi->fb.var); 		fbi->palette_cpu[regno] = val;		ret = 0;	}	return ret;} static intsa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,		   u_int trans, struct fb_info *info){	struct sa1100fb_info *fbi =		container_of(info, struct sa1100fb_info, fb);	unsigned int val;	int ret = 1; 	/*	 * If inverse mode was selected, invert all the colours	 * rather than the register number.  The register number	 * is what you poke into the framebuffer to produce the	 * colour you requested.	 */	if (fbi->inf->cmap_inverse) {		red   = 0xffff - red;		green = 0xffff - green;		blue  = 0xffff - blue;	} 	/*	 * If greyscale is true, then we convert the RGB value	 * to greyscale no mater what visual we are using.	 */	if (fbi->fb.var.grayscale)		red = green = blue = (19595 * red + 38470 * green +					7471 * blue) >> 16; 	switch (fbi->fb.fix.visual) {	case FB_VISUAL_TRUECOLOR:		/*		 * 12 or 16-bit True Colour.  We encode the RGB value		 * according to the RGB bitfield information.		 */		if (regno < 16) {			val  = chan_to_field(red, &fbi->fb.var.red);			val |= chan_to_field(green, &fbi->fb.var.green);			val |= chan_to_field(blue, &fbi->fb.var.blue); 			fbi->pseudo_palette[regno] = val;			ret = 0;		}		break; 	case FB_VISUAL_STATIC_PSEUDOCOLOR:	case FB_VISUAL_PSEUDOCOLOR:		ret = sa1100fb_setpalettereg(regno, red, green, blue, trans, info);		break;	} 	return ret;} #ifdef CONFIG_CPU_FREQ/* *  sa1100fb_display_dma_period() *    Calculate the minimum period (in picoseconds) between two DMA *    requests for the LCD controller.  If we hit this, it means we're *    doing nothing but LCD DMA. */static inline unsigned int sa1100fb_display_dma_period(struct fb_var_screeninfo *var){	/*	 * Period = pixclock * bits_per_byte * bytes_per_transfer	 *		/ memory_bits_per_pixel;	 */	return var->pixclock * 8 * 16 / var->bits_per_pixel;}#endif /* *  sa1100fb_check_var(): *    Round up in the following order: bits_per_pixel, xres, *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, *    bitfields, horizontal timing, vertical timing. */static intsa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){	struct sa1100fb_info *fbi =		container_of(info, struct sa1100fb_info, fb);	int rgbidx; 	if (var->xres < MIN_XRES)		var->xres = MIN_XRES;	if (var->yres < MIN_YRES)		var->yres = MIN_YRES;	if (var->xres > fbi->inf->xres)		var->xres = fbi->inf->xres;	if (var->yres > fbi->inf->yres)		var->yres = fbi->inf->yres;	var->xres_virtual = max(var->xres_virtual, var->xres);	var->yres_virtual = max(var->yres_virtual, var->yres); 	dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel);	switch (var->bits_per_pixel) {	case 4:		rgbidx = RGB_4;		break;	case 8:		rgbidx = RGB_8;		break;	case 16:		rgbidx = RGB_16;		break;	default:		return -EINVAL;	} 	/*	 * Copy the RGB parameters for this display	 * from the machine specific parameters.	 */	var->red    = fbi->rgb[rgbidx]->red;	var->green  = fbi->rgb[rgbidx]->green;	var->blue   = fbi->rgb[rgbidx]->blue;	var->transp = fbi->rgb[rgbidx]->transp; 	dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n",		var->red.length, var->green.length, var->blue.length,		var->transp.length); 	dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n",		var->red.offset, var->green.offset, var->blue.offset,		var->transp.offset); #ifdef CONFIG_CPU_FREQ	dev_dbg(fbi->dev, "dma period = %d ps, clock = %ld kHz\n",		sa1100fb_display_dma_period(var),		clk_get_rate(fbi->clk) / 1000);#endif 	return 0;} static void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual){	if (fbi->inf->set_visual)		fbi->inf->set_visual(visual);} /* * sa1100fb_set_par(): *	Set the user defined part of the display for the specified console */static int sa1100fb_set_par(struct fb_info *info){	struct sa1100fb_info *fbi =		container_of(info, struct sa1100fb_info, fb);	struct fb_var_screeninfo *var = &info->var;	unsigned long palette_mem_size; 	dev_dbg(fbi->dev, "set_par\n"); 	if (var->bits_per_pixel == 16)		fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;	else if (!fbi->inf->cmap_static)		fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;	else {		/*		 * Some people have weird ideas about wanting static		 * pseudocolor maps.  I suspect their user space		 * applications are broken.		 */		fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;	} 	fbi->fb.fix.line_length = var->xres_virtual *				  var->bits_per_pixel / 8;	fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; 	palette_mem_size = fbi->palette_size * sizeof(u16); 	dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size); 	fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);	fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; 	/*	 * Set (any) board control register to handle new color depth	 */	sa1100fb_set_visual(fbi, fbi->fb.fix.visual);	sa1100fb_activate_var(var, fbi); 	return 0;} #if 0static intsa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,		  struct fb_info *info){	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info; 	/*	 * Make sure the user isn't doing something stupid.	 */	if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static))		return -EINVAL; 	return gen_set_cmap(cmap, kspc, con, info);}#endif /* * Formal definition of the VESA spec: *  On *  	This refers to the state of the display when it is in full operation *  Stand-By *  	This defines an optional operating state of minimal power reduction with *  	the shortest recovery time

Lines 1–500 of 1226.