/******************************************************************************
 * 
 *  File:	linux/drivers/video/ep93xxfb.c
 *
 *  Purpose: Support CRT output on a EP9312/EP9315 evaluation board.
 *
 *  Build:	Define CONFIG_FB, CONFIG_FB_EP93XX
 *		which will bring in:
 *			CONFIG_FONT_8x8
 *			CONFIG_FONT_8x16
 *			CONFIG_FONT_ACORN_8x8
 *
 *		When building as console, define CONFIG_VT, CONFIG_VT_CONSOLE
 *		and make sure CONFIG_CMDLINE does NOT have "console=" set. 
 *      CONFIG_EP93XX_KBD_SPI or CONFIG_EP93XX_KBD_SCANNED should also 
 *      be specified.
 *
 *  History:	010529	Norman Farquhar at LynuxWorks.
 *
 *		Initial version supports:
 *		- crt only
 *		- fixed frequency monitor timings: 640x480
 *		- single default video mode supported: 640x480x8
 *		- no module support because this is core logic
 *		- never will be SMP
 *
 *		Derived from:	arm-linux-2.4.0-test11-rmk1
 * 				linux/drivers/video/sa1100fb.c
 *				Version 2000/08/29
 *  				Copyright (C) 1999 Eric A. Thomas
 *				See file for many other contributors.
 *  
 * (c) Copyright 2001 LynuxWorks, Inc., San Jose, CA.  All rights reserved.
 *   
 * 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.
 *
 *=============================================================================
 *	Overview of Frame Buffer driver
 *=============================================================================
 *
 *	The frame buffer driver supports the core logic display controller
 *	of the ep93xx family machines.  These machines are embedded systems
 *	and this driver provides for a few video modes with small code footprint.
 *	Both CRT and LCD may be supported in the future.
 *
 *	Generic fb driver model (skeletonfb.c, fbgen.c) is a bit whacked
 *	so this driver has been converted back to simpler older style.
 *	See below for list of complaints about generic fb model!
 *
 *
 *	Linux color models supported:
 *
 *		CFB8 is 8 bit packed pixel format using 256 color LUT
 *		Believed to be similar to Microsoft Windows:
 *			16 colors VGA standard
 *			16 grays
 *			224 color loadable palette
 *		Note: Linux Logo uses 6x6x6=216 color space
 *
 *	Linux color models NOT supported:
 *
 *		CFB4 is 4 bit packed pixel format using 16 color LUT
 *			16 colors VGA standard
 *		CFB16 is 16 bit flexible format (ep93xx uses R5:G6:B5)
 *		CFB24 is 24 bit truecolor R8:G8:B8
 *		CFB32 is 32 bit truecolor alpha:R8:G8:B8
 *
 *	Reminder about setting up /dev device nodes:
 *
 *		console	c 5,1		to virtual console driver
 *					so that Linux bootup is visible
 *					when fb is console.
 *
 *		fb	link /dev/fb0	used by applications
 *
 *		fb0	c 29,0		to fbmem device
 *
 *		tty	link /dev/console	recommended by BinZ for
 *		tty0	link /dev/console	older Linux applications
 *
 *
 *=============================================================================
 *	Hardware Details
 *=============================================================================
 *
 *	1.  Display controller uses system memory, not a dedicated frame buffer
 *	RAM.  This means that display controller bandwidth to RAM steals from
 *	CPU bandwidth to RAM.
 *
 *		So if bandwidth to memory is about 80MB/s assuming that
 *		all accesses are 16 byte blocks taking 10 clocks on 50MHz
 *		system bus.  Then 640x480x8 60Hz is about 19MB/s, or
 *		1/4 of bus bandwidth which should not impact us badly.
 *
 *	2.  Dual hw palettes are a way to avoid sparkle and allow updating at
 *	anytime, not just during blank period.  Pending hw palette switch
 *	takes place once per frame during blank.
 *
 *	So we keep the master palette copy in memory and update the active hw
 *	palette by updating the inactive hw palette and then switching.
 *
 *	The only complication is writing palette while switch is pending.
 *	We assume LUTSTAT is updated synchronously with bus clock so that
 *	writes to LUT complete to either RAM0 or RAM1, even if request to
 *	switch RAMs comes in asynchronously, in the middle of write to LUT.
 *
 *	3.  Some timing registers must be unlocked before access.
 *
 *	4.  TBD Check if we can make video buffer writethrough cache?
 *
 *	If cache is writeback/writethrough, then we can select writethrough
 *	caching for our video buffer.  This permits cache to speed up our
 *	read accesses to the video buffer.
 *
 *		Note: 	This assumes we are running only with CPU writing the
 *			video buffer, not DMA, not the graphics bitblit engine.
 *			Otherwise, writethrough policy fails and must keep
 *			video buffer uncached.
 *
 *	5.  Video clock derived from VIDDIV and trunk 0 or 1.
 *
 *	Current vclk = PLL0/2/7.5 = 368/15 = 24.533 Mhz
 *	within 2% of 25MHz nominal value for fixed frequency monitor.
 *
 *		Note:	Rev B chip runs on 3.6MHz XTAL and has way
 *			too much jitter on CRT.  I do not know if some
 *			magic setup can help it.		
 *
 *=============================================================================
 *	Send to www.complaints.linux.com
 *=============================================================================
 *
 *	Generic fb model is too messy IMHO and here is my list of
 *	problems with it.
 *
 *	1.  Note ALL fbgen.c functions really take fb_info_gen, instead of
 *	fb_info parameters, by casting info instead of changing prototype.
 *
 *	2.  Messy stuff in doing encode/decode var.
 *
 *	3.  The info structure holds the controlling variables for video.
 *	But data is passed around and variables tweaked inside routines
 *	in which you would not expect it.
 *
 *	4.  It is not clear that all variables of a structure get inited
 *	properly because it is not done in one spot.
 *
 *	5.  In the driver model fbskeleton.c, initially, both the 'var' and
 *	'fix' data are derived from 'par'.  This seems backward, and I think
 *	reflects Linux genesis on a PC with a BIOS which has already set the
 *	initial video mode.
 *
 *	6.  For some reason, there is a local copy of 'par' which is the true
 *	current mode (Why not use the 'par' field in info??).
 *
 *	7.  Consistency checks are device specific, but fb implementation
 *	is messy:  high level video mode requests are pushed down to low
 *	level data 'par' and then pulled back out again to high level
 *	with a consistent, possibly changed mode, changed 'var'
 *
 *		decode_var	really checks var consistency and
 *				translates var to par
 *
 *		encode_var	translates par to var
 *				(one driver just passes back current var
 *				 which assumes that par set from var)
 *
 ******************************************************************************/


#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/wrapper.h>

#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/proc/pgtable.h>

//TBD some of these may not be needed until full implementation

#include <video/fbcon.h>
#include <video/fbcon-mfb.h>
#include <video/fbcon-cfb4.h>
#include <video/fbcon-cfb8.h>
#include <video/fbcon-cfb16.h>

#define RasterSetLocked(registername,value) \
    {                                       \
        outl( 0xAA, REALITI_SWLOCK );       \
        outl( value, registername);         \
    }
//#define UART_HACK_DEBUG
/*
 *  Debug macros 
 */
#ifdef UART_HACK_DEBUG
void UARTWriteString(char * msg);
#endif // UART_HACK_DEBUG

#ifdef DEBUG
#define DPRINTK(fmt, args...) \
	{ char str[256];\
		sprintf(str, "%s: " fmt, __FUNCTION__ , ## args); \
		UARTWriteString(str);\
	}
//#  define DPRINTK(fmt, args...)	printk("%s: " fmt, __FUNCTION__ , ## args)
#else
#  define DPRINTK(fmt, args...)
#endif

// Max palette entries over all video modes
#define MAX_PALETTE_NUM_ENTRIES		256

// Max X resolution and Y resolution depend on whether LCD is supported,
// and the monitor type.

#define MAX_CRT_XRES	640
#define MAX_CRT_YRES	480

// Max bpp is fixed for low resolutions, but fundamentally depends on the
// memory bandwidth which the system can allocate to the raster engine,
// and so depends on application.
//
//	TBD can create an array which determines max bpp based on passed
//	TBD in resolutions.
//
#define MAX_BPP		32

// Framebuffer max mem size in bytes over all video modes
// Note: mypar not valid yet so need hard numbers here.
#define FB_MAX_MEM_SIZE ((640*480 * MAX_BPP)/8)

// Framebuffer mapped mem size in bytes
//	This will be an integral number of pages.
#define FB_MAPPED_MEM_SIZE (PAGE_ALIGN(FB_MAX_MEM_SIZE + PAGE_SIZE-1))

/* Minimum X and Y resolutions */
//TBD how realistic is this for CRT??
#define MIN_XRES	64
#define MIN_YRES	64

#define EP93XX_NAME	"EP93XX"
#define NR_MONTYPES	1

/* Local LCD controller parameters */
/* These can be reduced by making better use of fb_var_screeninfo parameters.*/
/* Several duplicates exist in the two structures. */
struct ep93xxfb_par
{
    u_char	   *p_screen_base;
    u_char	   *v_screen_base;
    unsigned long  screen_size;
    unsigned int   palette_size;
    unsigned int   xres;
    unsigned int   yres;
    unsigned int   bits_per_pixel;
      signed int   montype;
    unsigned int   currcon;
    unsigned int   visual;
    u16            palette[16];    // Funky 16 table lookup used by "optional"
                                    // Parameter.
};

//TBD Linux fbmon.c tries to check monitor support for requested mode.
//TBD But 2.4.0 test11 has removed this code from the build.
/* Fake monspecs to fill in fbinfo structure */
//TBD strict VGA monitor has 60,60 instead of 50,65
static struct fb_monspecs __initdata monspecs = {
	 30000, 70000, 50, 65, 0 	/* Generic, not fixed frequency */
};

// Default CRT video mode: PCLK = 25.175MHz, HSYNC = 31.5KHz(?), REFR = 59.5Hz
// TBD may want to change this ... I see different numbers some other places.
// TBD HSYNC is the important number for fixed frequency monitor.

static const char defaultname[]="VGA CRT";	// must not be __initdata!

#if defined (CONFIG_FB_LCD_EP93XX)
#define DEFAULT_MODE 1
#elif defined (CONFIG_FB_CX25871)
#define DEFAULT_MODE 3
#elif defined (CONFIG_FB_CRT_EP93XX)
#define DEFAULT_MODE 0
#else
#error What Display Setting was that!!!
#endif

typedef struct _DisplayTimingValues
{
    const char *    Name;
    unsigned long   DisplayID;
    int      (* RasterConfigure)(struct _DisplayTimingValues *pTimingValues);
    unsigned short  Refresh;
    unsigned long   VDiv;
 
    unsigned short  HRes;
    unsigned short  HFrontPorch;
    unsigned short  HBackPorch;
    unsigned short  HSyncWidth;
    unsigned short  HTotalClocks;

    unsigned short  VRes;
    unsigned short  VFrontPorch;
    unsigned short  VBackPorch;
    unsigned short  VSyncWidth;
    unsigned short  VTotalClocks;
} DisplayTimingValues;

typedef int (* fRasterConfigure)(DisplayTimingValues *);

typedef enum 
{
    CRT_GENERIC,
    Philips_LB064V02A1,
    CX25871,
    Sharp
} DisplayType;

#define TIMING_VALUES(NAME, DISPID, FUNC, REFRESH, VDIV,                \
                   HRES, HFP, HBP, HSYNC, VRES, VFP, VBP, VSYNC)        \
{                                                                       \
    Name:NAME,                                                          \
    DISPID, FUNC, REFRESH, VDIV,                                        \
    HRES, HFP, HBP, HSYNC, (HRES + HFP + HBP + HSYNC),                  \
    VRES, VFP, VBP, VSYNC, (VRES + VFP + VBP + VSYNC)                   \
}

static void InitializeCX25871For640x480NTSC(void);
static int Conexant_CX25871(DisplayTimingValues *pTimingValues);
static int PhilipsLCD(DisplayTimingValues *pTimingValues);
//static int NecLCD(DisplayTimingValues *pTimingValues);

DisplayTimingValues static TimingValues[]=
{
    //
    // 640x480 Progressive Scan
    //    
    TIMING_VALUES("CRT_GENERIC", CRT_GENERIC, 
		  (fRasterConfigure)0, 
		  60, 0x0000c108, 640, 16, 48, 96, 480, 11, 31, 2),
    //
    // 640x480 Progressive Scan Philips LB064V02A1 on EDB9312 Board.
    //
    TIMING_VALUES("Philips LB064V02A1", Philips_LB064V02A1, 
		  PhilipsLCD, 
		  68, 0x0000c107, 640, 16, 48, 96, 480, 11, 31, 2),
    //
    // Sharp LQ64D343 LCD Panel
    //
    TIMING_VALUES("Sharp LQ64d343", CRT_GENERIC,
                  (fRasterConfigure)0,
                  60, 0x0000c205, 640, 32, 32, 96, 480, 34, 34, 4),
    //
    // NEC LCD Panel
    //
//    TIMING_VALUES("NEC", CRT_GENERIC,
//                  NecLCD,
//                  60, 0x0000c204, 640, 32, 32, 96, 480, 34, 34, 4),
    //
    // 640x480 NTSC Support for Conexant CX25871 
    //
    TIMING_VALUES("Conexant CX25871", CX25871, 
		  Conexant_CX25871,
		  60, 0x0000c317, 640, 0, 0, 0, 480, 0, 0, 0),
};
#define NUM_TIMING_VALUES (sizeof(TimingValues)/sizeof(DisplayTimingValues))

unsigned int master_palette[256];	/* master copy of palette data */

//TBD Before fbcon started, the driver calls set_var with con=-1
//TBD which initializes global_disp and puts ptr to it in info.
//TBD Later on display console array keeps track of display settings.
static struct display global_disp;	/* Initial Display Settings */

static struct fb_info fb_info;		// initialized in init_fbinfo() 
static struct ep93xxfb_par mypar;
//static struct fb_var_screeninfo __initdata init_var = {};


static int ep93xxfb_get_fix(struct fb_fix_screeninfo *fix, 
			    int con, struct fb_info *info);
static int ep93xxfb_get_var(struct fb_var_screeninfo *var, 
			    int con, struct fb_info *info);
static int ep93xxfb_set_var(struct fb_var_screeninfo *var, 
			    int con, struct fb_info *info);
static int ep93xxfb_get_cmap(struct fb_cmap *cmap, int kspc, 
			     int con, struct fb_info *info);
static int ep93xxfb_set_cmap(struct fb_cmap *cmap, int kspc, 
			     int con, struct fb_info *info);
 
static int  ep93xxfb_switch(int con, struct fb_info *info);
static void ep93xxfb_blank(int blank, struct fb_info *info);
static int  MapFramebuffer(void);
static int  ConfigureRaster(struct fb_var_screeninfo *var, 
			    DisplayTimingValues *pTimingValues);
//static void ep93xxfb_enable_lcd_controller(void);
//static void ep93xxfb_disable_lcd_controller(void);

static struct fb_ops ep93xxfb_ops = {
    owner:		THIS_MODULE,
    fb_get_fix:	ep93xxfb_get_fix,
    fb_get_var:	ep93xxfb_get_var,
    fb_set_var:	ep93xxfb_set_var,
    fb_get_cmap:	ep93xxfb_get_cmap,
    fb_set_cmap:	ep93xxfb_set_cmap,
};

//-----------------------------------------------------------------------------
//	Local functions
//-----------------------------------------------------------------------------

#ifdef UART_HACK_DEBUG

void SendChar(char value)
{
    // send a char to the fifo               
    do
    {
    } while (inl(UART3FR) & 0x20);

    outl(value, UART3DR);
}

//****************************************************************************
// UARTWriteString
//****************************************************************************
//
void UARTWriteString(char * msg) 
{
    int index = 0;
	unsigned int uiTemp;

    if((inl(UART3CR) & 0x1) == 0)
    {
        uiTemp = inl(SYSCON_DEVCFG);
		SysconSetLocked(SYSCON_DEVCFG, (uiTemp | SYSCON_DEVCFG_U3EN) );  
		outl(0xf,UART3CR_L);
		outl(0, UART3CR_M);
		outl(0x70,UART3CR_H);
        outl(0x1,UART3CR);
    }
    while (msg[index] != 0)
    {
        if (msg[index] == '\n')
        {
            SendChar('\r');
            SendChar('\n');
        }
        else 
        {
            SendChar(msg[index]);
        }
        index++;
    }
}
#endif // UART_HACK_DEBUG

/*
 * ep93xxfb_palette_write:
 *	Encode palette data to 24bit palette format.
 *      Write palette data to the master palette and inactive hw palette
 *	switch palettes.  And handle asynchronous palette switches.
 */
static inline void 
ep93xxfb_palette_write(u_int regno, u_int red, u_int green,
		       u_int blue, u_int trans)
{
    unsigned int cont, i, pal;
    
    // Only supports color LUT, not gray LUT
    //
    //	TBD if not in 4 or 8bpp, then does LUT logic operate?
    //	TBD or do we need to do nothing here?
    
    // LCD:	TBD RGB mapping may match spec p193.
    //
    // CRT: LUT RGB mapping is really R/G/B from high to low bits
    //	because really determined by wiring to video DAC.
    //	(disregard spec p 193 showing color LUT B/G/R order)
    //	Here are the details:
    //
    //	Shift mode 1 directs LUT bits 7-2 onto P5-P0,
    //	LUT bits 15-10 onto P11-P6, and LUT bits 23-16
    //	onto P17-P12.
    //
    //	Board wired P17-12 to video DAC Red inputs, 	
    //	P11-P6 wired to video DAC Green inputs, and  	
    //	P5-P0 wired to video DAC Blue inputs. 	
    //
    pal = ((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
    
    master_palette[regno] = pal; 
    
    // Two cases here:
    // 1.  	If LUT switch is pending, then write inactive LUT and when
    //	switch happens, this new palette entry will be active.
    // 	Race condition here between LUT switch and this write is okay
    // 	since we fix it below.
    //
    // 2.	If LUT switch is not pending, then write here is incomplete
    //	and whole palette will be written below.
    
    outl( pal, (COLOR_LUT+(regno<<2)) );
    
    cont = inl(LUTCONT);
    
    if ((cont&LUTCONT_STAT && cont&LUTCONT_RAM1) ||
        (!(cont&LUTCONT_STAT) && !(cont&LUTCONT_RAM1)))
    {
        // LUT switch is no longer pending
        
        // We do not know if write to LUT above really went
        // to currently active LUT.  So need to make sure that
        // data gets into inactive LUT and switch LUTs.
        //
        // But currently inactive LUT may be out of date
        // in more entries than just last write.
        // Need to update currently inactive LUT for all writes
        // which went to currently active LUT.
        // Fully update the LUT now, which is a simpler policy
        // than trying to track writes and do partial update of LUT.
        // (Worstcase impact: we update palette every frame)
        
        for (i=0; i< 256; i++)	// Update inactive LUT
        {
            outl( master_palette[i], (COLOR_LUT+(i<<2)) );
        }
        // Switch active LUTs next frame
        outl( cont ^ LUTCONT_RAM1, LUTCONT );
    }
}

static inline void 
ep93xxfb_palette_read(u_int regno, u_int *red, u_int *green,
		      u_int *blue, u_int *trans)
{
	// Only supports color LUT, not gray LUT

	unsigned int pal;

	// Read only needs to access master palette, not hw palettes.
	pal = master_palette[regno];

	//TBD LCD mode may change LUT from R/G/B order to B/G/R order
	*red = (pal >> 8) & 0xFF00;
	*green = pal & 0xFF00;
	*blue = (pal << 8) & 0xFF00; 
    *trans  = 0;
}

//-----------------------------------------------------------------------------
//	Helper functions for fb driver
//-----------------------------------------------------------------------------

static int
ep93xxfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, 
		   u_int *trans, struct fb_info *info)
{
    if (regno >= mypar.palette_size)
    {
        return 1;
    }
    	
    ep93xxfb_palette_read(regno, red, green, blue, trans);
    
    return 0;
}

static inline u_int
chan_to_field(u_int chan, struct fb_bitfield *bf)
{
    chan &= 0xffff;
    chan >>= 16 - bf->length;
    return chan << bf->offset;
}

static int
ep93xxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
		   u_int trans, struct fb_info *info)
{
    u_int val;
    
    if (regno >= mypar.palette_size)
    {
        return 1;
    }
    
    switch (info->disp->visual)
    {
        case FB_VISUAL_TRUECOLOR:
        {
            if (regno < 16)
            {
                u16 *pal = info->pseudo_palette;
                val  = chan_to_field(red, &info->var.red);
                val |= chan_to_field(green, &info->var.green);
                val |= chan_to_field(blue, &info->var.blue);
                
                pal[regno] = val;
            }
            break;
        }
        case FB_VISUAL_PSEUDOCOLOR:
        {
            ep93xxfb_palette_write(regno, red, green, blue, trans);
            break;
        }
    }
    
    return 0;
}

//TBD Warning: fbmem.c ioctl could result in any of the helper functions being
//TBD called with con=-1, which happens when info->display_fg == NULL,
//TBD which presumably is only during initialization of fbcon.
//TBD Is there any other condition under which con=-1?
//TBD Maybe we do not even need to support it!

static int
ep93xxfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
                  struct fb_info *info)
{
    int err = 0;
    
    //TBD does not expect call with con=-1 if currcon!=-1
    if (con == -1)
    {
        DPRINTK("get_cmap called with con=-1\n");
    }
    
    DPRINTK("mypar.visual=%d\n", mypar.visual);
    if (con == mypar.currcon)
        err = fb_get_cmap(cmap, kspc, ep93xxfb_getcolreg, info);
    else if (fb_display[con].cmap.len)
        fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
    else
        fb_copy_cmap(fb_default_cmap(mypar.palette_size),
                     cmap, kspc ? 0 : 2);
    return err;
}

static int
ep93xxfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, 
		  struct fb_info *info)
{
    int err = 0;
    
    //
    // What kind of request is this???
    //
    if (con == -1)
    {
        DPRINTK("ERROR set_cmap called with con=-1\n");
        return(-1);
    }
    
    DPRINTK("mypar.visual=%d\n", mypar.visual);
    
    if (!fb_display[con].cmap.len)
    {
        err = fb_alloc_cmap(&fb_display[con].cmap, mypar.palette_size, 0);
    }
    if (!err)
    {
        if (con == mypar.currcon)
        {
            err = fb_set_cmap(cmap, kspc, ep93xxfb_setcolreg, info);
        }
        fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
    }
    return err;
}


 
//=============================================================================
// CheckAdjustVar
//=============================================================================
// Check and adjust the video params in 'var'. If a value doesn't fit, round it
// up, if it's too big, return -EINVAL.
//
// Suggestion: Round up in the following order: bits_per_pixel, xres, yres, 
// xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields, 
// horizontal timing, vertical timing.
//
// Note: This is similar to generic fb call sequence
//    (decode_var, set_par, decode_var)
// in that var gets fixed if fixable and error if not.  The translating
// to parameter settings is handled inside activate_var below.
//=============================================================================
static int CheckAdjustVar(struct fb_var_screeninfo *var) 
{
    DPRINTK("Entering\n");
    
    //
    // Just set virtual to actual resolution.
    //
    var->xres_virtual = mypar.xres;
    var->yres_virtual = mypar.yres;

    DPRINTK("check: var->bpp=%d\n", var->bits_per_pixel);
    switch (var->bits_per_pixel)
    {
#ifdef FBCON_HAS_CFB4
        case 4:					//TBD need to adjust var
            break;
#endif
#ifdef FBCON_HAS_CFB8
        case 8:		/* through palette */
        {
            var->red.length    = 4;	//TBD is this used? should be 8?
            //TBD what about offset?
            var->green         = var->red;
            var->blue          = var->red;
            var->transp.length = 0;
            break;
        }
#endif
#ifdef FBCON_HAS_CFB16
        case 16:  	/* RGB 565 */
        {
            var->red.length    = 5;
            var->blue.length   = 5;
            var->green.length  = 6;
            var->transp.length = 0;
            var->red.offset    = 11;
            var->green.offset  = 5;
            var->blue.offset   = 0;
            var->transp.offset = 0;
            break;
        }
#endif
        default:
        {
            DPRINTK("Invalid var->bits_per_pixel = %d\n",var->bits_per_pixel);
            return -EINVAL;
    	}
    }
    return 0;
}

//
//	Entry:	con	valid console number
//			or -1 to indicate current console
//			(note: current console maybe default initially)
//
//		var	pointer to var structure already allocated by caller.
//
//	Action: Set pointed at var structure to fb_display[con].var or current.
//
//	Exit:	none
//
//	Note:	This function called by fb_mem.c, 
static int
ep93xxfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
{
    //TBD I do not expect call with con=-1
    //TBD Maybe I do not really need to support this??
    if (con == -1)
    {
        DPRINTK("get_var called with con=-1\n");
    }
    
    DPRINTK("con=%d\n", con);
    if (con == -1) {
	
        //TBD need to set var with current settings.
        //TBD why can't we just take copy it from info??
        //ep93xxfb_get_par(&par);		
        //ep93xxfb_encode_var(var, &par);
        
        //TBD for now current settings is always equal to
        //TBD default display settings
        //TBD Is this really complete?
        *var = global_disp.var;		// copies structure     	
    } else
        *var = fb_display[con].var;	// copies structure
    
    return 0;
}

/*
 * ep93xxfb_set_var():
 *
 *	Entry:	con 	-1 means set default display
 *			else set fb_display[con]
 *
 *		var	static var structure to define video mode
 *
 *	Action:	Settings are tweaked to get a usable display mode.
 *		Set console display values for specified console,
 *		if console valid.  Else set default display values.
 *
 *	Exit:	Returns error if cannot tweak settings to usable mode.
 */
static int
ep93xxfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
{
    struct display *display;
    int err, chgvar = 0;

    if (con == -1)
    {
        DPRINTK("called with con=-1\n");
    }
    
    // 
    // Display is not passed in, but we alter as side-effect
    //
    if (con >= 0)
    {
        //
        // Display settings for console
        //
        display = &fb_display[con];
    }
    else
    {
        //
        // Set default display settings
        //
        display = &global_disp;
    }

    //
    // Check and adjust var if need be to get usable display mode, else return 
    // error if impossible to adjust
    //
    if ((err = CheckAdjustVar(var)))
    {
        return err;
    }

    //  Update parameters
    DPRINTK("check: var->bpp=%d\n", var->bits_per_pixel);
    switch (var->bits_per_pixel)
    {
#ifdef FBCON_HAS_CFB4
        case 4:
        {
            mypar.visual = FB_VISUAL_PSEUDOCOLOR;
            mypar.palette_size = 16; 
            break;
        }
#endif
#ifdef FBCON_HAS_CFB8
        case 8:
        {
            mypar.visual = FB_VISUAL_PSEUDOCOLOR;
            mypar.palette_size = 256; 
            break;
        }
#endif
#ifdef FBCON_HAS_CFB16
    	case 16:
    	{
            mypar.visual = FB_VISUAL_TRUECOLOR;
            mypar.palette_size = 16; 
            break;
        }
#endif		
        default:
        {
            DPRINTK("ERROR! Bad bpp %d\n", var->bits_per_pixel);
            return -EINVAL;
        }
    }
    
    if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
    {
        return 0;
    }
    else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) && 
             ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
    {
        return -EINVAL;
    }
    
    if (con >= 0) 
    {
        if ((display->var.xres != var->xres) ||
            (display->var.yres != var->yres) ||
            (display->var.xres_virtual != var->xres_virtual) ||
            (display->var.yres_virtual != var->yres_virtual) ||
            (display->var.sync != var->sync)                 ||
            (display->var.bits_per_pixel != var->bits_per_pixel) ||
            (memcmp(&display->var.red, &var->red, sizeof(var->red))) ||
            (memcmp(&display->var.green, &var->green, sizeof(var->green))) ||
            (memcmp(&display->var.blue, &var->blue, sizeof(var->blue)))) 
        {
            chgvar = 1;
        }
    }
    
    //TBD is this complete?
    
    display->var = *var;	// copies structure 
    display->screen_base = mypar.v_screen_base;
    display->visual	= mypar.visual;
    display->type = FB_TYPE_PACKED_PIXELS;
    display->type_aux = 0;
    display->ypanstep = 0;
    display->ywrapstep = 0;
    display->line_length = display->next_line = 
        (var->xres * var->bits_per_pixel) / 8;
    
    display->can_soft_blank	= 1;
    display->inverse = 0;

    switch (display->var.bits_per_pixel)
    {
#ifdef FBCON_HAS_CFB4
        case 4:
        {
            display->dispsw = &fbcon_cfb4;
            break;
        }
#endif
#ifdef FBCON_HAS_CFB8
    	case 8: 
    	{
            display->dispsw = &fbcon_cfb8;
            break;
        }
#endif
#ifdef FBCON_HAS_CFB16
    	case 16:
    	{
            display->dispsw = &fbcon_cfb16;
            display->dispsw_data = info->pseudo_palette;
            break;
    	}
#endif
    	default:
    	{
            display->dispsw = &fbcon_dummy;
            break;
    	}
    }
    
    /* If the console has changed and the console has defined */
    /* a changevar function, call that function. */
    if (chgvar && info && info->changevar)
    {
        info->changevar(con);
    }

    /* If the current console is selected and it's not truecolor, 
     *  update the palette 
     */
    if ((con == mypar.currcon) && (mypar.visual != FB_VISUAL_TRUECOLOR)) 
    {
        struct fb_cmap *cmap;
        
        //TBD what is the juggling of par about?
        //mypar = par;
        if (display->cmap.len)
            cmap = &display->cmap;
        else
            cmap = fb_default_cmap(mypar.palette_size);
        
        //TBD when is cmap.len set?
        fb_set_cmap(cmap, 1, ep93xxfb_setcolreg, info);
    }
    
    //
    // If the current console is selected, activate the new var.
    //
    if (con == mypar.currcon)
    {
        ConfigureRaster(var, info->par);
    }
    
    return 0;
}

//TBD why have this if it does nothing?

static int
ep93xxfb_updatevar(int con, struct fb_info *info)
{
	DPRINTK("entered\n");
	return 0;
}

//TBD what is relation between fix data in info and getting fix here?

static int
ep93xxfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
{
    struct display *display;

    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
    strcpy(fix->id, EP93XX_NAME);
    
    if (con >= 0)
    {
        DPRINTK("Using console specific display for con=%d\n",con);
        display = &fb_display[con];  /* Display settings for console */
    }
    else
        display = &global_disp;      /* Default display settings */
    //TBD global_disp is only valid after set_var called with -1

    fix->smem_start	 = (unsigned long)mypar.p_screen_base;
    fix->smem_len	 = mypar.screen_size;
    fix->type	 = display->type;
    fix->type_aux	 = display->type_aux;
    fix->xpanstep	 = 0;
    fix->ypanstep	 = display->ypanstep;
    fix->ywrapstep	 = display->ywrapstep;
    fix->visual	 = display->visual;
    fix->line_length = display->line_length;
    fix->accel	 = FB_ACCEL_NONE;
    
    return 0;
}


static void
__init ep93xxfb_init_fbinfo(void)
{
    //
    // Set up the display name and default font.
    //
    strcpy(fb_info.modename, TimingValues[DEFAULT_MODE].Name);
    strcpy(fb_info.fontname, "Acorn8x8");

    fb_info.node = -1;
    fb_info.flags = FBINFO_FLAG_DEFAULT;
    fb_info.open = 0;
	
    //
    // Set up initial parameters
    //
    fb_info.var.xres = TimingValues[DEFAULT_MODE].HRes;
    fb_info.var.yres = TimingValues[DEFAULT_MODE].VRes;
	
    // 
    // Virtual display not supported
    //
    fb_info.var.xres_virtual = fb_info.var.xres;
    fb_info.var.yres_virtual = fb_info.var.yres;

#ifdef CONFIG_FB_EP93XX_8BPP
    DPRINTK("Default framebuffer is 8bpp.");
    fb_info.var.bits_per_pixel	= 8;
    fb_info.var.red.length	= 8;				
    fb_info.var.green.length	= 8;
    fb_info.var.blue.length	= 8;

    fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
    mypar.bits_per_pixel = 8;
#endif // CONFIG_FB_EP93XX_8BPP
#ifdef CONFIG_FB_EP93XX_16BPP_565
    DPRINTK("Default framebuffer is 16bpp 565.");
    fb_info.var.bits_per_pixel	= 16;
    fb_info.var.red.length      = 5;
    fb_info.var.blue.length     = 5;
    fb_info.var.green.length    = 6;
    fb_info.var.transp.length   = 0;
    fb_info.var.red.offset      = 11;
    fb_info.var.green.offset    = 5;
    fb_info.var.blue.offset     = 0;
    fb_info.var.transp.offset   = 0;
    fb_info.fix.visual	= FB_VISUAL_TRUECOLOR;
    fb_info.pseudo_palette = mypar.palette;

    mypar.bits_per_pixel = 16;
#endif // CONFIG_FB_EP93XX_16BPP
	
    //TBD wierd to have a flag which has meaning when call set_var
    //TBD be copied into fb_info? this is more a parameter than
    //TBD some state to keep track of.
    fb_info.var.activate = FB_ACTIVATE_NOW;
    fb_info.var.height	= -1;			//TBD unknown
    fb_info.var.width	= -1;			//TBD unknown

    //
    // Set the default timing information.
    // 
    fb_info.var.left_margin =  TimingValues[DEFAULT_MODE].HFrontPorch;
    fb_info.var.right_margin = TimingValues[DEFAULT_MODE].HBackPorch;
    fb_info.var.upper_margin = TimingValues[DEFAULT_MODE].VFrontPorch;
    fb_info.var.lower_margin = TimingValues[DEFAULT_MODE].VBackPorch;
    fb_info.var.hsync_len = TimingValues[DEFAULT_MODE].HSyncWidth;
    fb_info.var.vsync_len = TimingValues[DEFAULT_MODE].VSyncWidth;
    fb_info.var.sync = 0;
    fb_info.var.vmode = FB_VMODE_NONINTERLACED;

    fb_info.fix.smem_start	= (__u32) mypar.p_screen_base;	// physical
    fb_info.fix.smem_len	= (__u32) mypar.screen_size;	//TBD in bytes
    fb_info.fix.type	= FB_TYPE_PACKED_PIXELS;
    fb_info.fix.line_length	= fb_info.var.xres_virtual *
        fb_info.var.bits_per_pixel/8;	// stride in bytes

    fb_info.monspecs            = monspecs; // copies structure
    fb_info.fbops		= &ep93xxfb_ops;
    fb_info.screen_base 	= mypar.v_screen_base;
    
    fb_info.disp		= &global_disp;
    fb_info.changevar	        = NULL;
    fb_info.switch_con	        = ep93xxfb_switch;
    fb_info.updatevar	        = ep93xxfb_updatevar;	//TBD why not NULL?
    fb_info.blank		= ep93xxfb_blank;

    //
    // Save the current TimingValues for later use in Configuration routine.
    //
    fb_info.par = &TimingValues[DEFAULT_MODE];
    
    mypar.screen_size	= FB_MAX_MEM_SIZE; // not a particular mode
    mypar.palette_size	= MAX_PALETTE_NUM_ENTRIES; // not a particular mode
    mypar.montype = 1;	//TBD why not 0 since single entry?
    mypar.currcon = 0;		//TBD is this right?

    // these are also set by set_var
    mypar.visual = FB_VISUAL_PSEUDOCOLOR;
}

//=============================================================================
// MapFramebuffer
//=============================================================================
// Allocates the DRAM memory for the frame buffer.  This buffer is remapped 
// into a non-cached, non-buffered, memory region to allow pixel writes to 
// occur without flushing the cache.  Once this area is remapped, all virtual 
// memory access to the video memory should occur at the new region.
//=============================================================================
static int __init MapFramebuffer(void)
{
    u_int required_pages;
    u_int extra_pages;
    u_int order;
    struct page *page;
    char *allocated_region;

    //
    // Make sure that we don't allocate the buffer twice.
    //
    if (mypar.p_screen_base != NULL)
    {
        return -EINVAL;
    }

    //
    // Find order required to allocate enough memory for framebuffer
    //
    required_pages = FB_MAPPED_MEM_SIZE >> PAGE_SHIFT;

    for (order = 0 ; required_pages >> order ; order++) {;}

    extra_pages = (1 << order) - required_pages;

    if ((allocated_region = 
        (char *)__get_free_pages(GFP_KERNEL, order)) == NULL)
    {
        return -ENOMEM;
    }

    mypar.v_screen_base = (u_char *)allocated_region +
        (extra_pages << PAGE_SHIFT); 
    mypar.p_screen_base =
        (u_char *)__virt_to_phys((u_long)mypar.v_screen_base);

    //
    // Free all pages that we don't need but were given to us because 
    // __get_free_pages() works on powers of 2.
    //
    for (;extra_pages;extra_pages--)
    {
        free_page((u_int)allocated_region + ((extra_pages-1) << PAGE_SHIFT));
    }

    //
    // Set reserved flag for fb memory to allow it to be remapped into user 
    // space by the common fbmem driver using remap_page_range().
    //
    for(page = virt_to_page((u_long)mypar.v_screen_base);
        page < virt_to_page((u_long)mypar.v_screen_base + FB_MAPPED_MEM_SIZE);
        page++)
    {
	mem_map_reserve(page);
    }

    //
    // Remap the fb memory to a non-buffered, non-cached region 
    //
    mypar.v_screen_base = (u_char *)__ioremap((u_long)mypar.p_screen_base,
					     FB_MAPPED_MEM_SIZE,
					     L_PTE_PRESENT  |
					     L_PTE_YOUNG    |
					     L_PTE_DIRTY    |
					     L_PTE_WRITE);

    if(mypar.v_screen_base == NULL)
    {
        DPRINTK("Error __ioremap failed.\n");
        return(-EINVAL);
    }

    DPRINTK("p_screen_base = 0x%08lx\n",(u_long)mypar.p_screen_base);
    DPRINTK("v_screen_base = 0x%08lx\n",(u_long)mypar.v_screen_base);

    return (0);
}

//=============================================================================
// ConfigureRaster():
//=============================================================================
// Configures LCD Controller based on entries in var parameter.
//
// Note: palette is setup elsewhere.
//=============================================================================
static int
ConfigureRaster(struct fb_var_screeninfo *var, 
		DisplayTimingValues *pTimingValues)
{						       
    u_long	flags, ulVIDEOATTRIBS;
    unsigned int total, uiDevCfg, uiBMAR;
    
#ifdef CONFIG_FB_LCD_EP93XX
    unsigned int uiPADDR, uiPADR;
#endif    

    DPRINTK("activating\n");

    /* Disable interrupts and save status */
    local_irq_save(flags);		// disable interrupts and save flags

    DPRINTK("Configuring %dx%dx%dbpp\n",var->xres, var->yres, 
	    var->bits_per_pixel);

    // Setup video mode according to var values.
    // Remember to unlock total, startstop, and linecarry registers.

    // Disable the video and outputs while changing the video mode.
    outl( 0, VIDEOATTRIBS );

    if (pTimingValues->RasterConfigure)
    {
        pTimingValues->RasterConfigure(pTimingValues);
    }
    else
    {
        total = var->vsync_len +  var->upper_margin + var->yres + 
	    var->lower_margin - 1;

	RasterSetLocked(VLINESTOTAL, total);

	RasterSetLocked(VSYNCSTRTSTOP, total - var->lower_margin +
            ((total - (var->lower_margin + var->vsync_len)) << 16));

	RasterSetLocked(VACTIVESTRTSTOP, var->yres-1 + (total << 16)); 
	
	// Reverse start/stop since N_VBLANK output
	// unblanked same as active	
        RasterSetLocked(VBLANKSTRTSTOP, var->yres-1 + (total << 16));

        RasterSetLocked(VCLKSTRTSTOP, total + (total << 16));

	//
	// Now configure the Horizontal timings.
	//
	total = var->hsync_len + var->left_margin + var->xres +
	    var->right_margin - 1;
	
	RasterSetLocked(HCLKSTOTAL,total);

	RasterSetLocked(HSYNCSTRTSTOP, total + 
			((total - var->hsync_len) << 16));

	RasterSetLocked(HACTIVESTRTSTOP, total - var->hsync_len -
			var->left_margin + ((var->right_margin - 1) << 16)); 
	
	RasterSetLocked(HBLANKSTRTSTOP, total - var->hsync_len -
			var->left_margin + ((var->right_margin - 1) << 16));
	
	RasterSetLocked(HCLKSTRTSTOP, total + (total << 16));

	RasterSetLocked(LINECARRY, 0);
	
	// 
	// Now that everything else is setup, enable video and outputs
	//
	// Invert Clock, enable outputs and VSYNC,HSYNC,BLANK active low
	// Invert means pixel output data changes on falling edge of clock, to
	// provide setup time for the video DAC to latch data on rising edge.
	//
	RasterSetLocked(VIDEOATTRIBS, VIDEOATTRIBS_INVCLK | 
			VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_SYNCEN | 
			VIDEOATTRIBS_DATAEN);
    }

    //
    // Configure the Frame Buffer size.
    //
    outl( (unsigned int)mypar.p_screen_base, VIDSCRNPAGE );
    outl( var->yres, SCRNLINES );

    //
    // Set up the Line size.
    //
    total = var->xres * var->bits_per_pixel / 32;
    outl( (total - 1), LINELENGTH );
    outl( total, VLINESTEP );


    if(var->bits_per_pixel == 8)
    {
        //
        // 8bpp framebuffer, color LUT and convert to 18bpp on pixel bus.
        //
    	outl( (0x8 | PIXELMODE_P_8BPP | PIXELMODE_C_LUT), PIXELMODE );
    }
    else if(var->bits_per_pixel == 16)
    {
        //
        // 16bpp framebuffer and convert to 18bpp on pixel bus.
        //
        outl( 0x8 | PIXELMODE_P_16BPP | ((PIXELMODE_C_565)<<(PIXELMODE_C_SHIFT)),
              PIXELMODE );
    }

    //
    // TODO this is more HACKING!
    // TODO add configurable framebuffer location and side-band option.
    //
	uiDevCfg = inl(SYSCON_DEVCFG);
    SysconSetLocked(SYSCON_DEVCFG, (uiDevCfg | SYSCON_DEVCFG_RasOnP3) );  

    //
    // TODO add code to calculate this not just slam it in there.
    //
    SysconSetLocked(SYSCON_VIDDIV, pTimingValues->VDiv);  

    //
    // Make sure that Raster has the highest bus priority.
    //
	uiBMAR = inl(SYSCON_BMAR);
	outl( (uiBMAR | 1), SYSCON_BMAR );

    //
    // Enable Raster and make sure that frame buffer chip select is specfied 
    // since this field is not readable.
    //
    ulVIDEOATTRIBS = inl(VIDEOATTRIBS);
    RasterSetLocked(VIDEOATTRIBS, ulVIDEOATTRIBS | VIDEOATTRIBS_EN | 
		    (3 << VIDEOATTRIBS_SDSEL_SHIFT));
            
            
    //
    // Turn on the LCD.
    //
#ifdef CONFIG_FB_LCD_EP93XX
    uiPADDR = inl(GPIO_PADDR) | 0x2;
    outl( uiPADDR, GPIO_PADDR );
  
    uiPADR = inl(GPIO_PADR) | 0x2;
    outl( uiPADR, GPIO_PADR );
#endif        
        
    //
    // Restore interrupt status
    //
    local_irq_restore(flags);
    return 0;
}


/*
 * ep93xxfb_blank():
 *	TBD not implemented
 */
static void
ep93xxfb_blank(int blank, struct fb_info *info)
{
}

//=============================================================================
// ep93xxfb_switch():       
//=============================================================================
// Change to the specified console.  Palette and video mode are changed to the 
// console's stored parameters. 
// 
//=============================================================================
static int ep93xxfb_switch(int con, struct fb_info *info)
{
    DPRINTK("con=%d info->modename=%s\n", con, info->modename);
    if (mypar.visual != FB_VISUAL_TRUECOLOR)
    {
        struct fb_cmap *cmap;
        if (mypar.currcon >= 0)
        {
            // Get the colormap for the selected console 
            cmap = &fb_display[mypar.currcon].cmap;
            
            if (cmap->len)
            {
                fb_get_cmap(cmap, 1, ep93xxfb_getcolreg, info);
            }
        }
    }
    
    mypar.currcon = con;
    fb_display[con].var.activate = FB_ACTIVATE_NOW;
    DPRINTK("fb_display[%d].var.activate=%x\n", con,
            fb_display[con].var.activate);
    
    ep93xxfb_set_var(&fb_display[con].var, con, info);
    
    return 0;
}

//=============================================================================
// ep93xxfb_init(void)
//=============================================================================
//=============================================================================
int __init ep93xxfb_init(void)
{
    int ret;
    
    DPRINTK("Entering: ep93xxfb_init.");
    //
    // Allocate and map video buffer
    //
    if ((ret = MapFramebuffer()) != 0)
    {
        DPRINTK("Leaving: ep93xxfb_init FAILED %08x.", ret);
        return ret;
    }
    
    //
    // Initialize the Framebuffer Info structure.
    //
    ep93xxfb_init_fbinfo();
    
    // 
    // Check and adjust var and set it active.
    // TBD is this okay to pass in fb_info.var or
    // TBD does it cause some aliasing problem? 
    // TBD can set var fail?
    //
    ep93xxfb_set_var(&fb_info.var, -1, &fb_info);
    
    register_framebuffer(&fb_info);
    
    DPRINTK("Leaving: ep93xxfb_init.");
    
    return 0;
}


//=============================================================================
// Conexant_CX25871
//=============================================================================
// Timing and screen configuration for the Conexant CX25871.
//=============================================================================
static int Conexant_CX25871(DisplayTimingValues *pTimingValues)
{
    unsigned int uiTemp;
	
	//
    // By name it Initializes the CX25871 to 640x480 NTSC.
    //
    InitializeCX25871For640x480NTSC();

    //
    // Disable the Raster Engine.
    //
    RasterSetLocked(VIDEOATTRIBS, 0);

    //
    // Configure the Raster to use external clock for pixel clock.
    //
	uiTemp = inl(SYSCON_DEVCFG);
    SysconSetLocked(SYSCON_DEVCFG, (uiTemp | SYSCON_DEVCFG_EXVC) );  

    //
    // Configure the Vertical Timing.
    // 
    RasterSetLocked(VLINESTOTAL, 0x0257);
    RasterSetLocked(VSYNCSTRTSTOP, 0x01FF022C); // was 024c
    RasterSetLocked(VBLANKSTRTSTOP, 0x000001E0);
    RasterSetLocked(VACTIVESTRTSTOP, 0x000001E0);
    RasterSetLocked(VCLKSTRTSTOP,0x07FF01E0);

    //
    // Configure the Horizontal Timing.
    //
    RasterSetLocked(HCLKSTOTAL,0x30F);
    RasterSetLocked(HSYNCSTRTSTOP, 0x02c0030F);
    RasterSetLocked(HBLANKSTRTSTOP,0x00000280);
    RasterSetLocked(HACTIVESTRTSTOP,0x00000280);
    RasterSetLocked(HCLKSTRTSTOP, 0x07ff0280);

    RasterSetLocked(LINECARRY, 0);

    //
    // Enable the Data and Sync outputs only.
    //
    RasterSetLocked(VIDEOATTRIBS, VIDEOATTRIBS_DATAEN | VIDEOATTRIBS_SYNCEN);
    
    return(0);
}

//=============================================================================
// NecLCD
//=============================================================================
// Timing and screen configuration for the Conexant CX25871.
//=============================================================================
#if 0
static int NecLCD(DisplayTimingValues *pTimingValues)
{
    //
    // Disable the Raster Engine.
    //
    RasterSetLocked(VIDEOATTRIBS, 0);

    //
    // Configure the Vertical Timing.
    // 
    RasterSetLocked(VLINESTOTAL, 0x020c);
    RasterSetLocked(VSYNCSTRTSTOP, 0x01fe0200); // was 024c
    RasterSetLocked(VBLANKSTRTSTOP, 0x0000000);
    RasterSetLocked(VACTIVESTRTSTOP, 0x020c01df);
    RasterSetLocked(VCLKSTRTSTOP,0x020c020c);

    //
    // Configure the Horizontal Timing.
    //
    RasterSetLocked(HCLKSTOTAL,0x323);
    RasterSetLocked(HSYNCSTRTSTOP, 0x02c00321);
    RasterSetLocked(HBLANKSTRTSTOP,0x00000);
    RasterSetLocked(HACTIVESTRTSTOP,0x00100290);
    RasterSetLocked(HCLKSTRTSTOP, 0x03230323);

    RasterSetLocked(LINECARRY, 0);

    //
    // Enable the Data and Sync outputs only.
    //
    RasterSetLocked(VIDEOATTRIBS, VIDEOATTRIBS_INVCLK | 
			VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_SYNCEN | 
			VIDEOATTRIBS_DATAEN);
    
    return(0);
}
#endif //0

//=============================================================================
// PhilipsLCD
//=============================================================================
// Timing and screen configuration for the Conexant CX25871.
//=============================================================================
static int PhilipsLCD(DisplayTimingValues *pTimingValues)
{
    //
    // Disable the Raster Engine.
    //
    RasterSetLocked(VIDEOATTRIBS, 0);

    //
    // Configure the Vertical Timing.
    // 
    RasterSetLocked(VLINESTOTAL,     0x0000020b);
    RasterSetLocked(VSYNCSTRTSTOP,   0x01ea01ec);
    RasterSetLocked(VBLANKSTRTSTOP,  0x020b01df);
    RasterSetLocked(VACTIVESTRTSTOP, 0x020b01df);
    RasterSetLocked(VCLKSTRTSTOP,    0x020b020b);

    //
    // Configure the Horizontal Timing.
    //
    RasterSetLocked(HCLKSTOTAL,      0x0000031f);
    RasterSetLocked(HSYNCSTRTSTOP,   0x02bf031f);
    RasterSetLocked(HBLANKSTRTSTOP,  0x002f02af);
    RasterSetLocked(HACTIVESTRTSTOP, 0x002f02af);
    RasterSetLocked(HCLKSTRTSTOP,    0x031f031f);

    RasterSetLocked(LINECARRY, 0);

    //
    // Enable the Data and Sync outputs only.
    // According the the LCD spec we should also be setting INVCLK_EN, but this
    // makes the data on the screen look incorrect.
    //
    RasterSetLocked(VIDEOATTRIBS, VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_SYNCEN | 
                    VIDEOATTRIBS_DATAEN);
    
    return(0);
}

//
// A good clock rate for EE is 10KHz.  That's a period of 10 uSec.
// So we have our EE_DELAY that times half a clock period set to 5 uSec.
// Note that for Kittyhawk 100Khz is to fast.
#define EE_DELAY_USEC       100

//
// The number of time we should read the two wire device before giving
// up.
//
#define EE_READ_TIMEOUT     100

//
// CS25871 Device Address.
//
#define CX25871_DEV_ADDRESS                     0x88

//
// Register 0x32
//
#define CX25871_REGx32_AUTO_CHK                 0x80
#define CX25871_REGx32_DRVS_MASK                0x60
#define CX25871_REGx32_DRVS_SHIFT               5
#define CX25871_REGx32_SETUP_HOLD               0x10
#define CX25871_REGx32_INMODE_                  0x08
#define CX25871_REGx32_DATDLY_RE                0x04
#define CX25871_REGx32_OFFSET_RGB               0x02
#define CX25871_REGx32_CSC_SEL                  0x01

//
// Register 0xBA
//
#define CX25871_REGxBA_SRESET                   0x80
#define CX25871_REGxBA_CHECK_STAT               0x40
#define CX25871_REGxBA_SLAVER                   0x20
#define CX25871_REGxBA_DACOFF                   0x10
#define CX25871_REGxBA_DACDISD                  0x08
#define CX25871_REGxBA_DACDISC                  0x04
#define CX25871_REGxBA_DACDISB                  0x02
#define CX25871_REGxBA_DACDISA                  0x01

//
// Register 0xC4
//
#define CX25871_REGxC4_ESTATUS_MASK             0xC0
#define CX25871_REGxC4_ESTATUS_SHIFT            6
#define CX25871_REGxC4_ECCF2                    0x20
#define CX25871_REGxC4_ECCF1                    0x10
#define CX25871_REGxC4_ECCGATE                  0x08
#define CX25871_REGxC4_ECBAR                    0x04
#define CX25871_REGxC4_DCHROMA                  0x02
#define CX25871_REGxC4_EN_OUT                   0x01
                                                

//
// Register 0xC6
//
#define CX25871_REGxC6_EN_BLANKO                0x80
#define CX25871_REGxC6_EN_DOT                   0x40
#define CX25871_REGxC6_FIELDI                   0x20
#define CX25871_REGxC6_VSYNCI                   0x10
#define CX25871_REGxC6_HSYNCI                   0x08
#define CX25871_REGxC6_INMODE_MASK              0x07
#define CX25871_REGxC6_INMODE_SHIFT             0

#define GPIOG_EEDAT 2
#define GPIOG_EECLK 1
static int WriteCX25871Reg(unsigned char ucRegAddr, unsigned char ucRegValue);

//****************************************************************************
// InitializeCX25871640x480
//****************************************************************************
// Initialize the CX25871 for 640x480 NTSC output.
// 
//
void InitializeCX25871For640x480NTSC(void)
{
    //
    // Perform auto-configuration
    //
    WriteCX25871Reg(0xB8, 0);
    mdelay(1000);

    //                
    // After auto-configuration, setup pseudo-master mode BUT with EN_BLANKO bit cleared
    //
    WriteCX25871Reg(0xBA, CX25871_REGxBA_SLAVER | CX25871_REGxBA_DACOFF);
    WriteCX25871Reg(0xC6, (CX25871_REGxC6_INMODE_MASK & 0x3));
    WriteCX25871Reg(0xC4, CX25871_REGxC4_EN_OUT);
    WriteCX25871Reg(0x32, 0);
    WriteCX25871Reg(0xBA, CX25871_REGxBA_SLAVER );
}

//****************************************************************************
// WriteCX25871Reg
//****************************************************************************
// ucRegAddr    - CS4228 Register Address.
// usRegValue   - CS4228 Register Value.
//
// Return     0 - Success
//            1 - Failure
//

static int WriteCX25871Reg(unsigned char ucRegAddr, unsigned char ucRegValue)
{

    unsigned long uiVal, uiDDR;
    unsigned char ucData, ucIdx, ucBit;
    unsigned long ulTimeout;

    //FUNC_I2S
    //(
    //    (
    //        L"WriteCX25871Reg: Reg = 0x%02x, Value = 0x%02x\n", 
    //        ucRegAddr, 
    //        ucRegValue
    //    )
    //);

    //
    // Read the current value of the GPIO data and data direction registers.
    //
    uiVal = inl(GPIO_PGDR);
    uiDDR = inl(GPIO_PGDDR);

    //
    // If the GPIO pins have not been configured since reset, the data 
    // and clock lines will be set as inputs and with data value of 0.
    // External pullup resisters are pulling them high.
    // Set them both high before configuring them as outputs.
    //
    uiVal |= (GPIOG_EEDAT | GPIOG_EECLK);
    outl( uiVal, GPIO_PGDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    //
    // Configure the EE data and clock lines as outputs.
    //
    uiDDR |= (GPIOG_EEDAT | GPIOG_EECLK);
    outl( uiDDR, GPIO_PGDDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    //
    // Drive the EE data line low.  Since the EE clock line is currently
    // high, this is the start condition.
    //
    uiVal &= ~GPIOG_EEDAT;
    outl( uiVal, GPIO_PGDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    //
    // Drive the EE clock line low.
    //
    uiVal &= ~GPIOG_EECLK;
    outl( uiVal, GPIO_PGDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    //
    // Loop through the three bytes which we will send.
    //
    for(ucIdx = 0; ucIdx < 3; ucIdx++)
    {
        //
        // Get the appropriate byte based on the current loop iteration.
        //
        if(ucIdx == 0)
        {
            //
            // Since this is a write operation, we set d0 of the address
            // which is the r/w bit.
            //
            ucData = (unsigned char)CX25871_DEV_ADDRESS;
        }
        else if(ucIdx == 1)
        {
            ucData = ucRegAddr;
        }
        else
        {
            ucData = ucRegValue;
        }

        //
        // Loop through the 8 bits in this byte.
        //
        for(ucBit = 0; ucBit < 8; ucBit++)
        {
            //
            // Set the EE data line to correspond to the most significant bit
            // of the data byte.
            // 
            if(ucData & 0x80)
            {
                uiVal |= GPIOG_EEDAT;
            }
            else
            {
                uiVal &= ~GPIOG_EEDAT;
            }
            outl( uiVal, GPIO_PGDR );

            //
            // Delay to meet the EE Interface timing specification.
            //
            udelay( EE_DELAY_USEC );

            //
            // Drive the EE clock line high.
            //
            outl( (uiVal | GPIOG_EECLK), GPIO_PGDR );

            //
            // Delay to meet the EE Interface timing specification.
            //
            udelay( EE_DELAY_USEC );

            //
            // Drive the EE clock line low.
            //
            outl( uiVal, GPIO_PGDR );

            //
            // Delay to meet the EE Interface timing specification.
            //
            udelay( EE_DELAY_USEC );

            //
            // Shift the data byte to the left by one bit.
            //
            ucData <<= 1;
        }

        //
        // We've sent the eight bits in this data byte, so we need to wait for
        // the acknowledge from the target.  Reconfigure the EE data line as
        // an input so we can read the acknowledge from the device.
        //
        uiDDR &= ~GPIOG_EEDAT;
        outl( uiDDR, GPIO_PGDDR );

        //
        // Delay to meet the EE Interface timing specification.
        //
        udelay( EE_DELAY_USEC );
    
        //
        // Drive the EE clock line high.
        //
        outl( (uiVal | GPIOG_EECLK), GPIO_PGDR );

        //
        // Delay to meet the EE Interface timing specification.
        //
        udelay( EE_DELAY_USEC );

        //
        // Wait until the EE data line is pulled low by the target device.
        //
        ulTimeout = 0;        
        while( inl(GPIO_PGDR) & GPIOG_EEDAT )
        {
            udelay( EE_DELAY_USEC );
            ulTimeout++;
            if(ulTimeout > EE_READ_TIMEOUT )
            {
                //ERRMSG
                //((
                //   L"I2SCodec::WriteCodecReg Write timeout on register 0x%02x\r\n",  
                //    (ULONG)ucRegAddr 
                //));
                return 1;
            }
        }

        //
        // Drive the EE clock line low.
        //
        outl( uiVal, GPIO_PGDR );

        //
        // Delay to meet the EE Interface timing specification.
        //
        udelay( EE_DELAY_USEC );
    
        //
        // Reconfigure the EE data line as an output.
        //
        uiDDR |= GPIOG_EEDAT;
        outl( uiDDR, GPIO_PGDDR );

        //
        // Delay to meet the EE Interface timing specification.
        //
        udelay( EE_DELAY_USEC );
    }

    //
    // Drive the EE data line low.
    //
    uiVal &= ~GPIOG_EEDAT;
    outl( uiVal, GPIO_PGDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    //
    // Drive the EE clock line high.
    //
    uiVal |= GPIOG_EECLK;
    outl( uiVal, GPIO_PGDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    //
    // Drive the EE data line high.  Since the EE clock line is currently
    // high, this is the stop condition.
    //
    uiVal |= GPIOG_EEDAT;
    outl( uiVal, GPIO_PGDR );

    //
    // Delay to meet the EE Interface timing specification.
    //
    udelay( EE_DELAY_USEC );

    return 0;
}

#if 0	//TBD future - multiple mode support
int __init ep93xxfb_setup(char *options)
{
    char *this_opt;
    
    if (!options || !*options)
        return 0;

    for (this_opt = strtok(options, ","); this_opt;
         this_opt = strtok(NULL, ",")) {
        
        if (!strncmp(this_opt, "lccr0:", 6))
	    lcd_shadow.lccr0 = simple_strtoul(this_opt+6, NULL, 0);
        if (!strncmp(this_opt, "lccr1:", 6)) {
	    lcd_shadow.lccr1 = simple_strtoul(this_opt+6, NULL, 0);
	    current_par.max_xres = (lcd_shadow.lccr1 & 0x3ff) + 16;
        }
        if (!strncmp(this_opt, "lccr2:", 6)) {
	    lcd_shadow.lccr2 = simple_strtoul(this_opt+6, NULL, 0);
	    current_par.max_yres = (lcd_shadow.lccr0 & LCCR0_SDS) ? 
                ((lcd_shadow.lccr2 & 0x3ff) + 1) * 2 :
                ((lcd_shadow.lccr2 & 0x3ff) + 1);
        }
        if (!strncmp(this_opt, "lccr3:", 6))
	    lcd_shadow.lccr3 = simple_strtoul(this_opt+6, NULL, 0);
    }
    return 0;
}
#endif
