Re: au1100fb.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jørg,

I've modified it a little bit since I've post it on the list, so here's the
.c/.h files I use now, and a patch that should also apply. This should work 
with current linux-mips cvs (actually it do on a 22/11/04 snapshot).

I've never tested it on other boards that the pb1100 though. Also, I use the
same scheme as in Linux 2.4, i.e. panels supported are declared in au1100fb.h.

I hope this will work for you, and don't hesitate to interrupt me if you have
any questions/problems. We personnally decided to work with a different 
graphic chip, so I probably don't need your version of the driver but thanks 
for offering it!

Regards,
Karl



Jørg Ulrich Hansen wrote:

Hi Karl

Sorry to disturb you, but I can see that you have a framebuffer for au1100
for Linux 2.6.
I have also done the same work but I have some problems with nano-X that I
belive is the framebuffers fault.

Your patch that I found on the list I am having problems with to apply.

Do you mind to send me your version of the framebuffer either directly or to
the list.

If you are interrested you can have my version of au1100fb.c/h as well.

Kind regards Jorg

--------------<<<<<<<<<OOOOOOOO>>>>>>>>>--------------
Jorg Hansen               email: jh@xxxxxxxxxxxxxxxxx
Hansen Telecom            Tel: +45 7342 0220
Ellegaardvej 36           Fax: +45 7342 0221
6400 Sonderborg           Mob: +45 2819 1969
Denmark                   http://www.hansen-telecom.dk

Modules for rapid design of mechatronic products
       http://www.mechatronicbrick.dk
--------------<<<<<<<<<OOOOOOOO>>>>>>>>>--------------


--- drivers/video/Makefile	Wed Dec  1 15:02:33 2004
+++ drivers/video/Makefile.patched	Wed Dec  1 15:01:53 2004
@@ -94,7 +94,7 @@
 obj-$(CONFIG_FB_PMAGB_B)	  += pmagb-b-fb.o  cfbfillrect.o cfbcopyarea.o cfbimgblt.o
 obj-$(CONFIG_FB_MAXINE)		  += maxinefb.o  cfbfillrect.o cfbcopyarea.o cfbimgblt.o
 obj-$(CONFIG_FB_TX3912)		  += tx3912fb.o  cfbfillrect.o cfbcopyarea.o cfbimgblt.o
-obj-$(CONFIG_FB_AU1100)		  += au1100fb.o fbgen.o
+obj-$(CONFIG_FB_AU1100)		  += au1100fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
 
 
 # Platform or fallback drivers go here
--- drivers/video/Kconfig	Wed Dec  1 15:02:33 2004
+++ drivers/video/Kconfig.patched	Wed Dec  1 15:01:53 2004
@@ -970,6 +978,10 @@
 config PB1500_TFT
 	prompt "Use TFT Panel on Pb1100 "
 	depends on FB_E1356 && MIPS_PB1100=y
+
+config FB_AU1100
+	bool "Au1100 LCD Driver"
+	depends on FB && MIPS && MIPS_PB1100=y
 
 config FB_SBUS
 	bool "SBUS and UPA framebuffers"
--- arch/mips/au1000/common/platform.c	Wed Dec  1 14:35:16 2004
+++ arch/mips/au1000/common/platform.c.patched	Wed Dec  1 14:37:45 2004
@@ -58,10 +58,39 @@
 	.resource	= au1xxx_usb_ohci_resources,
 };
 
+/*** AU1100 LCD controller ***/
+
+static struct resource au1100_lcd_resources[] = {
+	[0] = {
+		.start		= AU1100_LCD_BASE,
+		.end		= AU1100_LCD_BASE + AU1100_LCD_LEN - 1,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= AU1100_LCD_INT,
+		.end		= AU1100_LCD_INT,
+		.flags		= IORESOURCE_IRQ,
+	}
+};
+
+static u64 au1100_lcd_dmamask = ~(u32)0;
+
+static struct platform_device au1100_lcd_device = {
+	.name		= "au1100-lcd",
+	.id		= 0,
+	.dev = {
+		.dma_mask		= &au1100_lcd_dmamask,
+		.coherent_dma_mask	= 0xffffffff,
+	},
+	.num_resources  = ARRAY_SIZE(au1100_lcd_resources),
+	.resource       = au1100_lcd_resources,
+};
+
 /*-------------------------------------------------------------------------*/
 
 static struct platform_device *au1xxx_platform_devices[] __initdata = {
 	&au1xxx_usb_ohci_device,
+	&au1100_lcd_device,
 };
 
 int au1xxx_platform_init(void)
diff -u -r1.11 au1000.h
--- include/asm-mips/mach-au1x00/au1000.h	23 Sep 2004 06:06:50 -0000	1.11
+++ include/asm-mips/mach-au1x00/au1000.h	28 Oct 2004 15:11:37 -0000
@@ -494,6 +494,9 @@
 #define AU1100_ETH0_BASE	  0xB0500000
 #define AU1100_MAC0_ENABLE       0xB0520000
 #define NUM_ETH_INTERFACES 1
+
+#define AU1100_LCD_BASE           0x15000000
+#define AU1100_LCD_LEN            0x00000800
 #endif // CONFIG_SOC_AU1100
 
 #ifdef CONFIG_SOC_AU1550
@@ -1237,6 +1240,12 @@
   #define SYS_CS_MI2_MASK           (0x7<<SYS_CS_MI2_BIT)
   #define SYS_CS_DI2                (1<<16)
   #define SYS_CS_CI2                (1<<15)
+#ifdef CONFIG_SOC_AU1100
+  #define SYS_CS_ML_BIT             7
+  #define SYS_CS_ML_MASK            (0x7<<SYS_CS_ML_BIT)
+  #define SYS_CS_DL                 (1<<6)
+  #define SYS_CS_CL                 (1<<5)
+#else
   #define SYS_CS_MUH_BIT            12
   #define SYS_CS_MUH_MASK           (0x7<<SYS_CS_MUH_BIT)
   #define SYS_CS_DUH                (1<<11)
@@ -1245,6 +1254,7 @@
   #define SYS_CS_MUD_MASK           (0x7<<SYS_CS_MUD_BIT)
   #define SYS_CS_DUD                (1<<6)
   #define SYS_CS_CUD                (1<<5)
+#endif
   #define SYS_CS_MIR_BIT            2
   #define SYS_CS_MIR_MASK           (0x7<<SYS_CS_MIR_BIT)
   #define SYS_CS_DIR                (1<<1)
Index: include/asm/mach-pb1x00/pb1100.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips/mach-pb1x00/pb1100.h,v
retrieving revision 1.1
diff -u -r1.1 pb1100.h
--- include/asm/mach-pb1x00/pb1100.h	13 Jan 2004 08:09:22 -0000	1.1
+++ include/asm/mach-pb1x00/pb1100.h	2 Dec 2004 15:22:51 -0000
@@ -45,6 +45,21 @@
   #define PB1100_RS232_DSR        (1<<1)
   #define PB1100_RS232_RI         (1<<0)
 
+#define BCSR_SWITCHES_REG     0xAE000008
+  #define BCSR_SWITCHES_DIP_BIT   0
+  #define BCSR_SWITCHES_DIP_MASK  (0xFF<<BCSR_SWITCHES_DIP_BIT)
+  #define BCSR_SWITCHES_DIP_1	  (1<<7)
+  #define BCSR_SWITCHES_DIP_2	  (1<<6)
+  #define BCSR_SWITCHES_DIP_3	  (1<<5)
+  #define BCSR_SWITCHES_DIP_4	  (1<<4)
+  #define BCSR_SWITCHES_DIP_5	  (1<<3)
+  #define BCSR_SWITCHES_DIP_6	  (1<<2)
+  #define BCSR_SWITCHES_DIP_7     (1<<1)
+  #define BCSR_SWITCHES_DIP_8     (1<<0)
+  #define BCSR_SWITCHES_ROTARY_BIT 8
+  #define BCSR_SWITCHES_ROTARY_MASK (0xF<<BCSR_SWITCHES_ROTARY_BIT)
+  #define BCSR_SWITCHES_DOC_LOCK  (1<<15)
+
 #define PB1100_IRDA_RS232     0xAE00000C
   #define PB1100_IRDA_FULL       (0<<14) /* full power */
   #define PB1100_IRDA_SHUTDOWN   (1<<14)
@@ -63,6 +78,11 @@
   #define PC_DRV_EN               (1<<4)
 
 #define PB1100_G_CONTROL      0xAE000014 /* graphics control */
+  #define PB1100_G_CONTROL_RST	  (1<<7)
+  #define PB1100_G_CONTROL_BE 	  (1<<5)
+  #define PB1100_G_CONTROL_SM_PASS (1<<4)
+  #define PB1100_G_CONTROL_BL	  (1<<2)
+  #define PB1100_G_CONTROL_VDD	  (1<<1)
 
 #define PB1100_RST_VDDI       0xAE00001C
   #define PB1100_SOFT_RESET      (1<<15) /* clear to reset the board */
/*
 * BRIEF MODULE DESCRIPTION
 *	Au1100 LCD Driver.
 *
 * Copyright 2002 MontaVista Software
 * Author: MontaVista Software, Inc.
 *		ppopov@xxxxxxxxxx or source@xxxxxxxxxx
 *
 * Copyright 2002 Alchemy Semiconductor
 * Author: Alchemy Semiconductor
 *
 * Rewritten during Linux 2.6 port
 *  by Karl Lessard <klessard@xxxxxxxxxxxxxxxxxx>
 *
 * Based on:
 * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
 *  Created 28 Dec 1997 by Geert Uytterhoeven
 *
 *  This program is free software; you can redistribute	 it and/or modify it
 *  under  the terms of	 the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the	License, or (at your
 *  option) any later version.
 *
 *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED
 *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF
 *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the  GNU General Public License along
 *  with this program; if not, write  to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>

#include <asm/mach-au1x00/au1000.h>

#define DEBUG 1
#define SIMULATOR 0

#include <video/au1100fb.h>

/* 
 * Sanity check. If this is a new Au1100 based board, search for
 * the PB1100 ifdefs to make sure you modify the code accordingly.
 */
#if defined(CONFIG_MIPS_PB1100)
  #include <asm/mach-pb1x00/pb1100.h>
#elif defined(CONFIG_MIPS_DB1100)
  #include <asm/mach-db1x00/db1x00.h>
#else
  #error "Unknown Au1100 board, Au1100 FB driver not supported"
#endif

#define DRIVER_NAME "au1100fb"
#define DRIVER_DESC "LCD controller driver for AU1100 processors"

#define to_au1100fb_device(_info) \
	  (_info ? container_of(_info, struct au1100fb_device, fb_info) : NULL);

/* Driver global data */ 
struct au1100fb_drv_info 
{
        int	panel_idx;
	char*	opt_mode;

} drv_info = { -1, 0 };

/* Bitfields format supported by the controller. Note that the order of formats 
 * SHOULD be the same as in the LCD_CONTROL_SBPPF field, so we can retrieve the
 * right pixel format by doing rgb_bitfields[LCD_CONTROL_SBPPF_XXX >> LCD_CONTROL_SBPPF]
 */
struct fb_bitfield rgb_bitfields[][4] = 
{
  	/*     Red, 	   Green, 	 Blue, 	     Transp   */
	{ { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } },
	{ { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } },
	{ { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } },
	{ { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } },
	{ { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } },

	/* The last is used to describe 12bpp format */
	{ { 8, 4, 0 },  { 4, 4, 0 }, { 0, 4, 0 }, { 0, 0, 0 } },
};

/*-------------------------------------------------------------------------*/

/* Helpers */

static void 
au1100fb_update_fbinfo(struct fb_info *fbi)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	
	/* Update var-dependent FB info */
	if (panel_is_active(fbdev->panel) || panel_is_color(fbdev->panel)) {
		if (fbi->var.bits_per_pixel <= 8) {
			/* palettized */
			fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
			fbi->fix.line_length = fbi->var.xres_virtual / 
							(8/fbi->var.bits_per_pixel);
		} else {
			/* non-palettized */
			fbi->fix.visual = FB_VISUAL_TRUECOLOR;
			fbi->fix.line_length = fbi->var.xres_virtual << 1; /* depth=16 */
		}
	} else {
		/* mono */
		fbi->fix.visual = FB_VISUAL_MONO10;
		fbi->fix.line_length = fbi->var.xres_virtual / 8;
	}
	
	fbi->screen_size = fbi->fix.line_length * fbi->var.yres_virtual;
}

static int
au1100fb_match_rgb(struct fb_var_screeninfo *var)
{
	size_t bf_size = sizeof(struct fb_bitfield);
	int i;

	for (i = 0; i < ARRAY_SIZE(rgb_bitfields); i++) {
		if (!memcmp(&var->red, &rgb_bitfields[i][0], bf_size) &&
		    !memcmp(&var->green, &rgb_bitfields[i][1], bf_size) &&
		    !memcmp(&var->blue, &rgb_bitfields[i][2], bf_size) &&
		    !memcmp(&var->transp, &rgb_bitfields[i][3], bf_size))
			return i;
	}

	return -1;
}

#if DEBUG
static inline void
au1100fb_dump_registers(struct au1100fb_regs *regs)
{
	int active = regs->lcd_control & LCD_CONTROL_PT;
	int color = regs->lcd_control & LCD_CONTROL_PC;
	int format = regs->lcd_control & LCD_CONTROL_SBPPF_MASK;
	int pixclock;
	int refresh;
	int bpp = 0;

	switch (regs->lcd_control & LCD_CONTROL_BPP_MASK) {
		case LCD_CONTROL_BPP_1: bpp = 1; break;
		case LCD_CONTROL_BPP_2: bpp = 2; break;
		case LCD_CONTROL_BPP_4: bpp = 4; break;
		case LCD_CONTROL_BPP_8: bpp = 8; break;
		case LCD_CONTROL_BPP_12: bpp = 12; break;
		case LCD_CONTROL_BPP_16: bpp = 16; break;
	}

	pixclock = (2 * ((regs->lcd_clkcontrol & LCD_CLKCONTROL_PCD_MASK) + 1));
	pixclock = AU1100_LCD_MAX_CLK / pixclock;

	refresh = ((regs->lcd_horztiming & LCD_HORZTIMING_HN2_MASK) 
				>> LCD_HORZTIMING_HN2_BIT)
		 +((regs->lcd_horztiming & LCD_HORZTIMING_HN1_MASK) 
				>> LCD_HORZTIMING_HN1_BIT)
		 +((regs->lcd_horztiming & LCD_HORZTIMING_HPW_MASK) 
				>> LCD_HORZTIMING_HPW_BIT)
		 +((regs->lcd_horztiming & LCD_HORZTIMING_PPL_MASK) 
				>> LCD_HORZTIMING_PPL_BIT)
		 + 4 /* adjust */;

	refresh *= ((regs->lcd_verttiming & LCD_VERTTIMING_VN2_MASK) 
				>> LCD_VERTTIMING_VN2_BIT)
		 +((regs->lcd_verttiming & LCD_VERTTIMING_VN1_MASK) 
				>> LCD_VERTTIMING_VN1_BIT)
		 +((regs->lcd_verttiming & LCD_VERTTIMING_VPW_MASK) 
				>> LCD_VERTTIMING_VPW_BIT)
		 +((regs->lcd_verttiming & LCD_VERTTIMING_LPP_MASK) 
				>> LCD_VERTTIMING_LPP_BIT)
		 + 4 /* adjust */;

	refresh = pixclock / refresh;

	print_dbg("");
	print_dbg("LCD controller register dump:");

	print_dbg("");
	print_dbg("     control:    0x%08x", regs->lcd_control);
	print_dbg("     intstatus:  0x%08x", regs->lcd_intstatus);
	print_dbg("     intenable:  0x%08x", regs->lcd_intenable);
	print_dbg("     horztiming: 0x%08x", regs->lcd_horztiming);
	print_dbg("     verttiming: 0x%08x", regs->lcd_verttiming);
	print_dbg("     clkcontrol: 0x%08x", regs->lcd_clkcontrol);
	print_dbg("     dmaaddr0:   0x%08x", regs->lcd_dmaaddr0);
	print_dbg("     dmaaddr1:   0x%08x", regs->lcd_dmaaddr1);
	print_dbg("     words:      0x%08x", regs->lcd_words);
	print_dbg("     pwmdiv:     0x%08x", regs->lcd_pwmdiv);
	print_dbg("     pwmhi:      0x%08x", regs->lcd_pwmhi);

	print_dbg("");
	print_dbg("     %s %s %s panel",
		  active ? "TFT" : "STN",
		  color ? "color" : "monochrome",
		  active ? (regs->lcd_control & LCD_CONTROL_DB ? "12 pins" : "16 pins")
		         : (color ? (regs->lcd_control & LCD_CONTROL_DP ? "dual"
				 				      	: "single")
			 	  : (regs->lcd_control & LCD_CONTROL_MPI ? "8 bit"
							                 : "4 bit")
			   )
		 );
	print_dbg("     %dbpp%s %dx%d display at %dHz", 
		  bpp,
		  (bpp == 16) ? ((format == LCD_CONTROL_SBPPF_655) ? " 655" :
				 (format == LCD_CONTROL_SBPPF_565) ? " 565" :
				 (format == LCD_CONTROL_SBPPF_556) ? " 556" :
				 (format == LCD_CONTROL_SBPPF_1555) ? " 1555" : " 5551"
				)
		 	      : "",
		  (regs->lcd_horztiming & LCD_HORZTIMING_PPL_MASK) + 1,
		  (regs->lcd_verttiming & LCD_VERTTIMING_LPP_MASK) + 1,
		  refresh
		 );
	if (regs->lcd_control & LCD_CONTROL_SM_MASK) {
		u32 angle = regs->lcd_control & LCD_CONTROL_SM_MASK;
		print_dbg("     Rotated at %d degrees", 
			  (angle == LCD_CONTROL_SM_90) ? 90 :
			  (angle == LCD_CONTROL_SM_180) ? 180 : 270
			  );
	}
	print_dbg("     Pixel clock: %dkHz", pixclock/1000);
	print_dbg("");
}
#endif

/*-------------------------------------------------------------------------*/

/* AU1100 framebuffer driver */

/* fb_open
 * Open a new client reference for a device
 */
int au1100fb_fb_open(struct fb_info *fbi, int user)
{
	print_dbg("fb_open %p %d", fbi, user);
	return 0;
}

/* fb_release
 * Close a client reference to a device
 */
int au1100fb_fb_release(struct fb_info *fbi, int user)
{
	print_dbg("fb_release %p %d", fbi, user);
	return 0;
}

/* fb_check_var
 * Validate var settings with hardware restrictions and modify it if necessary 
 */
int au1100fb_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	struct au1100fb_panel *panel;
	u32 pixclock;
	int screen_size;

	print_dbg("fb_check_var %p %p", var, fbi);

	if (!fbdev)
		return -EINVAL;

	panel = fbdev->panel;

	/* Make sure that the mode respect all LCD controller and 
	 * panel restrictions. */
	var->xres = max(var->xres, panel->min_xres);
	var->xres = min(var->xres, min(panel->max_xres, (u32)AU1100_LCD_MAX_XRES));
	var->yres = max(var->yres, panel->min_yres);
	var->yres = min(var->yres, min(panel->max_yres, (u32)AU1100_LCD_MAX_YRES));
	
	/* We only support virtual mode in Y (no pitch) */
	var->xres_virtual = var->xres;
	var->yres_virtual = max(var->yres_virtual, var->yres);

	var->bits_per_pixel = min(var->bits_per_pixel, panel->max_bpp);

	screen_size = var->xres_virtual * var->yres_virtual;
	if (var->bits_per_pixel > 8) screen_size <<= 1;
	else screen_size /= (8/var->bits_per_pixel);

	if (fbdev->fb_len < screen_size)
		return -EINVAL; /* Virtual screen is to big, abort */

	if (var->rotate) {
		var->rotate = min(var->rotate, (u32)270);
		if (var->rotate % 90) {
			int diff = var->rotate % 90;
			var->rotate -= diff;
		}
		if ((var->rotate != 180) && 
		    ((var->xres > 320) || (var->yres > 240))) {
			var->rotate = 0; /* Resolution too high for such angle */
		}
	}

	/* The max LCD clock is fixed to 48MHz (value of AUX_CLK). The pixel
	 * clock can only be obtain by dividing this value by an even integer.
	 * Fallback to a slower pixel clock if necessary. */
	pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin);
	pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1100_LCD_MAX_CLK/2));

	if (AU1100_LCD_MAX_CLK % pixclock) {
		int diff = AU1100_LCD_MAX_CLK % pixclock;
		pixclock -= diff;
	}

	var->pixclock = KHZ2PICOS(pixclock/1000);

	if (!panel_is_active(panel)) {
		int pcd = AU1100_LCD_MAX_CLK / (pixclock * 2) - 1;

		if (!panel_is_color(panel) 
			&& (panel->control_base & LCD_CONTROL_MPI) && (pcd < 3)) {
			/* STN 8bit mono panel support is up to 6MHz pixclock */
			var->pixclock = KHZ2PICOS(6000);
		} else if (!pcd) {
			/* Other STN panel support is up to 12MHz  */
			var->pixclock = KHZ2PICOS(12000);
		}
	}
	
	/* Set bitfield accordingly */
	switch (var->bits_per_pixel) {

		case 1:
		case 2: 
		case 4: 
		case 8: 
			/* Pseudo color. SHOULD be the following. */
			var->red.offset    = 0;
			var->red.length    = var->bits_per_pixel;
			var->red.msb_right = 0;

			var->green.offset  = 0;
			var->green.length  = var->bits_per_pixel;
			var->green.msb_right = 0;

			var->blue.offset   = 0;
			var->blue.length   = var->bits_per_pixel;
			var->blue.msb_right = 0;

			var->transp.offset = 0;
			var->transp.length = 0;
			var->transp.msb_right = 0;

			break;
			
		case 12:
		{
			/* 12bpp True color. Use the last RGB bitfield */
			int idx = ARRAY_SIZE(rgb_bitfields) - 1;

			var->red    = rgb_bitfields[idx][0];
			var->green  = rgb_bitfields[idx][1];
			var->blue   = rgb_bitfields[idx][2];
			var->transp = rgb_bitfields[idx][3];

			break;
		}	
		case 16:
		{
			/* 16bpp True color. Check if we support it, or force default. */
			if (au1100fb_match_rgb(var) < 0) {

				int idx = LCD_CONTROL_DEFAULT_SBPPF >> LCD_CONTROL_SBPPF_BIT;
				var->red    = rgb_bitfields[idx][0];
				var->green  = rgb_bitfields[idx][1];
				var->blue   = rgb_bitfields[idx][2];
				var->transp = rgb_bitfields[idx][3];
			}
			break;
		}

		default:
			print_dbg("Unsupported depth %dbpp", var->bits_per_pixel);
			return -EINVAL;
	}

	return 0;
}

/* fb_set_par 
 * Set hardware with var settings. This will enable the controller with a specific
 * mode, normally validated with the fb_check_var method
 */
int au1100fb_fb_set_par(struct fb_info *fbi)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	struct au1100fb_regs *regs;
	struct fb_var_screeninfo *var;
	u32 words, pcd;

	print_dbg("fb_set_par %p", fbi);

	if (!fbdev)
		return -EINVAL;

 	var = &fbi->var;
#if SIMULATOR
	regs = (struct au1100fb_regs*)kmalloc(sizeof(struct au1100fb_regs), GFP_KERNEL);
#else
	regs = fbdev->regs;
#endif
	au1100fb_update_fbinfo(fbi);

	/* Stop and reconfigure controller... */
	au1100fb_stop_controller(fbdev, 1);

	/* Determine BPP mode and format */
	regs->lcd_control = fbdev->panel->control_base |
			    ((var->rotate/90) << LCD_CONTROL_SM_BIT);

	switch (var->bits_per_pixel) {
		case 1:
			regs->lcd_control |= LCD_CONTROL_BPP_1;
			break;
		case 2:
			regs->lcd_control |= LCD_CONTROL_BPP_2;
			break;
		case 4:
			regs->lcd_control |= LCD_CONTROL_BPP_4;
			break;
		case 8:
			regs->lcd_control |= LCD_CONTROL_BPP_8;
			break;
		case 12:
			regs->lcd_control |= LCD_CONTROL_BPP_12;
			break;
		case 16:
			regs->lcd_control |= LCD_CONTROL_BPP_16;
			break;
	}

	if (panel_is_active(fbdev->panel)) {

		if (var->bits_per_pixel == 16) {

			/* Find the right pixel format for this mode */
			int idx = au1100fb_match_rgb(var);
			regs->lcd_control |= (idx << LCD_CONTROL_SBPPF_BIT);
			
		} else if (var->bits_per_pixel <= 8) {

			/* For TFT pallettized mode, use 565 RGB palette entries */	
			regs->lcd_control |= LCD_CONTROL_SBPPF_565;
		}
	}

	regs->lcd_intenable = 0;
	regs->lcd_intstatus = 0;

	regs->lcd_horztiming = LCD_HORZTIMING_HN1_N(var->left_margin) |
			       LCD_HORZTIMING_HN2_N(var->right_margin) |
			       LCD_HORZTIMING_HPW_N(var->hsync_len) |
			       LCD_HORZTIMING_PPL_N(var->xres);

	regs->lcd_verttiming = LCD_VERTTIMING_VN1_N(var->upper_margin) |
			       LCD_VERTTIMING_VN2_N(var->lower_margin) |
			       LCD_VERTTIMING_VPW_N(var->vsync_len) |
			       LCD_VERTTIMING_LPP_N(var->yres);

	/* setup clock to obtain value in var->pixclock. 
	 * Note that LCD clock is setup to AUX clock, which is by default 
	 * (and assumed at) 48MHz */
	pcd = AU1100_LCD_MAX_CLK / ((PICOS2KHZ(var->pixclock) * 1000) * 2) - 1;
	regs->lcd_clkcontrol = LCD_CLKCONTROL_PCD_N(pcd) | fbdev->panel->clkcontrol_base;

	regs->lcd_dmaaddr0 = LCD_DMA_SA_N(fbdev->fb_phys);

	if (panel_is_dual(fbdev->panel)) {
		/* Second panel display seconf half of screen if possible,
		 * otherwise display the same as the first panel */
		if (var->yres_virtual >= (var->yres << 1)) {
			regs->lcd_dmaaddr1 = LCD_DMA_SA_N(fbdev->fb_phys +
							  (fbi->fix.line_length * 
						          (var->yres_virtual >> 1)));
		} else {
			regs->lcd_dmaaddr1 = LCD_DMA_SA_N(fbdev->fb_phys);
		}
	}

	words = fbi->fix.line_length / sizeof(u32);
	if (!var->rotate || (var->rotate == 180)) {
		words *= var->yres_virtual;
		if (var->rotate /* 180 */) {
			words -= (words % 8); /* should be divisable by 8 */
		}
	}
	regs->lcd_words = LCD_WRD_WRDS_N(words);

	regs->lcd_pwmdiv = 0;
	regs->lcd_pwmhi = 0;

#if DEBUG
	au1100fb_dump_registers(regs);
#endif
#if SIMULATOR
	kfree(regs);
#endif
	/* Resume controller */
	au1100fb_start_controller(fbdev);

	return 0;
}

/* fb_setcolreg
 * Set color in LCD palette.
 */
int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	u32 *palette = fbdev->regs->lcd_pallettebase;
	u32 value;

	if (regno > (AU1100_LCD_NBR_PALETTE_ENTRIES - 1))
		return -EINVAL;

	if (fbi->var.grayscale) {
		/* Convert color to grayscale */
		red = green = blue = 
			(19595 * red + 38470 * green + 7471 * blue) >> 16;
	}

	if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) {
		/* Place color in the pseudopalette */
		if (regno > 16)
			return -EINVAL;

		palette = (u32*)fbi->pseudo_palette;

		red   >>= (16 - fbi->var.red.length);
		green >>= (16 - fbi->var.green.length);
		blue  >>= (16 - fbi->var.blue.length);
	
		value = (red   << fbi->var.red.offset) 	|	
			(green << fbi->var.green.offset)|
			(blue  << fbi->var.blue.offset);
		value &= 0xFFFF;

	} else if (panel_is_active(fbdev->panel)) {
		/* COLOR TFT PALLETTIZED (use RGB 565) */
		value = (red & 0xF800)|((green >> 5) & 0x07E0)|((blue >> 11) & 0x001F);
		value &= 0xFFFF;

	} else if (panel_is_color(fbdev->panel)) {
		/* COLOR STN MODE */
		value = (((panel_swap_rgb(fbdev->panel) ? blue : red) >> 12) & 0x000F) | 
			((green >> 8) & 0x00F0) | 
			(((panel_swap_rgb(fbdev->panel) ? red : blue) >> 4) & 0x0F00);
		value &= 0xFFF;
	} else {
		/* MONOCHROME MODE */
		value = (green >> 12) & 0x000F;
		value &= 0xF;
	}

	palette[regno] = value;
	
	return 0;
}

/* fb_blank
 * Blank the screen. Depending on the mode, the screen will be
 * activated with the backlight color, or desactivated
 */
int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);

	print_dbg("fb_blank %d %p", blank_mode, fbi);

	switch (blank_mode) {

		case VESA_NO_BLANKING:
			/* Turn on panel */
			fbdev->regs->lcd_control |= LCD_CONTROL_GO;
#ifdef CONFIG_MIPS_PB1100
			if (drv_info.panel_idx == 1) {
				au_writew(au_readw(PB1100_G_CONTROL) 
					  | (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD), 
					  PB1100_G_CONTROL);
			}
#endif
			au_sync();
			break;

		case VESA_VSYNC_SUSPEND:
		case VESA_HSYNC_SUSPEND:
		case VESA_POWERDOWN:
			/* Turn off panel */
			fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
#ifdef CONFIG_MIPS_PB1100
			if (drv_info.panel_idx == 1) {
				au_writew(au_readw(PB1100_G_CONTROL) 
				  	  & ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
				  	  PB1100_G_CONTROL);
			}
#endif
			au_sync();
			break;

		default: 
			break;

	}
	return 0;
}

/* fb_pan_display
 * Pan display in x and/or y as specified
 */
int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	int dy;

	print_dbg("fb_pan_display %p %p", var, fbi);

	if (!var || !fbdev) {
		return -EINVAL;
	}

	if (var->xoffset - fbi->var.xoffset) {
		/* No support for X panning for now! */
		return -EINVAL;
	}
			
	dy = var->yoffset - fbi->var.yoffset;
	if (dy) {

		u32 dmaaddr;

		print_dbg("Panning screen of %d lines", dy);

		dmaaddr = fbdev->regs->lcd_dmaaddr0;
		dmaaddr += (fbi->fix.line_length * dy);

		/* TODO: Wait for current frame to finished */
		fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr);

		if (panel_is_dual(fbdev->panel)) {
			dmaaddr = fbdev->regs->lcd_dmaaddr1;
			dmaaddr += (fbi->fix.line_length * dy);
			fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr);
		}
	}

	return 0;
}

/* fb_rotate
 * Rotate the display of this angle. This doesn't seems to be used by the core,
 * but as our hardware supports it, so why not implementing it...
 */
void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);

	print_dbg("fb_rotate %p %d", fbi, angle);

	if (fbdev && (angle > 0) && !(angle % 90)) {

		au1100fb_stop_controller(fbdev, 1);

		fbdev->regs->lcd_control &= ~(LCD_CONTROL_SM_MASK);
		fbdev->regs->lcd_control |= ((angle/90) << LCD_CONTROL_SM_BIT);

		au1100fb_start_controller(fbdev);
	}
}

/* fb_mmap
 * Map video memory in user space. We don't use the generic fb_mmap method mainly
 * to allow the use of the TLB streaming flag (CCA=6)
 */
int au1100fb_fb_mmap(struct fb_info *fbi, struct file *file, struct vm_area_struct *vma)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	unsigned int len;
	unsigned long start=0, off;

	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
		return -EINVAL;
	}
    
	start = fbdev->fb_phys & PAGE_MASK;
	len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);

	off = vma->vm_pgoff << PAGE_SHIFT;

	if ((vma->vm_end - vma->vm_start + off) > len) {
		return -EINVAL;
	}

	off += start;
	vma->vm_pgoff = off >> PAGE_SHIFT;

	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
	pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6

	vma->vm_flags |= VM_IO;

	if (io_remap_page_range(vma, vma->vm_start, off,
			        vma->vm_end - vma->vm_start,
			        vma->vm_page_prot)) {
		return -EAGAIN;
	}

	return 0;
}

static struct fb_ops au1100fb_fb_ops = 
{
	.owner			= THIS_MODULE,
	.fb_open		= au1100fb_fb_open,
	.fb_release		= au1100fb_fb_release,
	.fb_check_var		= au1100fb_fb_check_var,
	.fb_set_par		= au1100fb_fb_set_par,
	.fb_setcolreg		= au1100fb_fb_setcolreg,
	.fb_blank		= au1100fb_fb_blank,
	.fb_pan_display		= au1100fb_fb_pan_display,
	.fb_fillrect		= cfb_fillrect,
	.fb_copyarea		= cfb_copyarea,
	.fb_imageblit		= cfb_imageblit,
	.fb_cursor		= soft_cursor,
	.fb_rotate		= au1100fb_fb_rotate,
	.fb_sync		= NULL,
	.fb_ioctl		= NULL,
	.fb_mmap		= au1100fb_fb_mmap,
};

/*-------------------------------------------------------------------------*/

static irqreturn_t au1100fb_handle_irq(int irq, void* dev_id, struct pt_regs *regs)
{
	struct au1100fb_device *fbdev = 
		(struct au1100fb_device*) dev_get_drvdata((struct device*)dev_id);

	/* Nothing to do for now, just clear any pending interrupt */
	fbdev->regs->lcd_intstatus = ~LCD_INT_SD;

	return IRQ_HANDLED;
}

/*-------------------------------------------------------------------------*/

/* AU1100 LCD device probe helpers */

static int au1100fb_init_mem(struct device* dev)
{
	struct au1100fb_device *fbdev = (struct au1100fb_device*) dev_get_drvdata(dev);
	struct resource *regs_res;
	unsigned long page;

	if (!dev || !fbdev || !fbdev->panel)
		return -EINVAL;

	/* Allocate region for our registers and map them */
	regs_res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0);
	if (!regs_res) {
		print_err("fail to retrieve registers resource");
		return -EFAULT;
	}

	fbdev->regs_len = regs_res->end - regs_res->start + 1;
	fbdev->regs_phys = regs_res->start;

	if (!request_mem_region(fbdev->regs_phys, fbdev->regs_len, DRIVER_NAME)) {
		print_err("fail to lock memory region at 0x%08x", fbdev->regs_phys);
		return -EBUSY;
	}

	fbdev->regs = (struct au1100fb_regs*)KSEG1ADDR(fbdev->regs_phys);

	print_dbg("Register memory map at %p", fbdev->regs);
	print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len);

	/* Allocate the framebuffer to the maximum screen size * nbr of video buffers */
	fbdev->fb_len = fbdev->panel->max_xres * fbdev->panel->max_yres *
		  	(fbdev->panel->max_bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS;

	fbdev->fb_mem = dma_alloc_coherent(dev, PAGE_ALIGN(fbdev->fb_len), 
					&fbdev->fb_phys, GFP_KERNEL);
	if (!fbdev->fb_mem) {
		print_err("fail to allocate frambuffer (size: %dK))", 
			  fbdev->fb_len / 1024);
		return -ENOMEM;
	}

	/*
	 * Set page reserved so that mmap will work. This is necessary
	 * since we'll be remapping normal memory.
	 */
	for (page = (unsigned long)fbdev->fb_mem;
	     page < PAGE_ALIGN((unsigned long)fbdev->fb_mem + fbdev->fb_len); 
	     page += PAGE_SIZE) {
#if CONFIG_DMA_NONCOHERENT
		SetPageReserved(virt_to_page(CAC_ADDR(page)));
#else
		SetPageReserved(virt_to_page(page));
#endif
	}
	print_dbg("Framebuffer memory map at %p", fbdev->fb_mem);
	print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024);

	return 0;
}

static int au1100fb_init_fbinfo(struct device* dev)
{
	struct au1100fb_device *fbdev = (struct au1100fb_device*) dev_get_drvdata(dev);
	struct fb_info *fbi;
	
	if (!dev || !fbdev || (drv_info.panel_idx < 0))
		return -EINVAL;

	fbi = &fbdev->fb_info;
	memset(fbi, 0, sizeof(struct fb_info));

	fbi->fbops = &au1100fb_fb_ops;

	/* Copy monitor specs from panel data */
	memcpy(&fbi->monspecs, &fbdev->panel->monspecs, sizeof(struct fb_monspecs));

	/* We first try the user mode passed in argument. If that failed, 
	 * or if no one has been specified, we default to the first mode of the 
	 * panel list. Note that after this call, var data will be set */
	if (!fb_find_mode(&fbi->var, 
			  fbi, 
			  drv_info.opt_mode, 
			  fbi->monspecs.modedb, 
			  fbi->monspecs.modedb_len,
			  fbi->monspecs.modedb, 
			  fbdev->panel->max_bpp)) {

		print_err("Cannot find valid mode for panel %s", fbdev->panel->name);
		return -EFAULT;
	}

	fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
	if (!fbi->pseudo_palette) {
		return -ENOMEM;
	}
	memset(fbi->pseudo_palette, 0, sizeof(u32) * 16);

	if (fb_alloc_cmap(&fbi->cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
		print_err("Fail to allocate colormap (%d entries)",
			   AU1100_LCD_NBR_PALETTE_ENTRIES);
		kfree(fbi->pseudo_palette);
		return -EFAULT;
	}

	strncpy(fbi->fix.id, "AU1100", sizeof(fbi->fix.id));
	fbi->fix.smem_start = fbdev->fb_phys;
	fbi->fix.smem_len = fbdev->fb_len;
	fbi->fix.type = FB_TYPE_PACKED_PIXELS;
	fbi->fix.xpanstep = 1;
	fbi->fix.ypanstep = 1;
	fbi->fix.mmio_start = 0;
	fbi->fix.mmio_len = 0;
	fbi->fix.accel = FB_ACCEL_NONE;

	fbi->screen_base = fbdev->fb_mem;

	au1100fb_update_fbinfo(fbi);

	return 0;
}

/*-------------------------------------------------------------------------*/

/* AU1100 LCD controller device driver */

int au1100fb_drv_probe(struct device *dev)
{
	struct au1100fb_device *fbdev = NULL;
	struct resource *irq_res = NULL;
	u32 sys_clksrc;
	int ret;

	if (!dev)
		return -EINVAL;

	/* Allocate new device private */
	fbdev = kmalloc(sizeof(struct au1100fb_device), GFP_KERNEL);
	if (!fbdev) {
		print_err("fail to allocate device private record");
		return -ENOMEM;
	}

	memset((void*)fbdev, 0, sizeof(struct au1100fb_device));
	fbdev->panel = &known_lcd_panels[drv_info.panel_idx];
	fbdev->irq = -1;

	dev_set_drvdata(dev, (void*)fbdev);

	/* Init IO and framebuffer memory */
	ret = au1100fb_init_mem(dev);
	if (ret < 0) {
		goto failed;
	}

	/* Request the IRQ line */
	irq_res = platform_get_resource(to_platform_device(dev), IORESOURCE_IRQ, 0);
	if (!irq_res) {
		print_err("fail to retrieve IRQ resource");
		ret = -EFAULT;
		goto failed;
	}

	ret = request_irq(irq_res->start, au1100fb_handle_irq, 
		 	  SA_INTERRUPT, "lcd", (void*)dev);
	if (ret < 0) {
		print_err("fail to request interrupt line %ld (err: %d)",
			  irq_res->start, ret);
		goto failed;
	}

	fbdev->irq = irq_res->start;

	/* Setup LCD clock to AUX (48 MHz) */
	sys_clksrc = au_readl(SYS_CLKSRC) & ~(SYS_CS_ML_MASK | SYS_CS_DL | SYS_CS_CL);
	au_writel((sys_clksrc | (1 << SYS_CS_ML_BIT)), SYS_CLKSRC);

	/* Init FB data */
	ret = au1100fb_init_fbinfo(dev);
	if (ret < 0) {
		goto failed;
	}

	/* Register new framebuffer */
	ret = register_framebuffer(&fbdev->fb_info);
	if (ret < 0) {
		print_err("cannot register new framebuffer");
		goto failed;
	}

#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
	if (fb_prepare_logo(&fbdev->fb_info)) {

		/* Start display and show logo on boot */
		au1100fb_fb_set_par(&fbdev->fb_info);
		fb_set_cmap(&fbdev->fb_info.cmap, &fbdev->fb_info);

		fb_show_logo(&fbdev->fb_info);
	}
#endif
	return 0;

failed:
	if (fbdev->irq >= 0) {
		free_irq(fbdev->irq, (void*)dev);
	}
	if (fbdev->regs) {
		release_mem_region(fbdev->regs_phys, fbdev->regs_len);
	}
	if (fbdev->fb_mem) {
		dma_free_noncoherent(dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys);
	}
	if (fbdev->fb_info.cmap.len != 0) {
		fb_dealloc_cmap(&fbdev->fb_info.cmap);
	}
	kfree(fbdev);
	dev_set_drvdata(dev, NULL);

	return ret;
}

int au1100fb_drv_remove(struct device *dev)
{
	struct au1100fb_device *fbdev = NULL;

	if (!dev)
		return -ENODEV;

	fbdev = (struct au1100fb_device*) dev_get_drvdata(dev);

#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
	au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->fb_info);
#endif
	au1100fb_stop_controller(fbdev, 0);

	/* Clean up all probe data */
	unregister_framebuffer(&fbdev->fb_info);

	free_irq(fbdev->irq, (void*)dev);
	release_mem_region(fbdev->regs_phys, fbdev->regs_len);

	dma_free_coherent(dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys);

	fb_dealloc_cmap(&fbdev->fb_info.cmap);
	kfree(fbdev->fb_info.pseudo_palette);
	kfree((void*)fbdev);

	return 0;
}

int au1100fb_drv_suspend(struct device *dev, u32 state, u32 level)
{
	/* TODO */
	return 0;
}

int au1100fb_drv_resume(struct device *dev, u32 level)
{
	/* TODO */
	return 0;
}

static struct device_driver au1100fb_driver = {
	.name		= "au1100-lcd",
	.bus		= &platform_bus_type,

	.probe		= au1100fb_drv_probe,
        .remove		= au1100fb_drv_remove,
	.suspend	= au1100fb_drv_suspend,
        .resume		= au1100fb_drv_resume,
};

/*-------------------------------------------------------------------------*/

/* Kernel driver */

int au1100fb_setup(char *options)
{
	char* this_opt;
	int num_panels = ARRAY_SIZE(known_lcd_panels);
	char* mode = NULL;
	int panel_idx = -1;

	if (num_panels <= 0) {
		print_err("No LCD panels supported by driver!");
		return -EFAULT;
	}

	if (options) {
		while ((this_opt = strsep(&options,",")) != NULL) {
			/* Panel option */
			if (!strncmp(this_opt, "panel:", 6)) {
				int i;
				this_opt += 6;
				for (i = 0; i < num_panels; i++) {
					if (!strncmp(this_opt,
					      	     known_lcd_panels[i].name, 
						     strlen(this_opt))) {
						panel_idx = i;
						break;
					}
				}
				if (i >= num_panels) {
 					print_warn("Panel %s not supported!", this_opt);
				}
			}
			/* Mode option (only option that start with digit) */
			else if (isdigit(this_opt[0])) {
				mode = kmalloc(strlen(this_opt) + 1, GFP_KERNEL);
				strncpy(mode, this_opt, strlen(this_opt) + 1);
			}
			/* Unsupported option */
			else {
				print_warn("Unsupported option \"%s\"", this_opt);
			}
		}
	}

	if (panel_idx < 0) {
#if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
		/* Get panel from S10 Switch (board switch) */
		panel_idx = ((*(volatile int *)BCSR_SWITCHES_REG) &
				BCSR_SWITCHES_ROTARY_MASK) >> BCSR_SWITCHES_ROTARY_BIT;
		if (panel_idx >= num_panels)
#endif
		panel_idx = 0;
	}

	drv_info.panel_idx = panel_idx;
	drv_info.opt_mode = mode;

	print_info("Panel=%s Mode=%s",
			known_lcd_panels[drv_info.panel_idx].name,
		      	drv_info.opt_mode ? drv_info.opt_mode : "default");

	return 0;
}

int __init au1100fb_init(void)
{
	char* options;
	int ret;
	
	print_info("" DRIVER_DESC "");
	
	memset(&drv_info, 0, sizeof(drv_info));

	if (fb_get_options(DRIVER_NAME, &options))
		return -ENODEV;

	/* Setup driver with options */
	ret = au1100fb_setup(options);
	if (ret < 0) {
		print_err("Fail to setup driver");
		return ret;
	}

	return driver_register(&au1100fb_driver);
}

void __exit au1100fb_cleanup(void)
{
	driver_unregister(&au1100fb_driver);

	if (drv_info.opt_mode)
		kfree(drv_info.opt_mode);
}

module_init(au1100fb_init);
module_exit(au1100fb_cleanup);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
 * BRIEF MODULE DESCRIPTION
 *	Hardware definitions for the Au1100 LCD controller
 *
 * Copyright 2002 MontaVista Software
 * Copyright 2002 Alchemy Semiconductor
 * Author:	Alchemy Semiconductor, MontaVista Software
 *
 *  This program is free software; you can redistribute	 it and/or modify it
 *  under  the terms of	 the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the	License, or (at your
 *  option) any later version.
 *
 *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED
 *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF
 *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the  GNU General Public License along
 *  with this program; if not, write  to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef _AU1100LCD_H
#define _AU1100LCD_H

#include <asm/mach-au1x00/au1000.h>

#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg)
#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg)
#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg)

#if DEBUG
#define print_dbg(f, arg...) printk(KERN_DEBUG __FILE__ ": " f "\n", ## arg)
#else
#define print_dbg(f, arg...) do {} while (0)
#endif

#if defined(__BIG_ENDIAN)
#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11
#else
#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00
#endif
#define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565

/********************************************************************/

/* LCD controller restrictions */
#define AU1100_LCD_MAX_XRES	800
#define AU1100_LCD_MAX_YRES	600
#define AU1100_LCD_MAX_BPP	16
#define AU1100_LCD_MAX_CLK	48000000
#define AU1100_LCD_NBR_PALETTE_ENTRIES 256

/* Default number of visible screen buffer to allocate */
#define AU1100FB_NBR_VIDEO_BUFFERS 4

/********************************************************************/

struct au1100fb_panel
{
	const char name[25];		/* Full name <vendor>_<model> */
	
	struct 	fb_monspecs monspecs; 	/* FB monitor specs */

	u32   	control_base;		/* Mode-independent control values */
	u32	clkcontrol_base;	/* Panel pixclock preferences */

	u32	min_xres;		/* Minimum horizontal resolution */
	u32	max_xres;		/* Maximum horizontal resolution */
	u32 	min_yres;		/* Minimum vertical resolution */
	u32 	max_yres;		/* Maximum vertical resolution */
	u32 	max_bpp;		/* Maximum depth supported */
};

struct au1100fb_regs 
{
	u32  lcd_control;
	u32  lcd_intstatus;
	u32  lcd_intenable;
	u32  lcd_horztiming;
	u32  lcd_verttiming;
	u32  lcd_clkcontrol;
	u32  lcd_dmaaddr0;
	u32  lcd_dmaaddr1;
	u32  lcd_words;
	u32  lcd_pwmdiv;
	u32  lcd_pwmhi;
	u32  reserved[(0x0400-0x002C)/4];
	u32  lcd_pallettebase[256];
};

struct au1100fb_device {

	struct fb_info fb_info;			/* FB driver info record */

	struct au1100fb_panel 	*panel;		/* Panel connected to this device */

	int irq;				/* IRQ used */

	struct au1100fb_regs* 	regs;		/* Registers memory map */
	size_t       		regs_len;
	unsigned int 		regs_phys;

	unsigned char* 		fb_mem;		/* FrameBuffer memory map */
	size_t	      		fb_len;
	dma_addr_t    		fb_phys;
};

/********************************************************************/

#define LCD_CONTROL                (AU1100_LCD_BASE + 0x0)
  #define LCD_CONTROL_SBB_BIT      21
  #define LCD_CONTROL_SBB_MASK     (0x3 << LCD_CONTROL_SBB_BIT)
    #define LCD_CONTROL_SBB_1        (0 << LCD_CONTROL_SBB_BIT)
    #define LCD_CONTROL_SBB_2        (1 << LCD_CONTROL_SBB_BIT)
    #define LCD_CONTROL_SBB_3        (2 << LCD_CONTROL_SBB_BIT)
    #define LCD_CONTROL_SBB_4        (3 << LCD_CONTROL_SBB_BIT)
  #define LCD_CONTROL_SBPPF_BIT    18
  #define LCD_CONTROL_SBPPF_MASK   (0x7 << LCD_CONTROL_SBPPF_BIT)
    #define LCD_CONTROL_SBPPF_655    (0 << LCD_CONTROL_SBPPF_BIT)
    #define LCD_CONTROL_SBPPF_565    (1 << LCD_CONTROL_SBPPF_BIT)
    #define LCD_CONTROL_SBPPF_556    (2 << LCD_CONTROL_SBPPF_BIT)
    #define LCD_CONTROL_SBPPF_1555   (3 << LCD_CONTROL_SBPPF_BIT)
    #define LCD_CONTROL_SBPPF_5551   (4 << LCD_CONTROL_SBPPF_BIT)
  #define LCD_CONTROL_WP           (1<<17)
  #define LCD_CONTROL_WD           (1<<16)
  #define LCD_CONTROL_C            (1<<15)
  #define LCD_CONTROL_SM_BIT       13
  #define LCD_CONTROL_SM_MASK      (0x3 << LCD_CONTROL_SM_BIT)
    #define LCD_CONTROL_SM_0         (0 << LCD_CONTROL_SM_BIT)
    #define LCD_CONTROL_SM_90        (1 << LCD_CONTROL_SM_BIT)
    #define LCD_CONTROL_SM_180       (2 << LCD_CONTROL_SM_BIT)
    #define LCD_CONTROL_SM_270       (3 << LCD_CONTROL_SM_BIT)
  #define LCD_CONTROL_DB           (1<<12)
  #define LCD_CONTROL_CCO          (1<<11)
  #define LCD_CONTROL_DP           (1<<10)
  #define LCD_CONTROL_PO_BIT       8
  #define LCD_CONTROL_PO_MASK      (0x3 << LCD_CONTROL_PO_BIT)
    #define LCD_CONTROL_PO_00        (0 << LCD_CONTROL_PO_BIT)
    #define LCD_CONTROL_PO_01        (1 << LCD_CONTROL_PO_BIT)
    #define LCD_CONTROL_PO_10        (2 << LCD_CONTROL_PO_BIT)
    #define LCD_CONTROL_PO_11        (3 << LCD_CONTROL_PO_BIT)
  #define LCD_CONTROL_MPI          (1<<7)
  #define LCD_CONTROL_PT           (1<<6)
  #define LCD_CONTROL_PC           (1<<5)
  #define LCD_CONTROL_BPP_BIT      1
  #define LCD_CONTROL_BPP_MASK     (0x7 << LCD_CONTROL_BPP_BIT)
    #define LCD_CONTROL_BPP_1        (0 << LCD_CONTROL_BPP_BIT)
    #define LCD_CONTROL_BPP_2        (1 << LCD_CONTROL_BPP_BIT)
    #define LCD_CONTROL_BPP_4        (2 << LCD_CONTROL_BPP_BIT)
    #define LCD_CONTROL_BPP_8        (3 << LCD_CONTROL_BPP_BIT)
    #define LCD_CONTROL_BPP_12       (4 << LCD_CONTROL_BPP_BIT)
    #define LCD_CONTROL_BPP_16       (5 << LCD_CONTROL_BPP_BIT)
  #define LCD_CONTROL_GO           (1<<0)

#define LCD_INTSTATUS              (AU1100_LCD_BASE + 0x4)
#define LCD_INTENABLE              (AU1100_LCD_BASE + 0x8)
  #define LCD_INT_SD               (1<<7)
  #define LCD_INT_OF               (1<<6)
  #define LCD_INT_UF               (1<<5)
  #define LCD_INT_SA               (1<<3)
  #define LCD_INT_SS               (1<<2)
  #define LCD_INT_S1               (1<<1)
  #define LCD_INT_S0               (1<<0)

#define LCD_HORZTIMING             (AU1100_LCD_BASE + 0xC)
  #define LCD_HORZTIMING_HN2_BIT   24
  #define LCD_HORZTIMING_HN2_MASK  (0xFF << LCD_HORZTIMING_HN2_BIT)
  #define LCD_HORZTIMING_HN2_N(N)  ((((N)-1) << LCD_HORZTIMING_HN2_BIT) & LCD_HORZTIMING_HN2_MASK)
  #define LCD_HORZTIMING_HN1_BIT   16
  #define LCD_HORZTIMING_HN1_MASK  (0xFF << LCD_HORZTIMING_HN1_BIT)
  #define LCD_HORZTIMING_HN1_N(N)  ((((N)-1) << LCD_HORZTIMING_HN1_BIT) & LCD_HORZTIMING_HN1_MASK)
  #define LCD_HORZTIMING_HPW_BIT   10
  #define LCD_HORZTIMING_HPW_MASK  (0x3F << LCD_HORZTIMING_HPW_BIT)
  #define LCD_HORZTIMING_HPW_N(N)  ((((N)-1) << LCD_HORZTIMING_HPW_BIT) & LCD_HORZTIMING_HPW_MASK)
  #define LCD_HORZTIMING_PPL_BIT   0
  #define LCD_HORZTIMING_PPL_MASK  (0x3FF << LCD_HORZTIMING_PPL_BIT)
  #define LCD_HORZTIMING_PPL_N(N)  ((((N)-1) << LCD_HORZTIMING_PPL_BIT) & LCD_HORZTIMING_PPL_MASK)

#define LCD_VERTTIMING             (AU1100_LCD_BASE + 0x10)
  #define LCD_VERTTIMING_VN2_BIT   24
  #define LCD_VERTTIMING_VN2_MASK  (0xFF << LCD_VERTTIMING_VN2_BIT)
  #define LCD_VERTTIMING_VN2_N(N)  ((((N)-1) << LCD_VERTTIMING_VN2_BIT) & LCD_VERTTIMING_VN2_MASK)
  #define LCD_VERTTIMING_VN1_BIT   16
  #define LCD_VERTTIMING_VN1_MASK  (0xFF << LCD_VERTTIMING_VN1_BIT)
  #define LCD_VERTTIMING_VN1_N(N)  ((((N)-1) << LCD_VERTTIMING_VN1_BIT) & LCD_VERTTIMING_VN1_MASK)
  #define LCD_VERTTIMING_VPW_BIT   10
  #define LCD_VERTTIMING_VPW_MASK  (0x3F << LCD_VERTTIMING_VPW_BIT)
  #define LCD_VERTTIMING_VPW_N(N)  ((((N)-1) << LCD_VERTTIMING_VPW_BIT) & LCD_VERTTIMING_VPW_MASK)
  #define LCD_VERTTIMING_LPP_BIT   0
  #define LCD_VERTTIMING_LPP_MASK  (0x3FF << LCD_VERTTIMING_LPP_BIT)
  #define LCD_VERTTIMING_LPP_N(N)  ((((N)-1) << LCD_VERTTIMING_LPP_BIT) & LCD_VERTTIMING_LPP_MASK)

#define LCD_CLKCONTROL             (AU1100_LCD_BASE + 0x14)
  #define LCD_CLKCONTROL_IB        (1<<18)
  #define LCD_CLKCONTROL_IC        (1<<17)
  #define LCD_CLKCONTROL_IH        (1<<16)
  #define LCD_CLKCONTROL_IV        (1<<15)
  #define LCD_CLKCONTROL_BF_BIT    10
  #define LCD_CLKCONTROL_BF_MASK   (0x1F << LCD_CLKCONTROL_BF_BIT)
  #define LCD_CLKCONTROL_BF_N(N)   ((((N)-1) << LCD_CLKCONTROL_BF_BIT) & LCD_CLKCONTROL_BF_MASK)
  #define LCD_CLKCONTROL_PCD_BIT   0
  #define LCD_CLKCONTROL_PCD_MASK  (0x3FF << LCD_CLKCONTROL_PCD_BIT)
  #define LCD_CLKCONTROL_PCD_N(N)  (((N) << LCD_CLKCONTROL_PCD_BIT) & LCD_CLKCONTROL_PCD_MASK)

#define LCD_DMAADDR0               (AU1100_LCD_BASE + 0x18)
#define LCD_DMAADDR1               (AU1100_LCD_BASE + 0x1C)
  #define LCD_DMA_SA_BIT           5
  #define LCD_DMA_SA_MASK          (0x7FFFFFF << LCD_DMA_SA_BIT)
  #define LCD_DMA_SA_N(N)          ((N) & LCD_DMA_SA_MASK)

#define LCD_WORDS                  (AU1100_LCD_BASE + 0x20)
  #define LCD_WRD_WRDS_BIT         0
  #define LCD_WRD_WRDS_MASK        (0xFFFFFFFF << LCD_WRD_WRDS_BIT)
  #define LCD_WRD_WRDS_N(N)        ((((N)-1) << LCD_WRD_WRDS_BIT) & LCD_WRD_WRDS_MASK)

#define LCD_PWMDIV                 (AU1100_LCD_BASE + 0x24)
  #define LCD_PWMDIV_EN            (1<<12)
  #define LCD_PWMDIV_PWMDIV_BIT    0
  #define LCD_PWMDIV_PWMDIV_MASK   (0xFFF << LCD_PWMDIV_PWMDIV_BIT)
  #define LCD_PWMDIV_PWMDIV_N(N)   ((((N)-1) << LCD_PWMDIV_PWMDIV_BIT) & LCD_PWMDIV_PWMDIV_MASK)

#define LCD_PWMHI                  (AU1100_LCD_BASE + 0x28)
  #define LCD_PWMHI_PWMHI1_BIT     12
  #define LCD_PWMHI_PWMHI1_MASK    (0xFFF << LCD_PWMHI_PWMHI1_BIT)
  #define LCD_PWMHI_PWMHI1_N(N)    (((N) << LCD_PWMHI_PWMHI1_BIT) & LCD_PWMHI_PWMHI1_MASK)
  #define LCD_PWMHI_PWMHI0_BIT     0
  #define LCD_PWMHI_PWMHI0_MASK    (0xFFF << LCD_PWMHI_PWMHI0_BIT)
  #define LCD_PWMHI_PWMHI0_N(N)    (((N) << LCD_PWMHI_PWMHI0_BIT) & LCD_PWMHI_PWMHI0_MASK)

#define LCD_PALLETTEBASE                (AU1100_LCD_BASE + 0x400)
  #define LCD_PALLETTE_MONO_MI_BIT      0
  #define LCD_PALLETTE_MONO_MI_MASK     (0xF << LCD_PALLETTE_MONO_MI_BIT)
  #define LCD_PALLETTE_MONO_MI_N(N)     (((N)<< LCD_PALLETTE_MONO_MI_BIT) & LCD_PALLETTE_MONO_MI_MASK)

  #define LCD_PALLETTE_COLOR_RI_BIT     8
  #define LCD_PALLETTE_COLOR_RI_MASK    (0xF << LCD_PALLETTE_COLOR_RI_BIT)
  #define LCD_PALLETTE_COLOR_RI_N(N)    (((N)<< LCD_PALLETTE_COLOR_RI_BIT) & LCD_PALLETTE_COLOR_RI_MASK)
  #define LCD_PALLETTE_COLOR_GI_BIT     4
  #define LCD_PALLETTE_COLOR_GI_MASK    (0xF << LCD_PALLETTE_COLOR_GI_BIT)
  #define LCD_PALLETTE_COLOR_GI_N(N)    (((N)<< LCD_PALLETTE_COLOR_GI_BIT) & LCD_PALLETTE_COLOR_GI_MASK)
  #define LCD_PALLETTE_COLOR_BI_BIT     0
  #define LCD_PALLETTE_COLOR_BI_MASK    (0xF << LCD_PALLETTE_COLOR_BI_BIT)
  #define LCD_PALLETTE_COLOR_BI_N(N)    (((N)<< LCD_PALLETTE_COLOR_BI_BIT) & LCD_PALLETTE_COLOR_BI_MASK)

  #define LCD_PALLETTE_TFT_DC_BIT       0
  #define LCD_PALLETTE_TFT_DC_MASK      (0xFFFF << LCD_PALLETTE_TFT_DC_BIT)
  #define LCD_PALLETTE_TFT_DC_N(N)      (((N)<< LCD_PALLETTE_TFT_DC_BIT) & LCD_PALLETTE_TFT_DC_MASK)

/********************************************************************/

/* List of default modes for a registered panel.
 * If you want to use generic modes, set panel's modedb to NULL.
 */
static struct fb_videomode sharp_lq038q5dr01_modes[] =
{
	{
		NULL, 0, 320, 240, KHZ2PICOS(6000), 60, 8, 17, 5, 12, 1,
		0, FB_VMODE_NONINTERLACED, 0
	},
};
static struct fb_videomode sharp_lq64d343_modes[] =
{
	{
		"SRTB", 0, 640, 480, KHZ2PICOS(12000), 8, 38, 26, 34, 36, 2,
		0, FB_VMODE_NONINTERLACED, 0
	},
};

/* List of panels known to work with the AU1100 LCD controller.
 * To add a new panel, enter the same specifications as the
 * Generic_TFT one, and MAKE SURE that it doesn't conflicts 
 * with the controller restrictions. Restrictions are:
 *
 * STN color panels: max_bpp <= 12
 * STN mono panels: max_bpp <= 4
 * TFT panels: max_bpp <= 16
 * max_xres <= 800
 * max_yres <= 600
 */
static struct au1100fb_panel known_lcd_panels[] =
{
	/*** Generic 640x480 16bpp TFT LCD ***/
	[0] = {
		.name = "Generic_TFT",
		.monspecs = {
			.modedb = NULL,
			.modedb_len = 0,
			.hfmin = 29000,
			.hfmax = 30000,
			.vfmin = 60,
			.vfmax = 60,
			.dclkmin = 12000000,
			.dclkmax = 12000000,
			.input = FB_DISP_RGB,
		},
		 
		( LCD_CONTROL_C
		| LCD_CONTROL_DEFAULT_PO
		| LCD_CONTROL_PT
		| LCD_CONTROL_PC ),
		 
		0,

		640, 640, 
		480, 480, 
		16,
	},

        /*** Pb1100 LCDA Sharp 320x240 TFT panel ***/
	[1] = {
		.name = "Sharp_LQ038Q5DR01",
		.monspecs = {
			.modedb  = sharp_lq038q5dr01_modes,
			.modedb_len = ARRAY_SIZE(sharp_lq038q5dr01_modes),
			.hfmin   = 12500,
			.hfmax	 = 20000,
			.vfmin	 = 38,
			.vfmax   = 81,
			.dclkmin = 4500000,
			.dclkmax = 6800000,
			.input = FB_DISP_RGB,
		},

		( LCD_CONTROL_C
		| LCD_CONTROL_DEFAULT_PO
		| LCD_CONTROL_PT
		| LCD_CONTROL_PC ),

		0,
		
		320, 320, 
		240, 240, 
		16,
	},

        /*** Sharp 640x480 TFT panel ***/
	[2] = {
		.name = "Sharp_LQ64D343",
		.monspecs = {
			.modedb  = sharp_lq64d343_modes,
			.modedb_len = ARRAY_SIZE(sharp_lq64d343_modes),
			.hfmin   = 22222,
			.hfmax	 = 31481,
			.vfmin	 = 40,
			.vfmax   = 61,
			.dclkmin = 6000000,
			.dclkmax = 28000000,
			.input = FB_DISP_RGB,
		},

		( LCD_CONTROL_C
		| LCD_CONTROL_DEFAULT_PO
		| LCD_CONTROL_CCO
		| LCD_CONTROL_PT
		| LCD_CONTROL_PC ),

		0,
		
		640, 640, 
		480, 480, 
		16,
	},
};

/********************************************************************/

/* Inline helpers */

#define panel_is_dual(panel)  (panel->control_base & LCD_CONTROL_DP)
#define panel_is_active(panel)(panel->control_base & LCD_CONTROL_PT)
#define panel_is_color(panel) (panel->control_base & LCD_CONTROL_PC)
#define panel_swap_rgb(panel) (panel->control_base & LCD_CONTROL_CCO)

static inline int
au1100fb_start_controller(struct au1100fb_device *fbdev)
{
	return fbdev->fb_info.fbops->fb_blank(VESA_NO_BLANKING, &fbdev->fb_info);
}

static inline int
au1100fb_stop_controller(struct au1100fb_device *fbdev, int wait)
{
	int ret = fbdev->fb_info.fbops->fb_blank(VESA_POWERDOWN, &fbdev->fb_info);
	if (wait) {
		/* Wait for the SD bit */
		u32 intstatus;
		do {
			*(volatile u32*)(&fbdev->regs->lcd_intstatus);
			intstatus = *(volatile u32*)(&fbdev->regs->lcd_intstatus);

		} while (!(intstatus & LCD_INT_SD));
	}
	return ret;
}

#endif /* _AU1100LCD_H */

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux