18661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
28661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *
38661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * Copyright (c) 2009 Nuvoton technology corporation
48661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * All rights reserved.
58661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *
68661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * This program is free software; you can redistribute it and/or modify
78661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * it under the terms of the GNU General Public License as published by
88661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * the Free Software Foundation; either version 2 of the License, or
98661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * (at your option) any later version.
108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *
118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *  Description:
128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *    Nuvoton LCD Controller Driver
138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *  Author:
148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *    Wang Qiang (rurality.linux@gmail.com) 2009/12/11
158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/module.h>
178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/kernel.h>
18ec03031707a348578c6ded277e5d92eb21de4fceJamie Iles#include <linux/err.h>
198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/errno.h>
208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/string.h>
218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/mm.h>
228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/tty.h>
238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/slab.h>
248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/delay.h>
258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/fb.h>
268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/init.h>
278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/dma-mapping.h>
288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/interrupt.h>
298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/workqueue.h>
308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/wait.h>
318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/platform_device.h>
328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/clk.h>
338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/cpufreq.h>
348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/io.h>
358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/pm.h>
368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <linux/device.h>
378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <mach/map.h>
398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <mach/regs-clock.h>
408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <mach/regs-ldm.h>
418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include <mach/fb.h>
428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#include "nuc900fb.h"
448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *  Initialize the nuc900 video (dual) buffer address
488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic void nuc900fb_set_lcdaddr(struct fb_info *info)
508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	void __iomem *regs = fbi->io;
538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned long vbaddr1, vbaddr2;
548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	vbaddr1  = info->fix.smem_start;
568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	vbaddr2  = info->fix.smem_start;
578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	vbaddr2 += info->fix.line_length * info->var.yres;
588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* set frambuffer start phy addr*/
608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(vbaddr1, regs + REG_LCM_VA_BADDR0);
618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(vbaddr2, regs + REG_LCM_VA_BADDR1);
628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_va_fbctrl, regs + REG_LCM_VA_FBCTRL);
648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_va_scale, regs + REG_LCM_VA_SCALE);
658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
688661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *	calculate divider for lcd div
698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic unsigned int nuc900fb_calc_pixclk(struct nuc900fb_info *fbi,
718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang					 unsigned long pixclk)
728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned long clk = fbi->clk_rate;
748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned long long div;
758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* pixclk is in picseconds. our clock is in Hz*/
778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* div = (clk * pixclk)/10^12 */
788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	div = (unsigned long long)clk * pixclk;
798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	div >>= 12;
808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	do_div(div, 625 * 625UL * 625);
818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "pixclk %ld, divisor is %lld\n", pixclk, div);
838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return div;
858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *	Check the video params of 'var'.
898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
908661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_check_var(struct fb_var_screeninfo *var,
918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			       struct fb_info *info)
928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_mach_info *mach_info = fbi->dev->platform_data;
958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_display *display = NULL;
968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_display *default_display = mach_info->displays +
978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang						   mach_info->default_display;
988661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int i;
998661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1008661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "check_var(var=%p, info=%p)\n", var, info);
1018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1028661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* validate x/y resolution */
1038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* choose default mode if possible */
1048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (var->xres == default_display->xres &&
1058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	    var->yres == default_display->yres &&
1068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	    var->bits_per_pixel == default_display->bpp)
1078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		display = default_display;
1088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	else
1098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		for (i = 0; i < mach_info->num_displays; i++)
1108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			if (var->xres == mach_info->displays[i].xres &&
1118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			    var->yres == mach_info->displays[i].yres &&
1128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			    var->bits_per_pixel == mach_info->displays[i].bpp) {
1138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				display = mach_info->displays + i;
1148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				break;
1158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			}
1168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (display == NULL) {
1188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		printk(KERN_ERR "wrong resolution or depth %dx%d at %d bit per pixel\n",
1198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			var->xres, var->yres, var->bits_per_pixel);
1208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return -EINVAL;
1218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
1228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* it should be the same size as the display */
1248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->xres_virtual	= display->xres;
1258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->yres_virtual	= display->yres;
1268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->height		= display->height;
1278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->width		= display->width;
1288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* copy lcd settings */
1308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->pixclock		= display->pixclock;
1318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->left_margin	= display->left_margin;
1328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->right_margin	= display->right_margin;
1338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->upper_margin	= display->upper_margin;
1348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->lower_margin	= display->lower_margin;
1358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->vsync_len		= display->vsync_len;
1368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->hsync_len		= display->hsync_len;
1378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->transp.offset	= 0;
1398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	var->transp.length	= 0;
1408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->regs.lcd_dccs = display->dccs;
1428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->regs.lcd_device_ctrl = display->devctl;
1438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->regs.lcd_va_fbctrl = display->fbctrl;
1448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->regs.lcd_va_scale = display->scale;
1458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* set R/G/B possions */
1478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	switch (var->bits_per_pixel) {
1488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 1:
1498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 2:
1508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 4:
1518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 8:
1528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	default:
1538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.offset 	= 0;
1548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.length 	= var->bits_per_pixel;
1558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green 		= var->red;
1568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue		= var->red;
1578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
1588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 12:
1598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.length		= 4;
1608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.length	= 4;
1618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.length	= 4;
1628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.offset		= 8;
1638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.offset	= 4;
1648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.offset	= 0;
1658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
1668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 16:
1678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.length		= 5;
1688661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.length	= 6;
1698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.length	= 5;
1708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.offset		= 11;
1718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.offset	= 5;
1728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.offset	= 0;
1738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
1748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 18:
1758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.length		= 6;
1768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.length	= 6;
1778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.length	= 6;
1788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.offset		= 12;
1798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.offset	= 6;
1808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.offset	= 0;
1818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
1828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 32:
1838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.length		= 8;
1848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.length	= 8;
1858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.length	= 8;
1868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->red.offset		= 16;
1878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->green.offset	= 8;
1888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		var->blue.offset	= 0;
1898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
1908661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
1918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
1938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
1948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
1958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
1968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *	Calculate lcd register values from var setting & save into hw
1978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
1988661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic void nuc900fb_calculate_lcd_regs(const struct fb_info *info,
1998661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang					struct nuc900fb_hw *regs)
2008661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
2018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	const struct fb_var_screeninfo *var = &info->var;
2028661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int vtt = var->height + var->upper_margin + var->lower_margin;
2038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int htt = var->width + var->left_margin + var->right_margin;
2048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int hsync = var->width + var->right_margin;
2058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int vsync = var->height + var->lower_margin;
2068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	regs->lcd_crtc_size = LCM_CRTC_SIZE_VTTVAL(vtt) |
2088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			      LCM_CRTC_SIZE_HTTVAL(htt);
2098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	regs->lcd_crtc_dend = LCM_CRTC_DEND_VDENDVAL(var->height) |
2108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			      LCM_CRTC_DEND_HDENDVAL(var->width);
2118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	regs->lcd_crtc_hr = LCM_CRTC_HR_EVAL(var->width + 5) |
2128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			    LCM_CRTC_HR_SVAL(var->width + 1);
2138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	regs->lcd_crtc_hsync = LCM_CRTC_HSYNC_EVAL(hsync + var->hsync_len) |
2148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			       LCM_CRTC_HSYNC_SVAL(hsync);
2158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	regs->lcd_crtc_vr = LCM_CRTC_VR_EVAL(vsync + var->vsync_len) |
2168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			    LCM_CRTC_VR_SVAL(vsync);
2178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
2198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
2218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *	Activate (set) the controller from the given framebuffer
2228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *	information
2238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
2248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic void nuc900fb_activate_var(struct fb_info *info)
2258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
2268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
2278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	void __iomem *regs = fbi->io;
2288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_var_screeninfo *var = &info->var;
2298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int clkdiv;
2308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	clkdiv = nuc900fb_calc_pixclk(fbi, var->pixclock) - 1;
2328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (clkdiv < 0)
2338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		clkdiv = 0;
2348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_calculate_lcd_regs(info, &fbi->regs);
2368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* set the new lcd registers*/
2388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "new lcd register set:\n");
2408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "dccs       = 0x%08x\n", fbi->regs.lcd_dccs);
2418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "dev_ctl    = 0x%08x\n", fbi->regs.lcd_device_ctrl);
2428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "crtc_size  = 0x%08x\n", fbi->regs.lcd_crtc_size);
2438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "crtc_dend  = 0x%08x\n", fbi->regs.lcd_crtc_dend);
2448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "crtc_hr    = 0x%08x\n", fbi->regs.lcd_crtc_hr);
2458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "crtc_hsync = 0x%08x\n", fbi->regs.lcd_crtc_hsync);
2468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "crtc_vr    = 0x%08x\n", fbi->regs.lcd_crtc_vr);
2478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_device_ctrl, regs + REG_LCM_DEV_CTRL);
2498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_crtc_size, regs + REG_LCM_CRTC_SIZE);
2508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_crtc_dend, regs + REG_LCM_CRTC_DEND);
2518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_crtc_hr, regs + REG_LCM_CRTC_HR);
2528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_crtc_hsync, regs + REG_LCM_CRTC_HSYNC);
2538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_crtc_vr, regs + REG_LCM_CRTC_VR);
2548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* set lcd address pointers */
2568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_set_lcdaddr(info);
2578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(fbi->regs.lcd_dccs, regs + REG_LCM_DCCS);
2598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
2608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
2628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *      Alters the hardware state.
2638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *
2648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
2658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_set_par(struct fb_info *info)
2668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
2678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_var_screeninfo *var = &info->var;
2688661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	switch (var->bits_per_pixel) {
2708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 32:
2718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 24:
2728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 18:
2738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 16:
2748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 12:
2758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		info->fix.visual = FB_VISUAL_TRUECOLOR;
2768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
2778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case 1:
2788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		info->fix.visual = FB_VISUAL_MONO01;
2798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
2808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	default:
2818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
2828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
2838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
2848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
2868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* activate this new configuration */
2888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_activate_var(info);
2898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
2908661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
2918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
2928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline unsigned int chan_to_field(unsigned int chan,
2938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang					 struct fb_bitfield *bf)
2948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
2958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	chan &= 0xffff;
2968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	chan >>= 16 - bf->length;
2978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return chan << bf->offset;
2988661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
2998661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3008661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_setcolreg(unsigned regno,
3018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			       unsigned red, unsigned green, unsigned blue,
3028661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			       unsigned transp, struct fb_info *info)
3038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
3048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned int val;
3058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	switch (info->fix.visual) {
3078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	case FB_VISUAL_TRUECOLOR:
3088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		/* true-colour, use pseuo-palette */
3098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		if (regno < 16) {
3108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			u32 *pal = info->pseudo_palette;
3118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			val  = chan_to_field(red, &info->var.red);
3138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			val |= chan_to_field(green, &info->var.green);
3148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			val |= chan_to_field(blue, &info->var.blue);
3158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			pal[regno] = val;
3168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		}
3178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		break;
3188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	default:
3208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return 1;   /* unknown type */
3218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
3228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
3238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
3248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/**
3268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *      nuc900fb_blank
3278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *
3288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
3298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_blank(int blank_mode, struct fb_info *info)
3308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
3318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
3338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
3348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic struct fb_ops nuc900fb_ops = {
3368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.owner			= THIS_MODULE,
3378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_check_var		= nuc900fb_check_var,
3388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_set_par		= nuc900fb_set_par,
3398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_blank		= nuc900fb_blank,
3408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_setcolreg		= nuc900fb_setcolreg,
3418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_fillrect		= cfb_fillrect,
3428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_copyarea		= cfb_copyarea,
3438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.fb_imageblit		= cfb_imageblit,
3448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang};
3458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline void modify_gpio(void __iomem *reg,
3488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			       unsigned long set, unsigned long mask)
3498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
3508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned long tmp;
3518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	tmp = readl(reg) & ~mask;
3528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(tmp | set, reg);
3538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
3548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
3568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * Initialise LCD-related registers
3578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
3588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_init_registers(struct fb_info *info)
3598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
3608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
3618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_mach_info *mach_info = fbi->dev->platform_data;
3628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	void __iomem *regs = fbi->io;
3638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/*reset the display engine*/
3658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(0, regs + REG_LCM_DCCS);
3668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_ENG_RST,
3678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	       regs + REG_LCM_DCCS);
3688661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	ndelay(100);
3698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(readl(regs + REG_LCM_DCCS) & (~LCM_DCCS_ENG_RST),
3708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	       regs + REG_LCM_DCCS);
3718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	ndelay(100);
3728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(0, regs + REG_LCM_DEV_CTRL);
3748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* config gpio output */
3768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	modify_gpio(W90X900_VA_GPIO + 0x54, mach_info->gpio_dir,
3778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		    mach_info->gpio_dir_mask);
3788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	modify_gpio(W90X900_VA_GPIO + 0x58, mach_info->gpio_data,
3798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		    mach_info->gpio_data_mask);
3808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
3828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
3838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
3868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *    Alloc the SDRAM region of NUC900 for the frame buffer.
3878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *    The buffer should be a non-cached, non-buffered, memory region
3888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *    to allow palette and pixel writes without flushing the cache.
3898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
3908661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int __init nuc900fb_map_video_memory(struct fb_info *info)
3918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
3928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
3938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dma_addr_t map_dma;
3948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned long map_size = PAGE_ALIGN(info->fix.smem_len);
3958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(fbi->dev, "nuc900fb_map_video_memory(fbi=%p) map_size %lu\n",
3978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		fbi, map_size);
3988661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
3998661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
4008661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang							&map_dma, GFP_KERNEL);
4018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4028661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (!info->screen_base)
4038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return -ENOMEM;
4048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	memset(info->screen_base, 0x00, map_size);
4068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	info->fix.smem_start = map_dma;
4078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
4098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline void nuc900fb_unmap_video_memory(struct fb_info *info)
4128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
4148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
4158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			      info->screen_base, info->fix.smem_start);
4168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic irqreturn_t nuc900fb_irqhandler(int irq, void *dev_id)
4198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = dev_id;
4218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	void __iomem *regs = fbi->io;
4228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	void __iomem *irq_base = fbi->irq_base;
4238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	unsigned long lcdirq = readl(regs + REG_LCM_INT_CS);
4248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (lcdirq & LCM_INT_CS_DISP_F_STATUS) {
4268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		writel(readl(irq_base) | 1<<30, irq_base);
4278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		/* wait VA_EN low */
4298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		if ((readl(regs + REG_LCM_DCCS) &
4308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		    LCM_DCCS_SINGLE) == LCM_DCCS_SINGLE)
4318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			while ((readl(regs + REG_LCM_DCCS) &
4328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			       LCM_DCCS_VA_EN) == LCM_DCCS_VA_EN)
4338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				;
4348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		/* display_out-enable */
4358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_DISP_OUT_EN,
4368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			regs + REG_LCM_DCCS);
4378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		/* va-enable*/
4388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_VA_EN,
4398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			regs + REG_LCM_DCCS);
4408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	} else if (lcdirq & LCM_INT_CS_UNDERRUN_INT) {
4418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		writel(readl(irq_base) | LCM_INT_CS_UNDERRUN_INT, irq_base);
4428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	} else if (lcdirq & LCM_INT_CS_BUS_ERROR_INT) {
4438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		writel(readl(irq_base) | LCM_INT_CS_BUS_ERROR_INT, irq_base);
4448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
4458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return IRQ_HANDLED;
4478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#ifdef CONFIG_CPU_FREQ
4508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_cpufreq_transition(struct notifier_block *nb,
4528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				       unsigned long val, void *data)
4538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *info;
4558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_info *fbinfo;
4568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	long delta_f;
4578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	info = container_of(nb, struct nuc900fb_info, freq_transition);
4588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo = platform_get_drvdata(to_platform_device(info->dev));
4598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	delta_f = info->clk_rate - clk_get_rate(info->clk);
4618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) ||
4638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	   (val == CPUFREQ_PRECHANGE && delta_f < 0)) {
4648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		info->clk_rate = clk_get_rate(info->clk);
4658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		nuc900fb_activate_var(fbinfo);
4668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
4678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4688661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
4698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi)
4728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->freq_transition.notifier_call = nuc900fb_cpufreq_transition;
4748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return cpufreq_register_notifier(&fbi->freq_transition,
4758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				  CPUFREQ_TRANSITION_NOTIFIER);
4768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *fbi)
4798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	cpufreq_unregister_notifier(&fbi->freq_transition,
4818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				    CPUFREQ_TRANSITION_NOTIFIER);
4828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#else
4848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline int nuc900fb_cpufreq_transition(struct notifier_block *nb,
4858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang				       unsigned long val, void *data)
4868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
4888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4908661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi)
4918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
4938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
4958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *info)
4968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
4978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
4988661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#endif
4998661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5008661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic char driver_name[] = "nuc900fb";
5018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5028661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int __devinit nuc900fb_probe(struct platform_device *pdev)
5038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
5048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi;
5058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_display *display;
5068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_info	   *fbinfo;
5078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_mach_info *mach_info;
5088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct resource *res;
5098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int ret;
5108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int irq;
5118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int i;
5128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int size;
5138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(&pdev->dev, "devinit\n");
5158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	mach_info = pdev->dev.platform_data;
5168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (mach_info == NULL) {
5178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev,
5188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			"no platform data for lcd, cannot attach\n");
5198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return -EINVAL;
5208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
5218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (mach_info->default_display > mach_info->num_displays) {
5238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev,
5248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			"default display No. is %d but only %d displays \n",
5258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			mach_info->default_display, mach_info->num_displays);
5268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return -EINVAL;
5278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
5288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	display = mach_info->displays + mach_info->default_display;
5318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	irq = platform_get_irq(pdev, 0);
5338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (irq < 0) {
5348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev, "no irq for device\n");
5358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return -ENOENT;
5368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
5378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo = framebuffer_alloc(sizeof(struct nuc900fb_info), &pdev->dev);
5398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (!fbinfo)
5408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		return -ENOMEM;
5418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	platform_set_drvdata(pdev, fbinfo);
5438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi = fbinfo->par;
5458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->dev = &pdev->dev;
5468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#ifdef CONFIG_CPU_NUC950
5488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->drv_type = LCDDRV_NUC950;
5498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#endif
5508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
55328f65c11f2ffb3957259dece647a24f8ad2e241bJoe Perches	size = resource_size(res);
5548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->mem = request_mem_region(res->start, size, pdev->name);
5558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (fbi->mem == NULL) {
5568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev, "failed to alloc memory region\n");
5578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		ret = -ENOENT;
5588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto free_fb;
5598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
5608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->io = ioremap(res->start, size);
5628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (fbi->io == NULL) {
5638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev, "ioremap() of lcd registers failed\n");
5648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		ret = -ENXIO;
5658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto release_mem_region;
5668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
5678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5688661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->irq_base = fbi->io + REG_LCM_INT_CS;
5698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* Stop the LCD */
5728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel(0, fbi->io + REG_LCM_DCCS);
5738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* fill the fbinfo*/
5758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	strcpy(fbinfo->fix.id, driver_name);
5768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fix.type		= FB_TYPE_PACKED_PIXELS;
5778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fix.type_aux		= 0;
5788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fix.xpanstep		= 0;
5798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fix.ypanstep		= 0;
5808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fix.ywrapstep		= 0;
5818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fix.accel		= FB_ACCEL_NONE;
5828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.nonstd		= 0;
5838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.activate		= FB_ACTIVATE_NOW;
5848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.accel_flags		= 0;
5858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.vmode		= FB_VMODE_NONINTERLACED;
5868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->fbops			= &nuc900fb_ops;
5878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->flags			= FBINFO_FLAG_DEFAULT;
5888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->pseudo_palette		= &fbi->pseudo_pal;
5898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
590f8798ccbefc0e4ef7438c080b7ba0410738c8cfaYong Zhang	ret = request_irq(irq, nuc900fb_irqhandler, 0,
5918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			  pdev->name, fbinfo);
5928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (ret) {
5938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n",
5948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			irq, ret);
5958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		ret = -EBUSY;
5968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto release_regs;
5978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
5988661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
5998661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->clk = clk_get(&pdev->dev, NULL);
600ec03031707a348578c6ded277e5d92eb21de4fceJamie Iles	if (IS_ERR(fbi->clk)) {
6018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		printk(KERN_ERR "nuc900-lcd:failed to get lcd clock source\n");
602ec03031707a348578c6ded277e5d92eb21de4fceJamie Iles		ret = PTR_ERR(fbi->clk);
6038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto release_irq;
6048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
6058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	clk_enable(fbi->clk);
6078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(&pdev->dev, "got and enabled clock\n");
6088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbi->clk_rate = clk_get_rate(fbi->clk);
6108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* calutate the video buffer size */
6128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	for (i = 0; i < mach_info->num_displays; i++) {
6138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		unsigned long smem_len = mach_info->displays[i].xres;
6148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		smem_len *= mach_info->displays[i].yres;
6158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		smem_len *= mach_info->displays[i].bpp;
6168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		smem_len >>= 3;
6178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		if (fbinfo->fix.smem_len < smem_len)
6188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			fbinfo->fix.smem_len = smem_len;
6198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
6208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	/* Initialize Video Memory */
6228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	ret = nuc900fb_map_video_memory(fbinfo);
6238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (ret) {
6248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		printk(KERN_ERR "Failed to allocate video RAM: %x\n", ret);
6258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto release_clock;
6268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
6278661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	dev_dbg(&pdev->dev, "got video memory\n");
6298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.xres = display->xres;
6318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.yres = display->yres;
6328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	fbinfo->var.bits_per_pixel = display->bpp;
6338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_init_registers(fbinfo);
6358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_check_var(&fbinfo->var, fbinfo);
6378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	ret = nuc900fb_cpufreq_register(fbi);
6398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (ret < 0) {
6408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		dev_err(&pdev->dev, "Failed to register cpufreq\n");
6418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto free_video_memory;
6428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
6438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6448661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	ret = register_framebuffer(fbinfo);
6458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	if (ret) {
6468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		printk(KERN_ERR "failed to register framebuffer device: %d\n",
6478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang			ret);
6488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		goto free_cpufreq;
6498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	}
6508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	printk(KERN_INFO "fb%d: %s frame buffer device\n",
6528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		fbinfo->node, fbinfo->fix.id);
6538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
6558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangfree_cpufreq:
6578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_cpufreq_deregister(fbi);
6588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangfree_video_memory:
6598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_unmap_video_memory(fbinfo);
6608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangrelease_clock:
6618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	clk_disable(fbi->clk);
6628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	clk_put(fbi->clk);
6638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangrelease_irq:
6648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	free_irq(irq, fbi);
6658661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangrelease_regs:
6668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	iounmap(fbi->io);
6678661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangrelease_mem_region:
668a42dcb883d948a5f26c005f0e401e8297aa05f76Dan Carpenter	release_mem_region(res->start, size);
6698661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangfree_fb:
6708661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	framebuffer_release(fbinfo);
6718661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return ret;
6728661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
6738661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6748661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
6758661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang * shutdown the lcd controller
6768661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
6778661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic void nuc900fb_stop_lcd(struct fb_info *info)
6788661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
6798661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = info->par;
6808661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	void __iomem *regs = fbi->io;
6818661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6828661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	writel((~LCM_DCCS_DISP_INT_EN) | (~LCM_DCCS_VA_EN) | (~LCM_DCCS_OSD_EN),
6838661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		regs + REG_LCM_DCCS);
6848661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
6858661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6868661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
6878661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *  Cleanup
6888661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
6898661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_remove(struct platform_device *pdev)
6908661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
6918661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_info *fbinfo = platform_get_drvdata(pdev);
6928661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = fbinfo->par;
6938661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	int irq;
6948661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6958661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_stop_lcd(fbinfo);
6968661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	msleep(1);
6978661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
6986c9571f4b759717e1c938cbc6b53ec8ce5813245Axel Lin	unregister_framebuffer(fbinfo);
6996c9571f4b759717e1c938cbc6b53ec8ce5813245Axel Lin	nuc900fb_cpufreq_deregister(fbi);
7008661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_unmap_video_memory(fbinfo);
7018661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7028661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	iounmap(fbi->io);
7038661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7048661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	irq = platform_get_irq(pdev, 0);
7058661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	free_irq(irq, fbi);
7068661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7078661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	release_resource(fbi->mem);
7088661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	kfree(fbi->mem);
7098661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7108661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	platform_set_drvdata(pdev, NULL);
7118661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	framebuffer_release(fbinfo);
7128661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7138661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
7148661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
7158661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7168661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#ifdef CONFIG_PM
7178661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7188661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang/*
7198661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang *	suspend and resume support for the lcd controller
7208661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang */
7218661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7228661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_suspend(struct platform_device *dev, pm_message_t state)
7238661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
7248661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
7258661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *info = fbinfo->par;
7268661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
727d10df505f81064cf983fb3c5111b004d181a10f5Axel Lin	nuc900fb_stop_lcd(fbinfo);
7288661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	msleep(1);
7298661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	clk_disable(info->clk);
7308661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
7318661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
7328661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7338661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic int nuc900fb_resume(struct platform_device *dev)
7348661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang{
7358661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
7368661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	struct nuc900fb_info *fbi = fbinfo->par;
7378661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7388661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	printk(KERN_INFO "nuc900fb resume\n");
7398661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7408661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	clk_enable(fbi->clk);
7418661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	msleep(1);
7428661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7438661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	nuc900fb_init_registers(fbinfo);
744d10df505f81064cf983fb3c5111b004d181a10f5Axel Lin	nuc900fb_activate_var(fbinfo);
7458661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7468661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	return 0;
7478661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang}
7488661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7498661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#else
7508661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#define nuc900fb_suspend NULL
7518661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#define nuc900fb_resume  NULL
7528661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang#endif
7538661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7548661970875d7d27e4de233d357327fffdb4a5e3dWang Qiangstatic struct platform_driver nuc900fb_driver = {
7558661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.probe		= nuc900fb_probe,
7568661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.remove		= nuc900fb_remove,
7578661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.suspend	= nuc900fb_suspend,
7588661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.resume		= nuc900fb_resume,
7598661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	.driver		= {
7608661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		.name	= "nuc900-lcd",
7618661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang		.owner	= THIS_MODULE,
7628661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang	},
7638661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang};
7648661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7654277f2c4667187cbbdd3da3be31ee681bc6b8300Axel Linmodule_platform_driver(nuc900fb_driver);
7668661970875d7d27e4de233d357327fffdb4a5e3dWang Qiang
7678661970875d7d27e4de233d357327fffdb4a5e3dWang QiangMODULE_DESCRIPTION("Framebuffer driver for the NUC900");
7688661970875d7d27e4de233d357327fffdb4a5e3dWang QiangMODULE_LICENSE("GPL");
769