/*
 * linux/drivers/video/w100fb.c
 *
 * Frame Buffer Device for ATI Imageon w100 (Wallaby)
 *
 * Copyright (C) 2002, ATI Corp.
 * Copyright (C) 2004-2005 Richard Purdie
 *
 * Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <video/w100fb.h>
#include "w100fb.h"

/*
 * Prototypes
 */
static void w100fb_save_buffer(void);
static void w100fb_clear_buffer(void);
static void w100fb_restore_buffer(void);
static void w100fb_clear_screen(u32 mode, long int offset);
static void w100_resume(void);
static void w100_suspend(u32 mode);
static void w100_init_qvga_rotation(u16 deg);
static void w100_init_vga_rotation(u16 deg);
static void w100_vsync(void);
static void w100_init_sharp_lcd(u32 mode);
static void w100_pwm_setup(void);
static void w100_InitExtMem(u32 mode);
static void w100_hw_init(void);
static u16 w100_set_fastsysclk(u16 Freq);

static void lcdtg_hw_init(u32 mode);
static void lcdtg_lcd_change(u32 mode);
static void lcdtg_resume(void);
static void lcdtg_suspend(void);


/* Register offsets & lengths */
#define REMAPPED_FB_LEN   0x15ffff

#define BITS_PER_PIXEL    16

/* Pseudo palette size */
#define MAX_PALETTES      16

/* for resolution change */
#define LCD_MODE_INIT (-1)
#define LCD_MODE_480    0
#define LCD_MODE_320    1
#define LCD_MODE_240    2
#define LCD_MODE_640    3

#define LCD_SHARP_QVGA 0
#define LCD_SHARP_VGA  1

#define LCD_MODE_PORTRAIT	0
#define LCD_MODE_LANDSCAPE	1

#define W100_SUSPEND_EXTMEM 0
#define W100_SUSPEND_ALL    1

/* General frame buffer data structures */
struct w100fb_par {
	u32 xres;
	u32 yres;
	int fastsysclk_mode;
	int lcdMode;
	int rotation_flag;
	int blanking_flag;
	int comadj;
	int phadadj;
};

static struct w100fb_par *current_par;

/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
static void *remapped_base;
static void *remapped_regs;
static void *remapped_fbuf;

/* External Function */
static void(*w100fb_ssp_send)(u8 adrs, u8 data);

/*
 * Sysfs functions
 */

static ssize_t rotation_show(struct device *dev, char *buf)
{
	struct fb_info *info = dev_get_drvdata(dev);
	struct w100fb_par *par=info->par;

	return sprintf(buf, "%d\n",par->rotation_flag);
}

static ssize_t rotation_store(struct device *dev, const char *buf, size_t count)
{
	unsigned int rotate;
	struct fb_info *info = dev_get_drvdata(dev);
	struct w100fb_par *par=info->par;

	rotate = simple_strtoul(buf, NULL, 10);

	if (rotate > 0) par->rotation_flag = 1;
	else par->rotation_flag = 0;

	if (par->lcdMode == LCD_MODE_320)
		w100_init_qvga_rotation(par->rotation_flag ? 270 : 90);
	else if (par->lcdMode == LCD_MODE_240)
		w100_init_qvga_rotation(par->rotation_flag ? 180 : 0);
	else if (par->lcdMode == LCD_MODE_640)
		w100_init_vga_rotation(par->rotation_flag ? 270 : 90);
	else if (par->lcdMode == LCD_MODE_480)
		w100_init_vga_rotation(par->rotation_flag ? 180 : 0);

	return count;
}

static DEVICE_ATTR(rotation, 0644, rotation_show, rotation_store);

static ssize_t w100fb_reg_read(struct device *dev, const char *buf, size_t count)
{
	unsigned long param;
	unsigned long regs;
	regs = simple_strtoul(buf, NULL, 16);
	param = readl(remapped_regs + regs);
	printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
	return count;
}

static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);

static ssize_t w100fb_reg_write(struct device *dev, const char *buf, size_t count)
{
	unsigned long regs;
	unsigned long param;
	sscanf(buf, "%lx %lx", &regs, &param);

	if (regs <= 0x2000) {
		printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
		writel(param, remapped_regs + regs);
	}

	return count;
}

static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);


static ssize_t fastsysclk_show(struct device *dev, char *buf)
{
	struct fb_info *info = dev_get_drvdata(dev);
	struct w100fb_par *par=info->par;

	return sprintf(buf, "%d\n",par->fastsysclk_mode);
}

static ssize_t fastsysclk_store(struct device *dev, const char *buf, size_t count)
{
	int param;
	struct fb_info *info = dev_get_drvdata(dev);
	struct w100fb_par *par=info->par;

	param = simple_strtoul(buf, NULL, 10);

	if (param == 75) {
		printk("Set fastsysclk %d\n", param);
		par->fastsysclk_mode = param;
		w100_set_fastsysclk(par->fastsysclk_mode);
	} else if (param == 100) {
		printk("Set fastsysclk %d\n", param);
		par->fastsysclk_mode = param;
		w100_set_fastsysclk(par->fastsysclk_mode);
	}
	return count;
}

static DEVICE_ATTR(fastsysclk, 0644, fastsysclk_show, fastsysclk_store);

/*
 * The touchscreen on this device needs certain information
 * from the video driver to function correctly. We export it here.
 */
int w100fb_get_xres(void) {
	return current_par->xres;
}

int w100fb_get_blanking(void) {
	return current_par->blanking_flag;
}

int w100fb_get_fastsysclk(void) {
	return current_par->fastsysclk_mode;
}
EXPORT_SYMBOL(w100fb_get_xres);
EXPORT_SYMBOL(w100fb_get_blanking);
EXPORT_SYMBOL(w100fb_get_fastsysclk);


/*
 * Set a palette value from rgb components
 */
static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
			     u_int trans, struct fb_info *info)
{
	unsigned int val;
	int ret = 1;

	/*
	 * If greyscale is true, then we convert the RGB value
	 * to greyscale no matter what visual we are using.
	 */
	if (info->var.grayscale)
		red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;

	/*
	 * 16-bit True Colour.  We encode the RGB value
	 * according to the RGB bitfield information.
	 */
	if (regno < MAX_PALETTES) {

		u32 *pal = info->pseudo_palette;

		val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
		pal[regno] = val;
		ret = 0;
	}
	return ret;
}


/*
 * Blank the display based on value in blank_mode
 */
static int w100fb_blank(int blank_mode, struct fb_info *info)
{
	struct w100fb_par *par;
	par=info->par;

	switch(blank_mode) {

 	case FB_BLANK_NORMAL: /* Normal blanking */
	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
 	case FB_BLANK_POWERDOWN: /* Poweroff */
  		if (par->blanking_flag == 0) {
			w100fb_save_buffer();
			lcdtg_suspend();
			par->blanking_flag = 1;
  		}
  		break;

 	case FB_BLANK_UNBLANK: /* Unblanking */
  		if (par->blanking_flag != 0) {
			w100fb_restore_buffer();
			lcdtg_resume();
			par->blanking_flag = 0;
  		}
  		break;
 	}
	return 0;
}

/*
 *  Change the resolution by calling the appropriate hardware functions
 */
static void w100fb_changeres(int rotate_mode, u32 mode)
{
	u16 rotation=0;

	switch(rotate_mode) {
	case LCD_MODE_LANDSCAPE:
		rotation=(current_par->rotation_flag ? 270 : 90);
		break;
	case LCD_MODE_PORTRAIT:
		rotation=(current_par->rotation_flag ? 180 : 0);
		break;
	}

	w100_pwm_setup();
	switch(mode) {
	case LCD_SHARP_QVGA:
		w100_vsync();
		w100_suspend(W100_SUSPEND_EXTMEM);
		w100_init_sharp_lcd(LCD_SHARP_QVGA);
		w100_init_qvga_rotation(rotation);
		w100_InitExtMem(LCD_SHARP_QVGA);
		w100fb_clear_screen(LCD_SHARP_QVGA, 0);
		lcdtg_lcd_change(LCD_SHARP_QVGA);
		break;
	case LCD_SHARP_VGA:
		w100fb_clear_screen(LCD_SHARP_QVGA, 0);
		writel(0xBFFFA000, remapped_regs + mmMC_EXT_MEM_LOCATION);
		w100_InitExtMem(LCD_SHARP_VGA);
		w100fb_clear_screen(LCD_SHARP_VGA, 0x200000);
		w100_vsync();
		w100_init_sharp_lcd(LCD_SHARP_VGA);
		if (rotation != 0)
			w100_init_vga_rotation(rotation);
		lcdtg_lcd_change(LCD_SHARP_VGA);
		break;
	}
}

/*
 * Set up the display for the fb subsystem
 */
static void w100fb_activate_var(struct fb_info *info)
{
	u32 temp32;
	struct w100fb_par *par=info->par;
	struct fb_var_screeninfo *var = &info->var;

	/* Set the hardware to 565 */
	temp32 = readl(remapped_regs + mmDISP_DEBUG2);
	temp32 &= 0xff7fffff;
	temp32 |= 0x00800000;
	writel(temp32, remapped_regs + mmDISP_DEBUG2);

	if (par->lcdMode == LCD_MODE_INIT) {
		w100_init_sharp_lcd(LCD_SHARP_VGA);
		w100_init_vga_rotation(par->rotation_flag ? 270 : 90);
		par->lcdMode = LCD_MODE_640;
		lcdtg_hw_init(LCD_SHARP_VGA);
	} else if (var->xres == 320 && var->yres == 240) {
		if (par->lcdMode != LCD_MODE_320) {
			w100fb_changeres(LCD_MODE_LANDSCAPE, LCD_SHARP_QVGA);
			par->lcdMode = LCD_MODE_320;
		}
	} else if (var->xres == 240 && var->yres == 320) {
		if (par->lcdMode != LCD_MODE_240) {
			w100fb_changeres(LCD_MODE_PORTRAIT, LCD_SHARP_QVGA);
			par->lcdMode = LCD_MODE_240;
		}
	} else if (var->xres == 640 && var->yres == 480) {
		if (par->lcdMode != LCD_MODE_640) {
			w100fb_changeres(LCD_MODE_LANDSCAPE, LCD_SHARP_VGA);
			par->lcdMode = LCD_MODE_640;
		}
	} else if (var->xres == 480 && var->yres == 640) {
		if (par->lcdMode != LCD_MODE_480) {
			w100fb_changeres(LCD_MODE_PORTRAIT, LCD_SHARP_VGA);
			par->lcdMode = LCD_MODE_480;
		}
	} else printk(KERN_ERR "W100FB: Resolution error!\n");
}


/*
 *  w100fb_check_var():
 *  Get the video params out of 'var'. If a value doesn't fit, round it up,
 *  if it's too big, return -EINVAL.
 *
 */
static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
	if (var->xres < var->yres) { /* Portrait mode */
		if ((var->xres > 480) || (var->yres > 640)) {
			return -EINVAL;
		} else if ((var->xres > 240) || (var->yres > 320)) {
			var->xres = 480;
			var->yres = 640;
		} else {
			var->xres = 240;
			var->yres = 320;
		}
	} else { /* Landscape mode */
		if ((var->xres > 640) || (var->yres > 480)) {
			return -EINVAL;
		} else if ((var->xres > 320) || (var->yres > 240)) {
			var->xres = 640;
			var->yres = 480;
		} else {
			var->xres = 320;
			var->yres = 240;
		}
	}

	var->xres_virtual = max(var->xres_virtual, var->xres);
	var->yres_virtual = max(var->yres_virtual, var->yres);

	if (var->bits_per_pixel > BITS_PER_PIXEL)
		return -EINVAL;
	else
		var->bits_per_pixel = BITS_PER_PIXEL;

	var->red.offset = 11;
	var->red.length = 5;
	var->green.offset = 5;
	var->green.length = 6;
	var->blue.offset = 0;
	var->blue.length = 5;
	var->transp.offset = var->transp.length = 0;

	var->nonstd = 0;

	var->height = -1;
	var->width = -1;
	var->vmode = FB_VMODE_NONINTERLACED;

	var->sync = 0;
	var->pixclock = 0x04;	/* 171521; */

	return 0;
}


/*
 * w100fb_set_par():
 *	Set the user defined part of the display for the specified console
 *  by looking at the values in info.var
 */
static int w100fb_set_par(struct fb_info *info)
{
	struct w100fb_par *par=info->par;

	par->xres = info->var.xres;
	par->yres = info->var.yres;

	info->fix.visual = FB_VISUAL_TRUECOLOR;

	info->fix.ypanstep = 0;
	info->fix.ywrapstep = 0;

	if (par->blanking_flag)
		w100fb_clear_buffer();

	w100fb_activate_var(info);

	if (par->lcdMode == LCD_MODE_480) {
		info->fix.line_length = (480 * BITS_PER_PIXEL) / 8;
		info->fix.smem_len = 0x200000;
	} else if (par->lcdMode == LCD_MODE_320) {
		info->fix.line_length = (320 * BITS_PER_PIXEL) / 8;
		info->fix.smem_len = 0x60000;
	} else if (par->lcdMode == LCD_MODE_240) {
		info->fix.line_length = (240 * BITS_PER_PIXEL) / 8;
		info->fix.smem_len = 0x60000;
	} else if (par->lcdMode == LCD_MODE_INIT || par->lcdMode == LCD_MODE_640) {
		info->fix.line_length = (640 * BITS_PER_PIXEL) / 8;
		info->fix.smem_len = 0x200000;
	}

	return 0;
}


/*
 *      Frame buffer operations
 */
static struct fb_ops w100fb_ops = {
	.owner = THIS_MODULE,
	.fb_check_var = w100fb_check_var,
	.fb_set_par = w100fb_set_par,
	.fb_setcolreg = w100fb_setcolreg,
	.fb_blank = w100fb_blank,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
	.fb_cursor = soft_cursor,
};


static void w100fb_clear_screen(u32 mode, long int offset)
{
	int i, numPix = 0;

	if (mode == LCD_SHARP_VGA)
		numPix = 640 * 480;
	else if (mode == LCD_SHARP_QVGA)
		numPix = 320 * 240;

	for (i = 0; i < numPix; i++)
		writew(0xffff, remapped_fbuf + offset + (2*i));
}


/* Need to split up the buffers to stay within the limits of kmalloc */
#define W100_BUF_NUM	6
static uint32_t *gSaveImagePtr[W100_BUF_NUM] = { NULL };

static void w100fb_save_buffer(void)
{
	int i, j, bufsize;

	bufsize=(current_par->xres * current_par->yres * BITS_PER_PIXEL / 8) / W100_BUF_NUM;
	for (i = 0; i < W100_BUF_NUM; i++) {
		if (gSaveImagePtr[i] == NULL)
			gSaveImagePtr[i] = kmalloc(bufsize, GFP_KERNEL);
		if (gSaveImagePtr[i] == NULL) {
			w100fb_clear_buffer();
			printk(KERN_WARNING "can't alloc pre-off image buffer %d\n", i);
			break;
		}
		for (j = 0; j < bufsize/4; j++)
			*(gSaveImagePtr[i] + j) = readl(remapped_fbuf + (bufsize*i) + j*4);
	}
}


static void w100fb_restore_buffer(void)
{
	int i, j, bufsize;

	bufsize=(current_par->xres * current_par->yres * BITS_PER_PIXEL / 8) / W100_BUF_NUM;
	for (i = 0; i < W100_BUF_NUM; i++) {
		if (gSaveImagePtr[i] == NULL) {
			printk(KERN_WARNING "can't find pre-off image buffer %d\n", i);
			w100fb_clear_buffer();
			break;
		}
		for (j = 0; j < (bufsize/4); j++)
			writel(*(gSaveImagePtr[i] + j),remapped_fbuf + (bufsize*i) + (j*4));
		kfree(gSaveImagePtr[i]);
		gSaveImagePtr[i] = NULL;
	}
}


static void w100fb_clear_buffer(void)
{
	int i;
	for (i = 0; i < W100_BUF_NUM; i++) {
		kfree(gSaveImagePtr[i]);
		gSaveImagePtr[i] = NULL;
	}
}


#ifdef CONFIG_PM
static int w100fb_suspend(struct device *dev, pm_message_t state, u32 level)
{
	if (level == SUSPEND_POWER_DOWN) {
		struct fb_info *info = dev_get_drvdata(dev);
		struct w100fb_par *par=info->par;

		w100fb_save_buffer();
		lcdtg_suspend();
		w100_suspend(W100_SUSPEND_ALL);
		par->blanking_flag = 1;
	}
	return 0;
}

static int w100fb_resume(struct device *dev, u32 level)
{
	if (level == RESUME_POWER_ON) {
		struct fb_info *info = dev_get_drvdata(dev);
		struct w100fb_par *par=info->par;

		w100_resume();
		w100fb_restore_buffer();
		lcdtg_resume();
		par->blanking_flag = 0;
	}
	return 0;
}
#else
#define w100fb_suspend	NULL
#define w100fb_resume	NULL
#endif


int __init w100fb_probe(struct device *dev)
{
	struct w100fb_mach_info *inf;
	struct fb_info *info;
	struct w100fb_par *par;
	struct platform_device *pdev = to_platform_device(dev);
	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

	if (!mem)
		return -EINVAL;

	/* remap the areas we're going to use */
	remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
	if (remapped_base == NULL)
		return -EIO;

	remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
	if (remapped_regs == NULL) {
		iounmap(remapped_base);
		return -EIO;
	}

	remapped_fbuf = ioremap_nocache(mem->start+MEM_EXT_BASE_VALUE, REMAPPED_FB_LEN);
	if (remapped_fbuf == NULL) {
		iounmap(remapped_base);
		iounmap(remapped_regs);
		return -EIO;
	}

	info=framebuffer_alloc(sizeof(struct w100fb_par), dev);
	if (!info) {
		iounmap(remapped_base);
		iounmap(remapped_regs);
		iounmap(remapped_fbuf);
		return -ENOMEM;
	}

	info->device=dev;
	par = info->par;
	current_par=info->par;
	dev_set_drvdata(dev, info);

	inf = dev->platform_data;
	par->phadadj = inf->phadadj;
	par->comadj = inf->comadj;
	par->fastsysclk_mode = 75;
	par->lcdMode = LCD_MODE_INIT;
	par->rotation_flag=0;
	par->blanking_flag=0;
	w100fb_ssp_send = inf->w100fb_ssp_send;

	w100_hw_init();
	w100_pwm_setup();

	info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
	if (!info->pseudo_palette) {
		iounmap(remapped_base);
		iounmap(remapped_regs);
		iounmap(remapped_fbuf);
		return -ENOMEM;
	}

	info->fbops = &w100fb_ops;
	info->flags = FBINFO_DEFAULT;
	info->node = -1;
	info->screen_base = remapped_fbuf;
	info->screen_size = REMAPPED_FB_LEN;

	info->var.xres = 640;
	info->var.xres_virtual = info->var.xres;
	info->var.yres = 480;
	info->var.yres_virtual = info->var.yres;
	info->var.pixclock = 0x04;	/* 171521; */
	info->var.sync = 0;
	info->var.grayscale = 0;
	info->var.xoffset = info->var.yoffset = 0;
	info->var.accel_flags = 0;
	info->var.activate = FB_ACTIVATE_NOW;

	strcpy(info->fix.id, "w100fb");
	info->fix.type = FB_TYPE_PACKED_PIXELS;
	info->fix.type_aux = 0;
	info->fix.accel = FB_ACCEL_NONE;
	info->fix.smem_start = mem->start+MEM_EXT_BASE_VALUE;
	info->fix.mmio_start = mem->start+W100_REG_BASE;
	info->fix.mmio_len = W100_REG_LEN;

	w100fb_check_var(&info->var, info);
	w100fb_set_par(info);

	if (register_framebuffer(info) < 0) {
		kfree(info->pseudo_palette);
		iounmap(remapped_base);
		iounmap(remapped_regs);
		iounmap(remapped_fbuf);
		return -EINVAL;
	}

	device_create_file(dev, &dev_attr_fastsysclk);
	device_create_file(dev, &dev_attr_reg_read);
	device_create_file(dev, &dev_attr_reg_write);
	device_create_file(dev, &dev_attr_rotation);

	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
	return 0;
}


static int w100fb_remove(struct device *dev)
{
	struct fb_info *info = dev_get_drvdata(dev);

	device_remove_file(dev, &dev_attr_fastsysclk);
	device_remove_file(dev, &dev_attr_reg_read);
	device_remove_file(dev, &dev_attr_reg_write);
	device_remove_file(dev, &dev_attr_rotation);

	unregister_framebuffer(info);

	w100fb_clear_buffer();
	kfree(info->pseudo_palette);

	iounmap(remapped_base);
	iounmap(remapped_regs);
	iounmap(remapped_fbuf);

	framebuffer_release(info);

	return 0;
}


/* ------------------- chipset specific functions -------------------------- */


static void w100_soft_reset(void)
{
	u16 val = readw((u16 *) remapped_base + cfgSTATUS);
	writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
	udelay(100);
	writew(0x00, (u16 *) remapped_base + cfgSTATUS);
	udelay(100);
}

/*
 * Initialization of critical w100 hardware
 */
static void w100_hw_init(void)
{
	u32 temp32;
	union cif_cntl_u cif_cntl;
	union intf_cntl_u intf_cntl;
	union cfgreg_base_u cfgreg_base;
	union wrap_top_dir_u wrap_top_dir;
	union cif_read_dbg_u cif_read_dbg;
	union cpu_defaults_u cpu_default;
	union cif_write_dbg_u cif_write_dbg;
	union wrap_start_dir_u wrap_start_dir;
	union mc_ext_mem_location_u mc_ext_mem_loc;
	union cif_io_u cif_io;

	w100_soft_reset();

	/* This is what the fpga_init code does on reset. May be wrong
	   but there is little info available */
	writel(0x31, remapped_regs + mmSCRATCH_UMSK);
	for (temp32 = 0; temp32 < 10000; temp32++)
		readl(remapped_regs + mmSCRATCH_UMSK);
	writel(0x30, remapped_regs + mmSCRATCH_UMSK);

	/* Set up CIF */
	cif_io.val = defCIF_IO;
	writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);

	cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
	cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
	cif_write_dbg.f.en_dword_split_to_rbbm = 1;
	cif_write_dbg.f.dis_timeout_during_rbbm = 1;
	writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);

	cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
	cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
	writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);

	cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
	cif_cntl.f.dis_system_bits = 1;
	cif_cntl.f.dis_mr = 1;
	cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
	cif_cntl.f.intb_oe = 1;
	cif_cntl.f.interrupt_active_high = 1;
	writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);

	/* Setup cfgINTF_CNTL and cfgCPU defaults */
	intf_cntl.val = defINTF_CNTL;
	intf_cntl.f.ad_inc_a = 1;
	intf_cntl.f.ad_inc_b = 1;
	intf_cntl.f.rd_data_rdy_a = 0;
	intf_cntl.f.rd_data_rdy_b = 0;
	writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);

	cpu_default.val = defCPU_DEFAULTS;
	cpu_default.f.access_ind_addr_a = 1;
	cpu_default.f.access_ind_addr_b = 1;
	cpu_default.f.access_scratch_reg = 1;
	cpu_default.f.transition_size = 0;
	writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);

	/* set up the apertures */
	writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);

	cfgreg_base.val = defCFGREG_BASE;
	cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
	writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);

	/* This location is relative to internal w100 addresses */
	writel(0x15FF1000, remapped_regs + mmMC_FB_LOCATION);

	mc_ext_mem_loc.val = defMC_EXT_MEM_LOCATION;
	mc_ext_mem_loc.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
	mc_ext_mem_loc.f.mc_ext_mem_top = MEM_EXT_TOP_VALUE >> 8;
	writel((u32) (mc_ext_mem_loc.val), remapped_regs + mmMC_EXT_MEM_LOCATION);

	if ((current_par->lcdMode == LCD_MODE_240) || (current_par->lcdMode == LCD_MODE_320))
		w100_InitExtMem(LCD_SHARP_QVGA);
	else
		w100_InitExtMem(LCD_SHARP_VGA);

	wrap_start_dir.val = defWRAP_START_DIR;
	wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
	writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);

	wrap_top_dir.val = defWRAP_TOP_DIR;
	wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
	writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);

	writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
}


/*
 * Types
 */

struct pll_parm {
	u16 freq;		/* desired Fout for PLL */
	u8 M;
	u8 N_int;
	u8 N_fac;
	u8 tfgoal;
	u8 lock_time;
};

struct power_state {
	union clk_pin_cntl_u clk_pin_cntl;
	union pll_ref_fb_div_u pll_ref_fb_div;
	union pll_cntl_u pll_cntl;
	union sclk_cntl_u sclk_cntl;
	union pclk_cntl_u pclk_cntl;
	union clk_test_cntl_u clk_test_cntl;
	union pwrmgt_cntl_u pwrmgt_cntl;
	u32 freq;		/* Fout for PLL calibration */
	u8 tf100;		/* for pll calibration */
	u8 tf80;		/* for pll calibration */
	u8 tf20;		/* for pll calibration */
	u8 M;			/* for pll calibration */
	u8 N_int;		/* for pll calibration */
	u8 N_fac;		/* for pll calibration */
	u8 lock_time;	/* for pll calibration */
	u8 tfgoal;		/* for pll calibration */
	u8 auto_mode;	/* hardware auto switch? */
	u8 pwm_mode;		/* 0 fast, 1 normal/slow */
	u16 fast_sclk;	/* fast clk freq */
	u16 norm_sclk;	/* slow clk freq */
};


/*
 * Global state variables
 */

static struct power_state w100_pwr_state;

/* This table is specific for 12.5MHz ref crystal.  */
static struct pll_parm gPLLTable[] = {
    /*freq     M   N_int    N_fac  tfgoal  lock_time */
    { 50,      0,   1,       0,     0xE0,        56}, /*  50.00 MHz */
    { 75,      0,   5,       0,     0xDE,	     37}, /*  75.00 MHz */
    {100,      0,   7,       0,     0xE0,        28}, /* 100.00 MHz */
    {125,      0,   9,       0,     0xE0,        22}, /* 125.00 MHz */
    {150,      0,   11,      0,     0xE0,        17}, /* 150.00 MHz */
    {  0,      0,   0,       0,        0,         0}  /* Terminator */
};


static u8 w100_pll_get_testcount(u8 testclk_sel)
{
	udelay(5);

	w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
	w100_pwr_state.clk_test_cntl.f.testclk_sel = testclk_sel;
	w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x1;	/*reset test count */
	writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
	w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x0;
	writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);

	w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x1;
	writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);

	udelay(20);

	w100_pwr_state.clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
	w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
	writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);

	return w100_pwr_state.clk_test_cntl.f.test_count;
}


static u8 w100_pll_adjust(void)
{
	do {
		/* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
		 * therefore, commented out the following lines
		 * tf80 meant tf100
		 * set VCO input = 0.8 * VDD
		 */
		w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
		writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

		w100_pwr_state.tf80 = w100_pll_get_testcount(0x1);	/* PLLCLK */
		if (w100_pwr_state.tf80 >= (w100_pwr_state.tfgoal)) {
			/* set VCO input = 0.2 * VDD */
			w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
			writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

			w100_pwr_state.tf20 = w100_pll_get_testcount(0x1);	/* PLLCLK */
			if (w100_pwr_state.tf20 <= (w100_pwr_state.tfgoal))
				return 1; // Success

			if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
			    ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
			     (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
				/* slow VCO config */
				w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
				w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
				w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
				writel((u32) (w100_pwr_state.pll_cntl.val),
					remapped_regs + mmPLL_CNTL);
				continue;
			}
		}
		if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
			w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
			writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
			continue;
		}
		if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
			w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
			w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
			writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
			continue;
		}
		return 0; // error
	} while(1);
}


/*
 * w100_pll_calibration
 *                freq = target frequency of the PLL
 *                (note: crystal = 14.3MHz)
 */
static u8 w100_pll_calibration(u32 freq)
{
	u8 status;

	/* initial setting */
	w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0;		/* power down */
	w100_pwr_state.pll_cntl.f.pll_reset = 0x0;		/* not reset */
	w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1;	/* Hi-Z */
	w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;		/* VCO gain = 0 */
	w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;		/* VCO frequency range control = off */
	w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;	/* current offset inside VCO = 0 */
	w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

	/* check for (tf80 >= tfgoal) && (tf20 =< tfgoal) */
	if ((w100_pwr_state.tf80 < w100_pwr_state.tfgoal) || (w100_pwr_state.tf20 > w100_pwr_state.tfgoal)) {
		status=w100_pll_adjust();
	}
	/* PLL Reset And Lock */

	/* set VCO input = 0.5 * VDD */
	w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

	/* reset time */
	udelay(1);

	/* enable charge pump */
	w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;	/* normal */
	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

	/* set VCO input = Hi-Z */
	/* disable DAC */
	w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

	/* lock time */
	udelay(400);	/* delay 400 us */

	/* PLL locked */

	w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x1;	/* PLL clock */
	writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);

	w100_pwr_state.tf100 = w100_pll_get_testcount(0x1);	/* PLLCLK */

	return status;
}


static u8 w100_pll_set_clk(void)
{
	u8 status;

	if (w100_pwr_state.auto_mode == 1)	/* auto mode */
	{
		w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;	/* disable fast to normal */
		w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;	/* disable normal to fast */
		writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
	}

	w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0;	/* crystal clock */
	writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);

	w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = w100_pwr_state.M;
	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = w100_pwr_state.N_int;
	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = w100_pwr_state.N_fac;
	w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = w100_pwr_state.lock_time;
	writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);

	w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
	writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);

	status = w100_pll_calibration (w100_pwr_state.freq);

	if (w100_pwr_state.auto_mode == 1)	/* auto mode */
	{
		w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1;	/* reenable fast to normal */
		w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1;	/* reenable normal to fast  */
		writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
	}
	return status;
}


/* assume reference crystal clk is 12.5MHz,
 * and that doubling is not enabled.
 *
 * Freq = 12 == 12.5MHz.
 */
static u16 w100_set_slowsysclk(u16 freq)
{
	if (w100_pwr_state.norm_sclk == freq)
		return freq;

	if (w100_pwr_state.auto_mode == 1)	/* auto mode */
		return 0;

	if (freq == 12) {
		w100_pwr_state.norm_sclk = freq;
		w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0;	/* Pslow = 1 */
		w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0;	/* crystal src */

		writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);

		w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x1;
		writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);

		w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x1;
		w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1;
		writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
		w100_pwr_state.pwm_mode = 1;	/* normal mode */
		return freq;
	} else
		return 0;
}


static u16 w100_set_fastsysclk(u16 freq)
{
	u16 pll_freq;
	int i;

	while(1) {
		pll_freq = (u16) (freq * (w100_pwr_state.sclk_cntl.f.sclk_post_div_fast + 1));
		i = 0;
		do {
			if (pll_freq == gPLLTable[i].freq) {
				w100_pwr_state.freq = gPLLTable[i].freq * 1000000;
				w100_pwr_state.M = gPLLTable[i].M;
				w100_pwr_state.N_int = gPLLTable[i].N_int;
				w100_pwr_state.N_fac = gPLLTable[i].N_fac;
				w100_pwr_state.tfgoal = gPLLTable[i].tfgoal;
				w100_pwr_state.lock_time = gPLLTable[i].lock_time;
				w100_pwr_state.tf20 = 0xff;	/* set highest */
				w100_pwr_state.tf80 = 0x00;	/* set lowest */

				w100_pll_set_clk();
				w100_pwr_state.pwm_mode = 0;	/* fast mode */
				w100_pwr_state.fast_sclk = freq;
				return freq;
			}
			i++;
		} while(gPLLTable[i].freq);

		if (w100_pwr_state.auto_mode == 1)
			break;

		if (w100_pwr_state.sclk_cntl.f.sclk_post_div_fast == 0)
			break;

		w100_pwr_state.sclk_cntl.f.sclk_post_div_fast -= 1;
		writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
	}
	return 0;
}


/* Set up an initial state.  Some values/fields set
   here will be overwritten. */
static void w100_pwm_setup(void)
{
	w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
	w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
	w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
	w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
	w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = 0x0;	/* no freq doubling */
	w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
	writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);

	w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0;	/* Crystal Clk */
	w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0;	/* Pfast = 1 */
	w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
	w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0;	/* Pslow = 1 */
	w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
	w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0;	/* Dynamic */
	w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
	w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
	w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
	w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
	writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);

	w100_pwr_state.pclk_cntl.f.pclk_src_sel = 0x0;	/* Crystal Clk */
	w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1;	/* P = 2 */
	w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0;	/* Dynamic */
	writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);

	w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0;	/* M = 1 */
	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0;	/* N = 1.0 */
	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
	w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
	w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
	writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);

	w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
	w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
	w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
	w100_pwr_state.pll_cntl.f.pll_mode = 0x0;	/* uses VCO clock */
	w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
	w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
	w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
	w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
	w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
	w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
	w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
	w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
	w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
	w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;	/* Hi-Z */
	w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
	w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
	w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
	w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);

	w100_pwr_state.clk_test_cntl.f.testclk_sel = 0x1;	/* PLLCLK (for testing) */
	w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
	w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x0;
	writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);

	w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
	w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1;	/* normal mode (0, 1, 3) */
	w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
	w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
	w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
	w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1;	/* PM4,ENG */
	w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1;	/* PM4,ENG */
	w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
	w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
	writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);

	w100_pwr_state.auto_mode = 0;	/* manual mode */
	w100_pwr_state.pwm_mode = 1;	/* normal mode (0, 1, 2) */
	w100_pwr_state.freq = 50000000;	/* 50 MHz */
	w100_pwr_state.M = 3;	/* M = 4 */
	w100_pwr_state.N_int = 6;	/* N = 7.0 */
	w100_pwr_state.N_fac = 0;
	w100_pwr_state.tfgoal = 0xE0;
	w100_pwr_state.lock_time = 56;
	w100_pwr_state.tf20 = 0xff;	/* set highest */
	w100_pwr_state.tf80 = 0x00;	/* set lowest */
	w100_pwr_state.tf100 = 0x00;	/* set lowest */
	w100_pwr_state.fast_sclk = 50;	/* 50.0 MHz */
	w100_pwr_state.norm_sclk = 12;	/* 12.5 MHz */
}


static void w100_init_sharp_lcd(u32 mode)
{
	u32 temp32;
	union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;

	/* Prevent display updates */
	disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
	disp_db_buf_wr_cntl.f.update_db_buf = 0;
	disp_db_buf_wr_cntl.f.en_db_buf = 0;
	writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);

	switch(mode) {
	case LCD_SHARP_QVGA:
		w100_set_slowsysclk(12);	/* use crystal -- 12.5MHz */
		/* not use PLL */

		writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
		writel(0x85FF8000, remapped_regs + mmMC_FB_LOCATION);
		writel(0x00000003, remapped_regs + mmLCD_FORMAT);
		writel(0x00CF1C06, remapped_regs + mmGRAPHIC_CTRL);
		writel(0x01410145, remapped_regs + mmCRTC_TOTAL);
		writel(0x01170027, remapped_regs + mmACTIVE_H_DISP);
		writel(0x01410001, remapped_regs + mmACTIVE_V_DISP);
		writel(0x01170027, remapped_regs + mmGRAPHIC_H_DISP);
		writel(0x01410001, remapped_regs + mmGRAPHIC_V_DISP);
		writel(0x81170027, remapped_regs + mmCRTC_SS);
		writel(0xA0140000, remapped_regs + mmCRTC_LS);
		writel(0x00400008, remapped_regs + mmCRTC_REV);
		writel(0xA0000000, remapped_regs + mmCRTC_DCLK);
		writel(0xC0140014, remapped_regs + mmCRTC_GS);
		writel(0x00010141, remapped_regs + mmCRTC_VPOS_GS);
		writel(0x8015010F, remapped_regs + mmCRTC_GCLK);
		writel(0x80100110, remapped_regs + mmCRTC_GOE);
		writel(0x00000000, remapped_regs + mmCRTC_FRAME);
		writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
		writel(0x01CC0000, remapped_regs + mmLCDD_CNTL1);
		writel(0x0003FFFF, remapped_regs + mmLCDD_CNTL2);
		writel(0x00FFFF0D, remapped_regs + mmGENLCD_CNTL1);
		writel(0x003F3003, remapped_regs + mmGENLCD_CNTL2);
		writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
		writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
		writel(0x000102aa, remapped_regs + mmGENLCD_CNTL3);
		writel(0x00800000, remapped_regs + mmGRAPHIC_OFFSET);
		writel(0x000001e0, remapped_regs + mmGRAPHIC_PITCH);
		writel(0x000000bf, remapped_regs + mmGPIO_DATA);
		writel(0x03c0feff, remapped_regs + mmGPIO_CNTL2);
		writel(0x00000000, remapped_regs + mmGPIO_CNTL1);
		writel(0x41060010, remapped_regs + mmCRTC_PS1_ACTIVE);
		break;
	case LCD_SHARP_VGA:
		w100_set_slowsysclk(12);	/* use crystal -- 12.5MHz */
		w100_set_fastsysclk(current_par->fastsysclk_mode);	/* use PLL -- 75.0MHz */
		w100_pwr_state.pclk_cntl.f.pclk_src_sel = 0x1;
		w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x2;
		writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
		writel(0x15FF1000, remapped_regs + mmMC_FB_LOCATION);
		writel(0x9FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
		writel(0x00000003, remapped_regs + mmLCD_FORMAT);
		writel(0x00DE1D66, remapped_regs + mmGRAPHIC_CTRL);

		writel(0x0283028B, remapped_regs + mmCRTC_TOTAL);
		writel(0x02360056, remapped_regs + mmACTIVE_H_DISP);
		writel(0x02830003, remapped_regs + mmACTIVE_V_DISP);
		writel(0x02360056, remapped_regs + mmGRAPHIC_H_DISP);
		writel(0x02830003, remapped_regs + mmGRAPHIC_V_DISP);
		writel(0x82360056, remapped_regs + mmCRTC_SS);
		writel(0xA0280000, remapped_regs + mmCRTC_LS);
		writel(0x00400008, remapped_regs + mmCRTC_REV);
		writel(0xA0000000, remapped_regs + mmCRTC_DCLK);
		writel(0x80280028, remapped_regs + mmCRTC_GS);
		writel(0x02830002, remapped_regs + mmCRTC_VPOS_GS);
		writel(0x8015010F, remapped_regs + mmCRTC_GCLK);
		writel(0x80100110, remapped_regs + mmCRTC_GOE);
		writel(0x00000000, remapped_regs + mmCRTC_FRAME);
		writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
		writel(0x01CC0000, remapped_regs + mmLCDD_CNTL1);
		writel(0x0003FFFF, remapped_regs + mmLCDD_CNTL2);
		writel(0x00FFFF0D, remapped_regs + mmGENLCD_CNTL1);
		writel(0x003F3003, remapped_regs + mmGENLCD_CNTL2);
		writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
		writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
		writel(0x000102aa, remapped_regs + mmGENLCD_CNTL3);
		writel(0x00800000, remapped_regs + mmGRAPHIC_OFFSET);
		writel(0x000003C0, remapped_regs + mmGRAPHIC_PITCH);
		writel(0x000000bf, remapped_regs + mmGPIO_DATA);
		writel(0x03c0feff, remapped_regs + mmGPIO_CNTL2);
		writel(0x00000000, remapped_regs + mmGPIO_CNTL1);
		writel(0x41060010, remapped_regs + mmCRTC_PS1_ACTIVE);
		break;
	default:
		break;
	}

	/* Hack for overlay in ext memory */
	temp32 = readl(remapped_regs + mmDISP_DEBUG2);
	temp32 |= 0xc0000000;
	writel(temp32, remapped_regs + mmDISP_DEBUG2);

	/* Re-enable display updates */
	disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
	disp_db_buf_wr_cntl.f.update_db_buf = 1;
	disp_db_buf_wr_cntl.f.en_db_buf = 1;
	writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
}


static void w100_set_vga_rotation_regs(u16 divider, unsigned long ctrl, unsigned long offset, unsigned long pitch)
{
	w100_pwr_state.pclk_cntl.f.pclk_src_sel = 0x1;
	w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
	writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);

	writel(ctrl, remapped_regs + mmGRAPHIC_CTRL);
	writel(offset, remapped_regs + mmGRAPHIC_OFFSET);
	writel(pitch, remapped_regs + mmGRAPHIC_PITCH);

	/* Re-enable display updates */
	writel(0x0000007b, remapped_regs + mmDISP_DB_BUF_CNTL);
}


static void w100_init_vga_rotation(u16 deg)
{
	switch(deg) {
	case 0:
		w100_set_vga_rotation_regs(0x02, 0x00DE1D66, 0x00800000, 0x000003c0);
		break;
	case 90:
		w100_set_vga_rotation_regs(0x06, 0x00DE1D0e, 0x00895b00, 0x00000500);
		break;
	case 180:
		w100_set_vga_rotation_regs(0x02, 0x00DE1D7e, 0x00895ffc, 0x000003c0);
		break;
	case 270:
		w100_set_vga_rotation_regs(0x06, 0x00DE1D16, 0x008004fc, 0x00000500);
		break;
	default:
		/* not-support */
		break;
	}
}


static void w100_set_qvga_rotation_regs(unsigned long ctrl, unsigned long offset, unsigned long pitch)
{
	writel(ctrl, remapped_regs + mmGRAPHIC_CTRL);
	writel(offset, remapped_regs + mmGRAPHIC_OFFSET);
	writel(pitch, remapped_regs + mmGRAPHIC_PITCH);

	/* Re-enable display updates */
	writel(0x0000007b, remapped_regs + mmDISP_DB_BUF_CNTL);
}


static void w100_init_qvga_rotation(u16 deg)
{
	switch(deg) {
	case 0:
		w100_set_qvga_rotation_regs(0x00d41c06, 0x00800000, 0x000001e0);
		break;
	case 90:
		w100_set_qvga_rotation_regs(0x00d41c0E, 0x00825580, 0x00000280);
		break;
	case 180:
		w100_set_qvga_rotation_regs(0x00d41c1e, 0x008257fc, 0x000001e0);
		break;
	case 270:
		w100_set_qvga_rotation_regs(0x00d41c16, 0x0080027c, 0x00000280);
		break;
	default:
		/* not-support */
		break;
	}
}


static void w100_suspend(u32 mode)
{
	u32 val;

	writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
	writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);

	val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
	val &= ~(0x00100000);	/* bit20=0 */
	val |= 0xFF000000;	/* bit31:24=0xff */
	writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);

	val = readl(remapped_regs + mmMEM_EXT_CNTL);
	val &= ~(0x00040000);	/* bit18=0 */
	val |= 0x00080000;	/* bit19=1 */
	writel(val, remapped_regs + mmMEM_EXT_CNTL);

	udelay(1);		/* wait 1us */

	if (mode == W100_SUSPEND_EXTMEM) {

		/* CKE: Tri-State */
		val = readl(remapped_regs + mmMEM_EXT_CNTL);
		val |= 0x40000000;	/* bit30=1 */
		writel(val, remapped_regs + mmMEM_EXT_CNTL);

		/* CLK: Stop */
		val = readl(remapped_regs + mmMEM_EXT_CNTL);
		val &= ~(0x00000001);	/* bit0=0 */
		writel(val, remapped_regs + mmMEM_EXT_CNTL);
	} else {

		writel(0x00000000, remapped_regs + mmSCLK_CNTL);
		writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
		writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);

		udelay(5);

		val = readl(remapped_regs + mmPLL_CNTL);
		val |= 0x00000004;	/* bit2=1 */
		writel(val, remapped_regs + mmPLL_CNTL);
		writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
	}
}


static void w100_resume(void)
{
	u32 temp32;

	w100_hw_init();
	w100_pwm_setup();

	temp32 = readl(remapped_regs + mmDISP_DEBUG2);
	temp32 &= 0xff7fffff;
	temp32 |= 0x00800000;
	writel(temp32, remapped_regs + mmDISP_DEBUG2);

	if (current_par->lcdMode == LCD_MODE_480 || current_par->lcdMode == LCD_MODE_640) {
		w100_init_sharp_lcd(LCD_SHARP_VGA);
		if (current_par->lcdMode == LCD_MODE_640) {
			w100_init_vga_rotation(current_par->rotation_flag ? 270 : 90);
		}
	} else {
		w100_init_sharp_lcd(LCD_SHARP_QVGA);
		if (current_par->lcdMode == LCD_MODE_320) {
			w100_init_qvga_rotation(current_par->rotation_flag ? 270 : 90);
		}
	}
}


static void w100_vsync(void)
{
	u32 tmp;
	int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */

	tmp = readl(remapped_regs + mmACTIVE_V_DISP);

	/* set vline pos  */
	writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);

	/* disable vline irq */
	tmp = readl(remapped_regs + mmGEN_INT_CNTL);

	tmp &= ~0x00000002;
	writel(tmp, remapped_regs + mmGEN_INT_CNTL);

	/* clear vline irq status */
	writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);

	/* enable vline irq */
	writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);

	/* clear vline irq status */
	writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);

	while(timeout > 0) {
		if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
			break;
		udelay(1);
		timeout--;
	}

	/* disable vline irq */
	writel(tmp, remapped_regs + mmGEN_INT_CNTL);

	/* clear vline irq status */
	writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
}


static void w100_InitExtMem(u32 mode)
{
	switch(mode) {
	case LCD_SHARP_QVGA:
		/* QVGA doesn't use external memory
		   nothing to do, really. */
		break;
	case LCD_SHARP_VGA:
		writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
		writel(0x00040003, remapped_regs + mmMEM_EXT_CNTL);
		writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
		udelay(100);
		writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
		udelay(100);
		writel(0x00650021, remapped_regs + mmMEM_SDRAM_MODE_REG);
		udelay(100);
		writel(0x10002a4a, remapped_regs + mmMEM_EXT_TIMING_CNTL);
		writel(0x7ff87012, remapped_regs + mmMEM_IO_CNTL);
		break;
	default:
		break;
	}
}


#define RESCTL_ADRS     0x00
#define PHACTRL_ADRS	0x01
#define DUTYCTRL_ADRS	0x02
#define POWERREG0_ADRS	0x03
#define POWERREG1_ADRS	0x04
#define GPOR3_ADRS		0x05
#define PICTRL_ADRS     0x06
#define POLCTRL_ADRS    0x07

#define RESCTL_QVGA     0x01
#define RESCTL_VGA      0x00

#define POWER1_VW_ON	0x01	/* VW Supply FET ON */
#define POWER1_GVSS_ON	0x02	/* GVSS(-8V) Power Supply ON */
#define POWER1_VDD_ON	0x04	/* VDD(8V),SVSS(-4V) Power Supply ON */

#define POWER1_VW_OFF	0x00	/* VW Supply FET OFF */
#define POWER1_GVSS_OFF	0x00	/* GVSS(-8V) Power Supply OFF */
#define POWER1_VDD_OFF	0x00	/* VDD(8V),SVSS(-4V) Power Supply OFF */

#define POWER0_COM_DCLK	0x01	/* COM Voltage DC Bias DAC Serial Data Clock */
#define POWER0_COM_DOUT	0x02	/* COM Voltage DC Bias DAC Serial Data Out */
#define POWER0_DAC_ON	0x04	/* DAC Power Supply ON */
#define POWER0_COM_ON	0x08	/* COM Powewr Supply ON */
#define POWER0_VCC5_ON	0x10	/* VCC5 Power Supply ON */

#define POWER0_DAC_OFF	0x00	/* DAC Power Supply OFF */
#define POWER0_COM_OFF	0x00	/* COM Powewr Supply OFF */
#define POWER0_VCC5_OFF	0x00	/* VCC5 Power Supply OFF */

#define PICTRL_INIT_STATE	0x01
#define PICTRL_INIOFF		0x02
#define PICTRL_POWER_DOWN	0x04
#define PICTRL_COM_SIGNAL_OFF	0x08
#define PICTRL_DAC_SIGNAL_OFF	0x10

#define PICTRL_POWER_ACTIVE	(0)

#define POLCTRL_SYNC_POL_FALL	0x01
#define POLCTRL_EN_POL_FALL	0x02
#define POLCTRL_DATA_POL_FALL	0x04
#define POLCTRL_SYNC_ACT_H	0x08
#define POLCTRL_EN_ACT_L	0x10

#define POLCTRL_SYNC_POL_RISE	0x00
#define POLCTRL_EN_POL_RISE	0x00
#define POLCTRL_DATA_POL_RISE	0x00
#define POLCTRL_SYNC_ACT_L	0x00
#define POLCTRL_EN_ACT_H	0x00

#define PHACTRL_PHASE_MANUAL	0x01

#define PHAD_QVGA_DEFAULT_VAL (9)
#define COMADJ_DEFAULT        (125)

static void lcdtg_ssp_send(u8 adrs, u8 data)
{
	w100fb_ssp_send(adrs,data);
}

/*
 * This is only a psuedo I2C interface. We can't use the standard kernel
 * routines as the interface is write only. We just assume the data is acked...
 */
static void lcdtg_ssp_i2c_send(u8 data)
{
	lcdtg_ssp_send(POWERREG0_ADRS, data);
	udelay(10);
}

static void lcdtg_i2c_send_bit(u8 data)
{
	lcdtg_ssp_i2c_send(data);
	lcdtg_ssp_i2c_send(data | POWER0_COM_DCLK);
	lcdtg_ssp_i2c_send(data);
}

static void lcdtg_i2c_send_start(u8 base)
{
	lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK | POWER0_COM_DOUT);
	lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK);
	lcdtg_ssp_i2c_send(base);
}

static void lcdtg_i2c_send_stop(u8 base)
{
	lcdtg_ssp_i2c_send(base);
	lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK);
	lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK | POWER0_COM_DOUT);
}

static void lcdtg_i2c_send_byte(u8 base, u8 data)
{
	int i;
	for (i = 0; i < 8; i++) {
		if (data & 0x80)
			lcdtg_i2c_send_bit(base | POWER0_COM_DOUT);
		else
			lcdtg_i2c_send_bit(base);
		data <<= 1;
	}
}

static void lcdtg_i2c_wait_ack(u8 base)
{
	lcdtg_i2c_send_bit(base);
}

static void lcdtg_set_common_voltage(u8 base_data, u8 data)
{
	/* Set Common Voltage to M62332FP via I2C */
	lcdtg_i2c_send_start(base_data);
	lcdtg_i2c_send_byte(base_data, 0x9c);
	lcdtg_i2c_wait_ack(base_data);
	lcdtg_i2c_send_byte(base_data, 0x00);
	lcdtg_i2c_wait_ack(base_data);
	lcdtg_i2c_send_byte(base_data, data);
	lcdtg_i2c_wait_ack(base_data);
	lcdtg_i2c_send_stop(base_data);
}

static struct lcdtg_register_setting {
	u8 adrs;
	u8 data;
	u32 wait;
} lcdtg_power_on_table[] = {

    /* Initialize Internal Logic & Port */
    { PICTRL_ADRS,
      PICTRL_POWER_DOWN | PICTRL_INIOFF | PICTRL_INIT_STATE |
      PICTRL_COM_SIGNAL_OFF | PICTRL_DAC_SIGNAL_OFF,
      0 },

    { POWERREG0_ADRS,
      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF | POWER0_COM_OFF |
      POWER0_VCC5_OFF,
      0 },

    { POWERREG1_ADRS,
      POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF,
      0 },

    /* VDD(+8V),SVSS(-4V) ON */
    { POWERREG1_ADRS,
      POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON /* VDD ON */,
      3000 },

    /* DAC ON */
    { POWERREG0_ADRS,
      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON /* DAC ON */ |
      POWER0_COM_OFF | POWER0_VCC5_OFF,
      0 },

    /* INIB = H, INI = L  */
    { PICTRL_ADRS,
      /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
      PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF,
      0 },

    /* Set Common Voltage */
    { 0xfe, 0, 0 },

    /* VCC5 ON */
    { POWERREG0_ADRS,
      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON /* DAC ON */ |
      POWER0_COM_OFF | POWER0_VCC5_ON /* VCC5 ON */,
      0 },

    /* GVSS(-8V) ON */
    { POWERREG1_ADRS,
      POWER1_VW_OFF | POWER1_GVSS_ON /* GVSS ON */ |
      POWER1_VDD_ON /* VDD ON */,
      2000 },

    /* COM SIGNAL ON (PICTL[3] = L) */
    { PICTRL_ADRS,
      PICTRL_INIT_STATE,
      0 },

    /* COM ON */
    { POWERREG0_ADRS,
      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON /* DAC ON */ |
      POWER0_COM_ON /* COM ON */ | POWER0_VCC5_ON /* VCC5_ON */,
      0 },

    /* VW ON */
    { POWERREG1_ADRS,
      POWER1_VW_ON /* VW ON */ | POWER1_GVSS_ON /* GVSS ON */ |
      POWER1_VDD_ON /* VDD ON */,
      0 /* Wait 100ms */ },

    /* Signals output enable */
    { PICTRL_ADRS,
      0 /* Signals output enable */,
      0 },

    { PHACTRL_ADRS,
      PHACTRL_PHASE_MANUAL,
      0 },

    /* Initialize for Input Signals from ATI */
    { POLCTRL_ADRS,
      POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE | POLCTRL_DATA_POL_RISE |
      POLCTRL_SYNC_ACT_L | POLCTRL_EN_ACT_H,
      1000 /*100000*/ /* Wait 100ms */ },

    /* end mark */
    { 0xff, 0, 0 }
};

static void lcdtg_resume(void)
{
	if (current_par->lcdMode == LCD_MODE_480 || current_par->lcdMode == LCD_MODE_640) {
		lcdtg_hw_init(LCD_SHARP_VGA);
	} else {
		lcdtg_hw_init(LCD_SHARP_QVGA);
	}
}

static void lcdtg_suspend(void)
{
	int i;

	for (i = 0; i < (current_par->xres * current_par->yres); i++) {
		writew(0xffff, remapped_fbuf + (2*i));
	}

	/* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
	mdelay(34);

	/* (1)VW OFF */
	lcdtg_ssp_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);

	/* (2)COM OFF */
	lcdtg_ssp_send(PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
	lcdtg_ssp_send(POWERREG0_ADRS, POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);

	/* (3)Set Common Voltage Bias 0V */
	lcdtg_set_common_voltage(POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON, 0);

	/* (4)GVSS OFF */
	lcdtg_ssp_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);

	/* (5)VCC5 OFF */
	lcdtg_ssp_send(POWERREG0_ADRS, POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);

	/* (6)Set PDWN, INIOFF, DACOFF */
	lcdtg_ssp_send(PICTRL_ADRS, PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
			PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);

	/* (7)DAC OFF */
	lcdtg_ssp_send(POWERREG0_ADRS, POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);

	/* (8)VDD OFF */
	lcdtg_ssp_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);

}

static void lcdtg_set_phadadj(u32 mode)
{
	int adj;

	if (mode == LCD_SHARP_VGA) {
		/* Setting for VGA */
		adj = current_par->phadadj;
		if (adj < 0) {
			adj = PHACTRL_PHASE_MANUAL;
		} else {
			adj = ((adj & 0x0f) << 1) | PHACTRL_PHASE_MANUAL;
		}
	} else {
		/* Setting for QVGA */
		adj = (PHAD_QVGA_DEFAULT_VAL << 1) | PHACTRL_PHASE_MANUAL;
	}
	lcdtg_ssp_send(PHACTRL_ADRS, adj);
}

static void lcdtg_hw_init(u32 mode)
{
	int i;
	int comadj;

	i = 0;
	while(lcdtg_power_on_table[i].adrs != 0xff) {
		if (lcdtg_power_on_table[i].adrs == 0xfe) {
			/* Set Common Voltage */
			comadj = current_par->comadj;
			if (comadj < 0) {
				comadj = COMADJ_DEFAULT;
			}
			lcdtg_set_common_voltage((POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF), comadj);
		} else if (lcdtg_power_on_table[i].adrs == PHACTRL_ADRS) {
			/* Set Phase Adjuct */
			lcdtg_set_phadadj(mode);
		} else {
			/* Other */
			lcdtg_ssp_send(lcdtg_power_on_table[i].adrs, lcdtg_power_on_table[i].data);
		}
		if (lcdtg_power_on_table[i].wait != 0)
			udelay(lcdtg_power_on_table[i].wait);
		i++;
	}

	switch(mode) {
	case LCD_SHARP_QVGA:
		/* Set Lcd Resolution (QVGA) */
		lcdtg_ssp_send(RESCTL_ADRS, RESCTL_QVGA);
		break;
	case LCD_SHARP_VGA:
		/* Set Lcd Resolution (VGA) */
		lcdtg_ssp_send(RESCTL_ADRS, RESCTL_VGA);
		break;
	default:
		break;
	}
}

static void lcdtg_lcd_change(u32 mode)
{
	/* Set Phase Adjuct */
	lcdtg_set_phadadj(mode);

	if (mode == LCD_SHARP_VGA)
		/* Set Lcd Resolution (VGA) */
		lcdtg_ssp_send(RESCTL_ADRS, RESCTL_VGA);
	else if (mode == LCD_SHARP_QVGA)
		/* Set Lcd Resolution (QVGA) */
		lcdtg_ssp_send(RESCTL_ADRS, RESCTL_QVGA);
}


static struct device_driver w100fb_driver = {
	.name		= "w100fb",
	.bus		= &platform_bus_type,
	.probe		= w100fb_probe,
	.remove		= w100fb_remove,
	.suspend	= w100fb_suspend,
	.resume		= w100fb_resume,
};

int __devinit w100fb_init(void)
{
	return driver_register(&w100fb_driver);
}

void __exit w100fb_cleanup(void)
{
 	driver_unregister(&w100fb_driver);
}

module_init(w100fb_init);
module_exit(w100fb_cleanup);

MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
MODULE_LICENSE("GPLv2");
