logo 延续

核心思想:
bootstrap从nand flash 引导logo,进入内核LCD驱动,首先把logo的物理地址映射成虚拟地址,
nuc970fb_activate_var函数中第一次初始化fb时,设置LCD显存物理地址,第二次不设置。这样进入应用程序,对LCD的初始化清屏等操作就不会显示在LCD上,实现logo延续

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/io.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/gpio.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-lcd.h>
#include <mach/mfp.h>
#include <linux/platform_data/video-nuc970fb.h>
#include<mach/regs-gcr.h>

#include "nuc970fb.h"
#include "bmp_layout.h"
#include <asm/memory.h>
#include <linux/vmalloc.h>
#include <linux/memblock.h>
#include <linux/delay.h>


#define logo_phy_addr 0x1d00000

#ifdef CONFIG_ILI9806E_480X800

#define REGFLAG_DELAY   0xFD

#define SPI_CNTRL    0x00
#define SPI_DIVIDER  0x04
#define SPI_SSR      0x08
#define SPI_TX0      0x10

struct LCM_setting_table {
    unsigned char cmd;
    unsigned char count;
    unsigned char para_list[6];
};

static struct LCM_setting_table lcm_spi_init_setting[]={
	{0xFF,	5,	{0xFF, 0x98, 0x06, 0x04, 0x01}},
	{0x08,	1,	{0x10}}
	};

static struct LCM_setting_table ili9806e_init_setting[]={
	{0xFF,	5,	{0xFF, 0x98, 0x06, 0x04, 0x01}},
	{0x08,	1,	{0x10}},
	{0x20,	1,	{0x00}},
	{0x21,	1,	{0X01}},
	{0x30,  1,  {0x02}},	  
	{0x31,  1,  {0x02}},	  
	{0x50,  1,  {0x78}},	  
	{0x51,  1,  {0x78}},	  
	{0x52,  1,  {0x00}},	  
	{0x53,  1,  {0x42}},	  
	{0x57,  1,  {0x50}},	  
	{0x60,  1,  {0x07}},	  
	{0x61,  1,  {0x00}},	  
	{0x62,  1,  {0x07}},	  
	{0x63,  1,  {0x00}},	  
	{0x40,  1,  {0x16}},	  
	{0x41,  1,  {0x33}},	  
	{0x42,  1,  {0x11}},	  
	{0x43,  1,  {0x05}},	  
	{0x44,  1,  {0x0b}},
	{0x45,  1,  {0x1b}},	  
	{0xFF,  5,  {0xFF, 0x98, 0x06, 0x04, 0x01}},	  
	{0xA0,  1,  {0x00}},	  
	{0xA1,  1,  {0x0b}},	  
	{0xA2,  1,  {0x12}},	  
	{0xA3,  1,  {0x0c}},	  
	{0xA4,  1,  {0x05}},	  
	{0xA5,  1,  {0x0c}},	  
	{0xA6,  1,  {0x07}},	  
	{0xA7,  1,  {0x16}},	  
	{0xA8,  1,  {0x06}},	  
	{0xA9,  1,  {0x0A}},	  
	{0xAA,  1,  {0x0F}},	  
	{0xAB,  1,  {0x06}},	  
	{0xAC,  1,  {0x0E}},	  
	{0xAD,  1,  {0x1A}},	  
	{0xAE,  1,  {0x12}},	  
	{0xAF,  1,  {0x00}},	  
	{0xC0,  1,  {0x00}},	  
	{0xC1,  1,  {0x0B}},	  
	{0xC2,  1,  {0x12}},	  
	{0xC3,  1,  {0x0C}},	  
	{0xC4,  1,  {0x05}},	  
	{0xC5,  1,  {0x0C}},	  
	{0xC6,  1,  {0x07}},	  
	{0xC7,  1,  {0x16}},	  
	{0xC8,  1,  {0x06}},	  
	{0xC9,  1,  {0x0A}},	    
	{0xCA,  1,  {0x0F}},	   
	{0xCB,  1,  {0x06}},	  
	{0xCC,  1,  {0x0E}},
	{0xCD,  1,  {0x1A}},	  
	{0xCE,  1,  {0x12}},	  
	{0xCF,  1,  {0x00}},	  
	{0xFF,  5,  {0xFF, 0x98, 0x06, 0x04, 0x06}},		  	  
	{0x00,  1,  {0xA0}},	  
	{0x01,  1,  {0x05}},	   
	{0x02,  1,  {0x00}},	  
	{0x03,  1,  {0x00}},	  
	{0x04,  1,  {0x01}},	  
	{0x05,  1,  {0x01}},
	{0x06,  1,  {0x88}},	  
	{0x07,  1,  {0x04}},	   
	{0x08,  1,  {0x01}},	  
	{0x09,  1,  {0x90}},	  
	{0x0A,  1,  {0x04}},	  
	{0x0B,  1,  {0x01}},
	{0x0C,  1,  {0x01}},	  
	{0x0D,  1,  {0x01}},	   
	{0x0E,  1,  {0x00}},	  
	{0x0F,  1,  {0x00}},	  
	{0x10,  1,  {0x55}},	  
	{0x11,  1,  {0x50}},
	{0x12,  1,  {0x01}},	  
	{0x13,  1,  {0x85}},	   
	{0x14,  1,  {0x85}},	  
	{0x15,  1,  {0xC0}},	  
	{0x16,  1,  {0x0B}},	  
	{0x17,  1,  {0x00}},
	{0x18,  1,  {0x00}},	  
	{0x19,  1,  {0x00}},	   
	{0x1A,  1,  {0x00}},	  
	{0x1B,  1,  {0x00}},	  
	{0x1C,  1,  {0x00}},	  
	{0x1D,  1,  {0x00}},
	{0x20,  1,  {0x01}},	  
	{0x21,  1,  {0x23}},	   
	{0x22,  1,  {0x41}},	//0x45  
	{0x23,  1,  {0x67}},	  
	{0x24,  1,  {0x01}},	  
	{0x25,  1,  {0x23}},	
	{0x26,  1,  {0x45}},	  
	{0x27,  1,  {0x67}},	   
	{0x30,  1,  {0x02}},	  
	{0x31,  1,  {0x22}},	  
	{0x32,  1,  {0x11}},	  
	{0x33,  1,  {0xAA}},
	{0x34,  1,  {0xBB}},	  
	{0x35,  1,  {0x66}},	   
	{0x36,  1,  {0x00}},	  
	{0x37,  1,  {0x22}},	  
	{0x38,  1,  {0x22}},	  
	{0x39,  1,  {0x22}},
	{0x3A,  1,  {0x22}},	  
	{0x3B,  1,  {0x22}},	   
	{0x3C,  1,  {0x22}},	  
	{0x3D,  1,  {0x22}},	  
	{0x3E,  1,  {0x22}},	  
	{0x3F,  1,  {0x22}},
	{0x40,  1,  {0x22}},	  
	{0x52,  1,  {0x10}},	   
	{0x53,  1,  {0x10}},	  
	{0xFF,  5,  {0xFF, 0x98, 0x06, 0x04, 0x07}},	  
	{0x17,  1,  {0x12}},	  
	{0x02,  1,  {0x77}},
	{0xE1,  1,  {0x79}},	  
	{0x26,  1,  {0xB2}},	   
	{0xFF,  5,  {0xFF, 0x98, 0x06, 0x04, 0x00}},	  
	{0x3A,  1,  {0x57}},	  
	{0x11,  1,  {0x00}},	  
	{REGFLAG_DELAY,  120,  {}},
	{0x21,  1,  {0x00}},	  
	{REGFLAG_DELAY,  120,  {}},	   
	{0x29,  1,  {0x00}},	  
	{REGFLAG_DELAY,  120,  {}}
		  
	};	
	

static void push_table(struct LCM_setting_table *table, unsigned int count)	
{
	int index,num;
	unsigned int cmd,data;
	
	for(index = 0; index < count;index ++)
	{
		cmd = table[index].cmd;
		switch(cmd)
		{
			case REGFLAG_DELAY:
				mdelay(table[index].count);
				break;
			default:
				{
					__gpio_set_value(NUC970_PB6,0);
					cmd = cmd +0x0000;
					while ((__raw_readl(NUC970_VA_SPI0+SPI_CNTRL) & 0x01) == 1);
					__raw_writel(cmd,NUC970_VA_SPI0+SPI_TX0);
	    		__raw_writel(0x01|(0x1<<2)|(9<<3),NUC970_VA_SPI0+SPI_CNTRL);
	    		while ((__raw_readl(NUC970_VA_SPI0+SPI_CNTRL) & 0x01) == 1);
	    		__gpio_set_value(NUC970_PB6,1);
	    		udelay(10);
	    		for(num = 0; num < table[index].count;num++)
	    		{
	    			__gpio_set_value(NUC970_PB6,0);
	    			while ((__raw_readl(NUC970_VA_SPI0+SPI_CNTRL) & 0x01) == 1);
	    			data = table[index].para_list[num] + 0x0100;
	    			__raw_writel(data,NUC970_VA_SPI0+SPI_TX0);
	    			__raw_writel(0x01|(0x1<<2)|(9<<3),NUC970_VA_SPI0+SPI_CNTRL);
	    			while ((__raw_readl(NUC970_VA_SPI0+SPI_CNTRL) & 0x01) == 1);
	    			udelay(10);
	    			__gpio_set_value(NUC970_PB6,1);
	    		}
	    		//while ((__raw_readl(NUC970_VA_SPI0+SPI_CNTRL) & 0x01) == 1);
	    		//__gpio_set_value(NUC970_PB6,1);
	    	}
				break;
		}
		
	}
	
	
}
#endif

#define WORD unsigned short
typedef unsigned char BYTE;


int lcd_display_bitmap(bmp_image_t *bmp, struct fb_info *fbinfo)
{
	int c=0;
	int d=0;
	unsigned char *fb;
	unsigned char *bmap;
	short padded_line;
	unsigned long width, height;
	int lcd_line_length=0;

	lcd_line_length=fbinfo->var.xres*2;//一行数据大小
	printk("lcd_line_length %d",lcd_line_length);
	if (!((bmp->header.signature[0]=='B') &&
		(bmp->header.signature[1]=='M'))) {
		printk ("Error: no valid bmp image ");
		return 1;
	}
	
	width = bmp->header.width;
	height = bmp->header.height;

	if(bmp->header.bit_count == 8)
	{
		WORD *addr_line;
		int h,w;
		int bytes_per_pixel;
		//#define CMSG_ALIGN(len)  ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) ) 
		#define RGBto565(r,g,b)    ((WORD)((r>>3)<<11)|(WORD)((g>>2)<<5)|(b>>3))

		bmp_color_table_entry_t *pRgb = bmp->color_table;

		bytes_per_pixel = bmp->header.bit_count/8;
		padded_line = width*bytes_per_pixel;
		//padded_line = CMSG_ALIGN(padded_line);

		
		bmap = (unsigned char *)bmp + bmp->header.data_offset;
		printk("bmp->header.data_offset=%d\n",bmp->header.data_offset);
		fb	 = (fbinfo->screen_base + ( height - 1) * lcd_line_length );
		//fb	 = fbinfo->screen_base;
		for (h = 0; h < height; h++) 
		{	
			c++;
			addr_line = (WORD*)fb;
			for(w = 0; w < width; w++)
			{
				d++;
				int index = bmap[w];
				*(addr_line+w)=RGBto565(pRgb[index].red,pRgb[index].green,pRgb[index].blue);
			}		
			bmap += (padded_line);
			fb	 -= (lcd_line_length);
			//fb	 += (lcd_line_length);
		}
	}
	printk("c=%d d=%d \n",c,d);
	return (0);
}


#ifdef CONFIG_ILI9431_MPU80_240x320
void nuc970_mpu_write_cmd(struct fb_info *info, unsigned short uscmd)
{
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;

	writel(readl(regs+REG_LCM_MPU_CMD) & ~(1<<30), regs+REG_LCM_MPU_CMD);        //RS=0
	writel(readl(regs+REG_LCM_MPU_CMD) & ~(1<<29), regs+REG_LCM_MPU_CMD);        //w

	writel((readl(regs+REG_LCM_DCCS) | (1<<5)), regs+REG_LCM_DCCS);              //CMD ON
	writel(((readl(regs+REG_LCM_MPU_CMD) & ~0xffff) | uscmd), regs+REG_LCM_MPU_CMD);
	while(readl(regs+REG_LCM_MPU_CMD) & (1ul<<31));
	writel((readl(regs+REG_LCM_DCCS) & ~(1<<5)), regs+REG_LCM_DCCS);             //CMD OFF
}

void nuc970_mpu_write_data(struct fb_info *info, unsigned short usdata)
{
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;

	writel(readl(regs+REG_LCM_MPU_CMD) | (1<<30), regs+REG_LCM_MPU_CMD);        //RS=1
	writel(readl(regs+REG_LCM_MPU_CMD) & ~(1<<29), regs+REG_LCM_MPU_CMD);        //w

	writel((readl(regs+REG_LCM_DCCS) | (1<<5)), regs+REG_LCM_DCCS);              //CMD ON
	writel(((readl(regs+REG_LCM_MPU_CMD) & ~0xffff) | usdata), regs+REG_LCM_MPU_CMD);
	while(readl(regs+REG_LCM_MPU_CMD) & (1ul<<31));
	writel((readl(regs+REG_LCM_DCCS) & ~(1<<5)), regs+REG_LCM_DCCS);             //CMD OFF
}

static void init_ili9341(struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;

	writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_DISP_OUT_EN, regs + REG_LCM_DCCS);

	nuc970_mpu_write_cmd(info, 0xCB);
	nuc970_mpu_write_data(info, 0x39);
	nuc970_mpu_write_data(info, 0x2C);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0x34);
	nuc970_mpu_write_data(info, 0x02);

	nuc970_mpu_write_cmd(info, 0xCF);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0xC1);
	nuc970_mpu_write_data(info, 0x30);

	nuc970_mpu_write_cmd(info, 0xE8);
	nuc970_mpu_write_data(info, 0x85);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0x78);

	nuc970_mpu_write_cmd(info, 0xEA);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0x00);

	nuc970_mpu_write_cmd(info, 0xED);
	nuc970_mpu_write_data(info, 0x64);
	nuc970_mpu_write_data(info, 0x03);
	nuc970_mpu_write_data(info, 0x12);
	nuc970_mpu_write_data(info, 0x81);

	nuc970_mpu_write_cmd(info, 0xF7);
	nuc970_mpu_write_data(info, 0x20);

	nuc970_mpu_write_cmd(info, 0xC0);
	nuc970_mpu_write_data(info, 0x23);

	nuc970_mpu_write_cmd(info, 0xC1);
	nuc970_mpu_write_data(info, 0x10);

	nuc970_mpu_write_cmd(info, 0xC5);
	nuc970_mpu_write_data(info, 0x3e);
	nuc970_mpu_write_data(info, 0x28);

	nuc970_mpu_write_cmd(info, 0xC7);
	nuc970_mpu_write_data(info, 0x86);

	nuc970_mpu_write_cmd(info, 0x36);
	nuc970_mpu_write_data(info, 0x48);

	nuc970_mpu_write_cmd(info, 0x3A);
	nuc970_mpu_write_data(info, 0x55);

	nuc970_mpu_write_cmd(info, 0xB1);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0x18);

	nuc970_mpu_write_cmd(info, 0xB6);
	nuc970_mpu_write_data(info, 0x08);
	nuc970_mpu_write_data(info, 0x82);
	nuc970_mpu_write_data(info, 0x27);

	nuc970_mpu_write_cmd(info, 0xF2);
	nuc970_mpu_write_data(info, 0x00);

	nuc970_mpu_write_cmd(info, 0x26);
	nuc970_mpu_write_data(info, 0x01);

	nuc970_mpu_write_cmd(info, 0xE0);
	nuc970_mpu_write_data(info, 0x0F);
	nuc970_mpu_write_data(info, 0x31);
	nuc970_mpu_write_data(info, 0x2B);
	nuc970_mpu_write_data(info, 0x0C);
	nuc970_mpu_write_data(info, 0x0E);
	nuc970_mpu_write_data(info, 0x08);
	nuc970_mpu_write_data(info, 0x4E);
	nuc970_mpu_write_data(info, 0xF1);
	nuc970_mpu_write_data(info, 0x37);
	nuc970_mpu_write_data(info, 0x07);
	nuc970_mpu_write_data(info, 0x10);
	nuc970_mpu_write_data(info, 0x03);
	nuc970_mpu_write_data(info, 0x0E);
	nuc970_mpu_write_data(info, 0x09);
	nuc970_mpu_write_data(info, 0x00);

	nuc970_mpu_write_cmd(info, 0xE1);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0x0E);
	nuc970_mpu_write_data(info, 0x14);
	nuc970_mpu_write_data(info, 0x03);
	nuc970_mpu_write_data(info, 0x11);
	nuc970_mpu_write_data(info, 0x07);
	nuc970_mpu_write_data(info, 0x31);
	nuc970_mpu_write_data(info, 0xC1);
	nuc970_mpu_write_data(info, 0x48);
	nuc970_mpu_write_data(info, 0x08);
	nuc970_mpu_write_data(info, 0x0F);
	nuc970_mpu_write_data(info, 0x0C);
	nuc970_mpu_write_data(info, 0x31);
	nuc970_mpu_write_data(info, 0x36);
	nuc970_mpu_write_data(info, 0x0F);

	//set display location
	nuc970_mpu_write_cmd(info, 0x2a);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 0x00);
	nuc970_mpu_write_data(info, 239>>8);
	nuc970_mpu_write_data(info, 239);

	nuc970_mpu_write_cmd(info, 0x2b);
	nuc970_mpu_write_data(info, 0);
	nuc970_mpu_write_data(info, 0);
	nuc970_mpu_write_data(info, 319>>8);
	nuc970_mpu_write_data(info, 319);

	nuc970_mpu_write_cmd(info, 0x11);
	mdelay(1);

	nuc970_mpu_write_cmd(info, 0x29);    //Display on
	nuc970_mpu_write_cmd(info, 0x2C);    //Write memory

	writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_VA_EN, regs + REG_LCM_DCCS);
}
#endif


/*
 *  Initialize the nuc970 video (dual) buffer address
 */
static void nuc970fb_set_lcdaddr(struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;
	unsigned long vbaddr1, vbaddr2;

	vbaddr1  = info->fix.smem_start;
	vbaddr2  = info->fix.smem_start;
	vbaddr2 += info->fix.line_length * info->var.yres;

	/* set frambuffer start phy addr*/
	writel(vbaddr1, regs + REG_LCM_VA_BADDR0);
	writel(vbaddr2, regs + REG_LCM_VA_BADDR1);

	writel(fbi->regs.lcd_va_fbctrl, regs + REG_LCM_VA_FBCTRL);
	writel(fbi->regs.lcd_va_scale, regs + REG_LCM_VA_SCALE);


}

/*
 *	Check the video params of 'var'.
 */
static int nuc970fb_check_var(struct fb_var_screeninfo *var,
				   struct fb_info *info)
{	
	struct nuc970fb_info *fbi = info->par;
	struct nuc970fb_mach_info *mach_info = fbi->mach_info;
	struct nuc970fb_display *display = NULL;
	struct nuc970fb_display *default_display = mach_info->displays +
						   mach_info->default_display;
	int i;

	dev_dbg(fbi->dev, "check_var(var=%p, info=%p)\n", var, info);

	/* validate x/y resolution */
	/* choose default mode if possible */
	if (var->xres == default_display->xres &&
		var->yres == default_display->yres &&
		var->bits_per_pixel == default_display->bpp)
		display = default_display;
	else
		for (i = 0; i < mach_info->num_displays; i++)
			if (var->xres == mach_info->displays[i].xres &&
				var->yres == mach_info->displays[i].yres &&
				var->bits_per_pixel == mach_info->displays[i].bpp) {
				display = mach_info->displays + i;
				break;
			}

	if (display == NULL) {
		printk(KERN_ERR "wrong resolution or depth %dx%d at %d bit per pixel\n",
			var->xres, var->yres, var->bits_per_pixel);
		return -EINVAL;
	}

	/* it should be the same size as the display */
	var->xres_virtual	= display->xres;
#ifdef CONFIG_NUC970_DUAL_FB
	var->yres_virtual	= display->yres * 2;
#else
	var->yres_virtual	= display->yres;
#endif
	var->height		= display->height;
	var->width		= display->width;

	/* copy lcd settings */
	var->pixclock		= display->pixclock;
	var->left_margin	= display->left_margin;
	var->right_margin	= display->right_margin;
	var->upper_margin	= display->upper_margin;
	var->lower_margin	= display->lower_margin;
	var->vsync_len		= display->vsync_len;
	var->hsync_len		= display->hsync_len;

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

	fbi->regs.lcd_dccs = display->dccs;
	fbi->regs.lcd_device_ctrl = display->devctl;
	fbi->regs.lcd_va_fbctrl = display->fbctrl;
	fbi->regs.lcd_va_scale = display->scale;

	/* set R/G/B possions */
	switch (var->bits_per_pixel) {
	case 1:
	case 2:
	case 4:
	case 8:
	default:
		var->red.offset 	= 0;
		var->red.length 	= var->bits_per_pixel;
		var->green 		= var->red;
		var->blue		= var->red;
		break;
	case 12:
		var->red.length		= 4;
		var->green.length	= 4;
		var->blue.length	= 4;
		var->red.offset		= 8;
		var->green.offset	= 4;
		var->blue.offset	= 0;
		break;
	case 16:
		var->red.length		= 5;
		var->green.length	= 6;
		var->blue.length	= 5;
		var->red.offset		= 11;
		var->green.offset	= 5;
		var->blue.offset	= 0;
		break;
	case 18:
		var->red.length		= 6;
		var->green.length	= 6;
		var->blue.length	= 6;
		var->red.offset		= 12;
		var->green.offset	= 6;
		var->blue.offset	= 0;
		break;
	case 32:
		var->red.length		= 8;
		var->green.length	= 8;
		var->blue.length	= 8;
		var->red.offset		= 16;
		var->green.offset	= 8;
		var->blue.offset	= 0;
		break;
	}

	return 0;
}

/*
 *	Calculate lcd register values from var setting & save into hw
 */
static void nuc970fb_calculate_lcd_regs(const struct fb_info *info,
					struct nuc970fb_hw *regs)
{
	const struct fb_var_screeninfo *var = &info->var;
	int vtt = var->height + var->upper_margin + var->lower_margin;
	int htt = var->width + var->left_margin + var->right_margin;
	int hsync = var->width + var->right_margin;
	int vsync = var->height + var->lower_margin;

	regs->lcd_crtc_size = LCM_CRTC_SIZE_VTTVAL(vtt) |
				  LCM_CRTC_SIZE_HTTVAL(htt);
	regs->lcd_crtc_dend = LCM_CRTC_DEND_VDENDVAL(var->height) |
				  LCM_CRTC_DEND_HDENDVAL(var->width);
	regs->lcd_crtc_hr = LCM_CRTC_HR_EVAL(var->width + 5) |
				LCM_CRTC_HR_SVAL(var->width + 1);
	regs->lcd_crtc_hsync = LCM_CRTC_HSYNC_EVAL(hsync + var->hsync_len) |
				   LCM_CRTC_HSYNC_SVAL(hsync);
	regs->lcd_crtc_vr = LCM_CRTC_VR_EVAL(vsync + var->vsync_len) |
				LCM_CRTC_VR_SVAL(vsync);
}

/*
 *	Activate (set) the controller from the given framebuffer
 *	information
 */
int nuc970fb_initcount=1;
static void nuc970fb_activate_var(struct fb_info *info)
{	
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;

	nuc970fb_calculate_lcd_regs(info, &fbi->regs);

	/* set the new lcd registers*/

	dev_dbg(fbi->dev, "new lcd register set:\n");
	dev_dbg(fbi->dev, "dccs       = 0x%08x\n", fbi->regs.lcd_dccs);
	dev_dbg(fbi->dev, "dev_ctl    = 0x%08x\n", fbi->regs.lcd_device_ctrl);
	dev_dbg(fbi->dev, "crtc_size  = 0x%08x\n", fbi->regs.lcd_crtc_size);
	dev_dbg(fbi->dev, "crtc_dend  = 0x%08x\n", fbi->regs.lcd_crtc_dend);
	dev_dbg(fbi->dev, "crtc_hr    = 0x%08x\n", fbi->regs.lcd_crtc_hr);
	dev_dbg(fbi->dev, "crtc_hsync = 0x%08x\n", fbi->regs.lcd_crtc_hsync);
	dev_dbg(fbi->dev, "crtc_vr    = 0x%08x\n", fbi->regs.lcd_crtc_vr);
	
	writel(fbi->regs.lcd_device_ctrl, regs + REG_LCM_DEV_CTRL);
	writel(fbi->regs.lcd_crtc_size, regs + REG_LCM_CRTC_SIZE);
	writel(fbi->regs.lcd_crtc_dend, regs + REG_LCM_CRTC_DEND);
	writel(fbi->regs.lcd_crtc_hr, regs + REG_LCM_CRTC_HR);
	writel(fbi->regs.lcd_crtc_hsync, regs + REG_LCM_CRTC_HSYNC);
	writel(fbi->regs.lcd_crtc_vr, regs + REG_LCM_CRTC_VR);

	/* set lcd address pointers */
	
	if(nuc970fb_initcount){
		nuc970fb_set_lcdaddr(info);	
	}
	writel(fbi->regs.lcd_dccs, regs + REG_LCM_DCCS);


}

/*
 *      Alters the hardware state.
 *
 */


static int nuc970fb_set_par(struct fb_info *info)
{	
	struct fb_var_screeninfo *var = &info->var;

	switch (var->bits_per_pixel) {
	case 32:
	case 24:
	case 18:
	case 16:
	case 12:
		info->fix.visual = FB_VISUAL_TRUECOLOR;
		break;
	case 1:
		info->fix.visual = FB_VISUAL_MONO01;
		break;
	default:
		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
		break;
	}

	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;

	/* activate this new configuration */
	nuc970fb_activate_var(info);	
	return 0;
}

#define FBIOSET_DISPLAY_PAR		0x46F1

int nuc970_lcdfb_ioctrl(struct fb_info *info, unsigned int cmd,
			unsigned long arg)
{
	
	struct nuc970fb_info *fbi = (struct nuc970fb_info *)info->par;
	struct device *dev = fbi->dev;

	switch (cmd) 
	{		
		case FBIOSET_DISPLAY_PAR:
		{
			dev_info(dev, "lcd is avaliable.\n");					
			nuc970fb_set_lcdaddr(info);			
			printk("free_logo_buffermem\n");

		}
		break;
	}

	return 0;
}

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

static int nuc970fb_setcolreg(unsigned regno,
				   unsigned red, unsigned green, unsigned blue,
				   unsigned transp, struct fb_info *info)
{ 
	unsigned int val;	
	switch (info->fix.visual) {
	case FB_VISUAL_TRUECOLOR:
		/* true-colour, use pseuo-palette */
		if (regno < 16) {
			u32 *pal = info->pseudo_palette;

			val  = chan_to_field(red, &info->var.red);
			val |= chan_to_field(green, &info->var.green);
			val |= chan_to_field(blue, &info->var.blue);
			pal[regno] = val;
		}
		break;

	default:
		return 1;   /* unknown type */
	}
	return 0;
}

/**
 *      nuc970fb_blank
 *
 */
static int nuc970fb_blank(int blank_mode, struct fb_info *info)
{					   
	return 0;
}


#ifdef CONFIG_NUC970_DUAL_FB
static int nuc970fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	unsigned long flags;

	spin_lock_irqsave(&fbi->lock, flags);

	fbi->dual_fb_base = var->yoffset * info->fix.line_length;
	
	spin_unlock_irqrestore(&fbi->lock, flags);

	// printk("pan_display: [0x%x],  %d, %d - %d, %d\n", fbi->dual_fb_base, var->xoffset, var->yoffset, info->var.xoffset, info->var.yoffset);
	return 0;
}
#endif  /* CONFIG_NUC970_DUAL_FB */


static struct fb_ops nuc970fb_ops = {
	.owner			= THIS_MODULE,
	.fb_check_var	= nuc970fb_check_var,
	.fb_set_par		= nuc970fb_set_par,
	.fb_blank		= nuc970fb_blank,
#ifdef CONFIG_NUC970_DUAL_FB
	.fb_pan_display = nuc970fb_pan_display,
#endif
	.fb_setcolreg	= nuc970fb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
	.fb_ioctl       = nuc970_lcdfb_ioctrl,
};


static inline void modify_gpio(void __iomem *reg,
				   unsigned long set, unsigned long mask)
{
	unsigned long tmp;
	tmp = readl(reg) & ~mask;
	writel(tmp | set, reg);
}

/*
 * Initialise LCD-related registers
 */
static int nuc970fb_init_registers(struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;

	/*reset the display engine*/
	if(nuc970fb_initcount){
	writel(0, regs + REG_LCM_DCCS);
	writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_ENG_RST,
	       regs + REG_LCM_DCCS);
	ndelay(100);
	writel(readl(regs + REG_LCM_DCCS) & (~LCM_DCCS_ENG_RST),
	       regs + REG_LCM_DCCS);
	ndelay(100);

	writel(0, regs + REG_LCM_DEV_CTRL);
		}
		
	int ret;
	int gpio_index;
	int mul_gpio;
	struct nuc970fb_mach_info *mach_info = fbi->mach_info;
	mach_info->gpio_blen=0;
 	if(mach_info->gpio_blen)
	{
    	/* control blen pin to let LED on */
		gpio_index = mach_info->gpio_blen & 0xF;
		mul_gpio =NUC970_VA_GCR + 0x70 + 8 *((mach_info->gpio_blen & 0x1F0)/0x20);
		printk("mul_gpio = 0x%x ,gpio_index = 0x%x\n",mul_gpio,gpio_index);
		if(gpio_index > 8)
		{	
			gpio_index =(gpio_index - 8)*4; 
			__raw_writel(__raw_readl(mul_gpio + 4)&~(0xF<<gpio_index),mul_gpio + 4);
    	}
		else
		{
			gpio_index *= 4;
			__raw_writel(__raw_readl(mul_gpio)&~(0xF << gpio_index),mul_gpio);
		}

		ret = gpio_request(mach_info->gpio_blen,NULL);
    	if (ret) 
        	printk("Warning, request gpio fail...\n");
#ifdef CONFIG_COMEN_M300_NUC972
		gpio_direction_output(mach_info->gpio_blen,1);
#else
	gpio_direction_output(mach_info->gpio_blen, 0);
#endif
	}
    return 0;
}

/*
 *    Alloc the SDRAM region of nuc970 for the frame buffer.
 *    The buffer should be a non-cached, non-buffered, memory region
 *    to allow palette and pixel writes without flushing the cache.
 */
static int nuc970fb_map_video_memory(struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	dma_addr_t map_dma;
	unsigned long map_size = PAGE_ALIGN(info->fix.smem_len);

	dev_dbg(fbi->dev, "nuc970fb_map_video_memory(fbi=%p) map_size %lu\n",
		fbi, map_size);

	info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
							&map_dma, GFP_KERNEL);

	if (!info->screen_base)
		return -ENOMEM;

	//memset(info->screen_base, 0x00, map_size);
	info->fix.smem_start = map_dma;

	return 0;
}

static inline void nuc970fb_unmap_video_memory(struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
				  info->screen_base, info->fix.smem_start);
}

static irqreturn_t nuc970fb_irqhandler(int irq, void *dev_id)
{
	struct nuc970fb_info *fbi = dev_id;
	void __iomem *regs = fbi->io;
	void __iomem *irq_base = fbi->irq_base;
	unsigned long lcdirq = readl(regs + REG_LCM_INT_CS);

	if (lcdirq & LCM_INT_CS_DISP_F_STATUS) 
	{
		writel(readl(irq_base) | 1<<30, irq_base);
#ifdef CONFIG_PM
		if(fbi->powerdown) {
			complete(&fbi->completion);
		}
#endif
		/* wait VA_EN low */
		if ((readl(regs + REG_LCM_DCCS) &
			LCM_DCCS_SINGLE) == LCM_DCCS_SINGLE)
			while ((readl(regs + REG_LCM_DCCS) &
				   LCM_DCCS_VA_EN) == LCM_DCCS_VA_EN)
				;
		/* display_out-enable */
		writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_DISP_OUT_EN,
			regs + REG_LCM_DCCS);
		/* va-enable*/
		writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_VA_EN,
			regs + REG_LCM_DCCS);

#ifdef CONFIG_NUC970_DUAL_FB
		if (fbi->dual_fb_base == 0) 
		{
			/* Starting fetch data from VA_BADDR0 */
			writel(readl(regs + REG_LCM_VA_FBCTRL) & ~LCM_VA_FBCTRL_START_BUF, regs + REG_LCM_VA_FBCTRL);		
		}
		else
		{
			/* Starting fetch data from VA_BADDR1 */
			writel(readl(regs + REG_LCM_VA_BADDR0) + fbi->dual_fb_base, regs + REG_LCM_VA_BADDR1);
			writel(readl(regs + REG_LCM_VA_FBCTRL) | LCM_VA_FBCTRL_START_BUF, regs + REG_LCM_VA_FBCTRL);		
		}
#endif

	} 
	else if (lcdirq & LCM_INT_CS_UNDERRUN_INT) 
	{
		writel(readl(irq_base) | LCM_INT_CS_UNDERRUN_INT, irq_base);
	} 
	else if (lcdirq & LCM_INT_CS_BUS_ERROR_INT) 
	{
		writel(readl(irq_base) | LCM_INT_CS_BUS_ERROR_INT, irq_base);
	}
	return IRQ_HANDLED;
}

#ifdef CONFIG_CPU_FREQ

static int nuc970fb_cpufreq_transition(struct notifier_block *nb,
					   unsigned long val, void *data)
{
	struct nuc970fb_info *info;
	struct fb_info *fbinfo;
	long delta_f;
	info = container_of(nb, struct nuc970fb_info, freq_transition);
	fbinfo = platform_get_drvdata(to_platform_device(info->dev));

	delta_f = info->clk_rate - clk_get_rate(info->clk);

	if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) ||
	   (val == CPUFREQ_PRECHANGE && delta_f < 0)) {
		info->clk_rate = clk_get_rate(info->clk);
		nuc970fb_activate_var(fbinfo);
	}

	return 0;
}

static inline int nuc970fb_cpufreq_register(struct nuc970fb_info *fbi)
{
	fbi->freq_transition.notifier_call = nuc970fb_cpufreq_transition;
	return cpufreq_register_notifier(&fbi->freq_transition,
				  CPUFREQ_TRANSITION_NOTIFIER);
}

static inline void nuc970fb_cpufreq_deregister(struct nuc970fb_info *fbi)
{
	cpufreq_unregister_notifier(&fbi->freq_transition,
					CPUFREQ_TRANSITION_NOTIFIER);
}
#else
static inline int nuc970fb_cpufreq_transition(struct notifier_block *nb,
					   unsigned long val, void *data)
{
	return 0;
}

static inline int nuc970fb_cpufreq_register(struct nuc970fb_info *fbi)
{
	return 0;
}

static inline void nuc970fb_cpufreq_deregister(struct nuc970fb_info *info)
{
}
#endif

static char driver_name[] = "nuc970fb";

#ifdef CONFIG_OF
static struct nuc970fb_mach_info *nuc970fb_parse_dt(struct device *dev)
{
	struct nuc970fb_mach_info *mach_info;
	struct nuc970fb_display *display;
	u32 temp;

	mach_info = devm_kzalloc(dev, sizeof(*mach_info), GFP_KERNEL);
	if (!mach_info) {
		dev_err(dev, "memory allocation for fb_info failed\n");
		return ERR_PTR(-ENOMEM);
	}

	display = devm_kzalloc(dev, sizeof(*display), GFP_KERNEL);
	if (!display) {
		dev_err(dev, "memory allocation for fb_info failed\n");
		return ERR_PTR(-ENOMEM);
	}

	printk("\t[%s]-->1\n", __func__);

	mach_info->displays = display;
	mach_info->num_displays = 1;		//fixed to only 1 display
	mach_info->default_display = 0;

	printk("\t[%s]-->2\n", __func__);
	if (of_property_read_u32(dev->of_node, "type", &temp)) {
		dev_warn(dev, "can't get type from dt\n");
		goto read_error;
	} else {
		display->type = temp;
	}

	if (of_property_read_u32(dev->of_node, "bpp", &temp)) {
		dev_warn(dev, "can't get bpp from dt\n");
		goto read_error;
	} else {
		display->bpp = temp;
	}

	if (of_property_read_u32(dev->of_node, "width", &temp)) {
		dev_warn(dev, "can't get width from dt\n");
		goto read_error;
	} else {
		display->width = temp;
	}

	if (of_property_read_u32(dev->of_node, "height", &temp)) {
		dev_warn(dev, "can't get height from dt\n");
		goto read_error;
	} else {
		display->height = temp;
	}

	if (of_property_read_u32(dev->of_node, "xres", &temp)) {
		dev_warn(dev, "can't get xres from dt\n");
		goto read_error;
	} else {
		display->xres = temp;
	}

	if (of_property_read_u32(dev->of_node, "yres", &temp)) {
		dev_warn(dev, "can't get yres from dt\n");
		goto read_error;
	} else {
		display->yres = temp;
	}

	if (of_property_read_u32(dev->of_node, "pixclock", &temp)) {
		dev_warn(dev, "can't get pixclock from dt\n");
		goto read_error;
	} else {
		display->pixclock = temp;
	}

	if (of_property_read_u32(dev->of_node, "left_margin", &temp)) {
		dev_warn(dev, "can't get left_margin from dt\n");
		goto read_error;
	} else {
		display->left_margin = temp;
	}

	if (of_property_read_u32(dev->of_node, "right_margin", &temp)) {
		dev_warn(dev, "can't get right_margin from dt\n");
		goto read_error;
	} else {
		display->right_margin = temp;
	}

	if (of_property_read_u32(dev->of_node, "hsync_len", &temp)) {
		dev_warn(dev, "can't get hsync_len from dt\n");
		goto read_error;
	} else {
		display->hsync_len = temp;
	}

	if (of_property_read_u32(dev->of_node, "upper_margin", &temp)) {
		dev_warn(dev, "can't get upper_margin from dt\n");
		goto read_error;
	} else {
		display->upper_margin = temp;
	}

	if (of_property_read_u32(dev->of_node, "lower_margin", &temp)) {
		dev_warn(dev, "can't get lower_margin from dt\n");
		goto read_error;
	} else {
		display->lower_margin = temp;
	}

	if (of_property_read_u32(dev->of_node, "vsync_len", &temp)) {
		dev_warn(dev, "can't get vsync_len from dt\n");
		goto read_error;
	} else {
		display->vsync_len = temp;
	}

	if (of_property_read_u32(dev->of_node, "dccs", &temp)) {
		dev_warn(dev, "can't get dccs from dt\n");
		goto read_error;
	} else {
		display->dccs = temp;
	}

	if (of_property_read_u32(dev->of_node, "fbctrl", &temp)) {
		dev_warn(dev, "can't get fbctrl from dt\n");
		goto read_error;
	} else {
		display->fbctrl = temp;
	}

	if (of_property_read_u32(dev->of_node, "devctl", &temp)) {
		dev_warn(dev, "can't get devctl from dt\n");
		goto read_error;
	} else {
		display->devctl = temp;
	}

	if (of_property_read_u32(dev->of_node, "scale", &temp)) {
		dev_warn(dev, "can't get scale from dt\n");
		goto read_error;
	} else {
		display->scale = temp;
	}

	if (of_property_read_u32(dev->of_node, "gpio_blen", &temp)) {
		dev_warn(dev, "can't get gpio_blen from dt\n");
		goto read_error;
	} else {
		mach_info->gpio_blen = temp;
	}

	if (of_property_read_u32(dev->of_node, "gpio_lcs", &temp)) {
		dev_warn(dev, "can't get gpio_lcs from dt\n");
		goto read_error;
	} else {
		mach_info->gpio_lcs = temp;
	}

	return mach_info;

read_error:
	devm_kfree(dev, display);
	devm_kfree(dev, mach_info);

	return ERR_PTR(-EINVAL);
}
#else
static struct nuc970fb_mach_info *nuc970fb_parse_dt(struct device *dev)
{
	return dev->platform_data;
}
#endif

static void nuc970fb_stop_lcd(struct fb_info *info);

static int nuc970fb_probe(struct platform_device *pdev)
{
	struct nuc970fb_info *fbi;
	struct nuc970fb_display *display;
	struct fb_info *fbinfo;
	struct nuc970fb_mach_info *mach_info;
	struct resource *res;
	int ret;
	int irq;
	int i;
	int size;
	struct pinctrl *p;
	struct clk *clkmux, *clkuplldiv;
	bmp_image_t *bmp=NULL;
	bmp_image_t *bmpbuf=NULL;
	int bmp_size;
	
	reinit:
	printk("==========nuc970fb_probe==========start=====\r\n");
	
#ifdef CONFIG_ILI9806E_480X800	

	static	struct clk * spi_clk;
	printk("SPI INIT===========\n");
	ret = gpio_request(NUC970_PB9,NULL);
	
	ret = gpio_request(NUC970_PB6,NULL);
	
	ret = gpio_direction_output(NUC970_PB6,1);
	
	__raw_writel((__raw_readl(REG_MFP_GPB_L)&~(0xF<<28))|(0xB<<28),REG_MFP_GPB_L);//SPI0_CLK
	__raw_writel((__raw_readl(REG_MFP_GPB_H)&~(0xF))|(0xB),REG_MFP_GPB_H);//SPI0_MOSI 	
	__raw_writel(0,NUC970_VA_SPI0+SPI_CNTRL);


	spi_clk = clk_get(NULL,"spi0");
    clk_prepare(spi_clk);
    clk_enable(spi_clk);
  
	__raw_writel(7500,NUC970_VA_SPI0+SPI_DIVIDER);
	__raw_writel(0,NUC970_VA_SPI0+SPI_SSR);



	gpio_direction_output(NUC970_PB9,0);
	mdelay(120);
	__gpio_set_value(NUC970_PB9,1);
	mdelay(100);
	__gpio_set_value(NUC970_PB9,0);
	mdelay(100);
	__gpio_set_value(NUC970_PB9,1);
	mdelay(100);
	push_table(lcm_spi_init_setting, sizeof(lcm_spi_init_setting) / sizeof(struct LCM_setting_table));
	push_table(ili9806e_init_setting, sizeof(ili9806e_init_setting) / sizeof(struct LCM_setting_table));
	printk("SPI INIT finish===========\n");
#endif

	mach_info = nuc970fb_parse_dt(&pdev->dev);
	if (mach_info == NULL) {
		dev_err(&pdev->dev,
			"no platform data for lcd, cannot attach\n");
		return -EINVAL;
	}

	if (mach_info->default_display > mach_info->num_displays) {
		dev_err(&pdev->dev,
			"default display No. is %d but only %d displays \n",
			mach_info->default_display, mach_info->num_displays);
		return -EINVAL;
	}

	display = mach_info->displays + mach_info->default_display;


	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "no irq for device\n");
		return -ENOENT;
	}

	fbinfo = framebuffer_alloc(sizeof(struct nuc970fb_info), &pdev->dev);
	if (!fbinfo)
		return -ENOMEM;

	platform_set_drvdata(pdev, fbinfo);

	fbi = fbinfo->par;
	fbi->dev = &pdev->dev;
	fbi->mach_info = mach_info;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

	size = resource_size(res);
	fbi->mem = request_mem_region(res->start, size, pdev->name);
	if (fbi->mem == NULL) {
		dev_err(&pdev->dev, "failed to alloc memory region\n");
		ret = -ENOENT;
		goto free_fb;
	}

	fbi->io = ioremap(res->start, size);
	if (fbi->io == NULL) {
		dev_err(&pdev->dev, "ioremap() of lcd registers failed\n");
		ret = -ENXIO;
		goto release_mem_region;
	}

	fbi->irq_base = fbi->io + REG_LCM_INT_CS;
	
	spin_lock_init(&fbi->lock);
	fbi->dual_fb_base        = 0;

	/* Stop the LCD */
	if(nuc970fb_initcount)
		writel(0, fbi->io + REG_LCM_DCCS);

	/* fill the fbinfo*/
	strcpy(fbinfo->fix.id, driver_name);
	fbinfo->fix.type		= FB_TYPE_PACKED_PIXELS;
	fbinfo->fix.type_aux		= 0;
	fbinfo->fix.xpanstep		= 0;
#ifdef CONFIG_NUC970_DUAL_FB
	fbinfo->fix.ypanstep		= 1;
#else
	fbinfo->fix.ypanstep		= 0;
#endif
	fbinfo->fix.ywrapstep		= 0;
	fbinfo->fix.accel		= FB_ACCEL_NONE;
	fbinfo->var.nonstd		= 0;
	fbinfo->var.activate		= FB_ACTIVATE_NOW;
	fbinfo->var.accel_flags		= 0;
	fbinfo->var.vmode		= FB_VMODE_NONINTERLACED;
	fbinfo->fbops			= &nuc970fb_ops;
	fbinfo->flags			= FBINFO_FLAG_DEFAULT;
	fbinfo->pseudo_palette		= &fbi->pseudo_pal;
#ifdef CONFIG_PM
	fbi->powerdown           = 0;
#endif
#ifdef CONFIG_PM
	init_completion(&fbi->completion);
#endif

	if(nuc970fb_initcount){
		ret = request_irq(irq, nuc970fb_irqhandler, 0,pdev->name, fbi);
		if (ret) {
			dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n",irq, ret);
			ret = -EBUSY;
			goto release_regs;
		}
	}


	clk_prepare(clk_get(NULL, "lcd_hclk"));
	clk_enable(clk_get(NULL, "lcd_hclk"));
	fbi->clk = clk_get(NULL, "lcd_eclk");

	if(display->pixclock > 12000000)
	{
		// change clock source to upll
		clkmux = clk_get(NULL, "lcd_eclk_mux");
		if (IS_ERR(clkmux)) {
			printk(KERN_ERR "Failed to get lcd clock mux control\n");
			ret = PTR_ERR(clkmux);
			return ret;
		}

		clkuplldiv = clk_get(NULL, "lcd_uplldiv");
		if (IS_ERR(clkuplldiv)) {
			printk(KERN_ERR "Failed to get lcd clock divider control\n");
			ret = PTR_ERR(clkuplldiv);
			return ret;
		}

		// select lcd clock from upll
		clk_set_parent(clkmux, clkuplldiv);
	}


	clk_set_rate(fbi->clk, display->pixclock);

	clk_prepare(fbi->clk);
	clk_enable(fbi->clk);
	fbi->clk_rate = clk_get_rate(fbi->clk);
	
	dev_dbg(&pdev->dev, "got and enabled clock\n");

	/* calutate the video buffer size */
	for (i = 0; i < mach_info->num_displays; i++) {
		unsigned long smem_len = mach_info->displays[i].xres;
		smem_len *= mach_info->displays[i].yres;
		smem_len *= mach_info->displays[i].bpp;
		smem_len >>= 3;
		if (fbinfo->fix.smem_len < smem_len)
			fbinfo->fix.smem_len = smem_len;
	}
	
#ifdef CONFIG_NUC970_DUAL_FB
	fbinfo->fix.smem_len *= 2;
#endif
	/* Initialize Video Memory */ 
	ret = nuc970fb_map_video_memory(fbinfo);
	
	fbinfo->var.xres = display->xres;
	fbinfo->var.yres = display->yres;
	fbinfo->var.bits_per_pixel = display->bpp;

	nuc970fb_init_registers(fbinfo);	
	nuc970fb_check_var(&fbinfo->var, fbinfo);
 	ret = nuc970fb_cpufreq_register(fbi);
	if (ret < 0) {
		dev_err(&pdev->dev, "Failed to register cpufreq\n");		
	}
	p = devm_pinctrl_get_select(&pdev->dev, "lcd-24bit");

	ret = register_framebuffer(fbinfo);
	if (ret) {
		printk(KERN_ERR "failed to register framebuffer device: %d\n",
			ret);
		goto free_cpufreq;
	}	
	if(nuc970fb_initcount--){		
		bmp=phys_to_virt(logo_phy_addr);
		bmp_size=bmp->header.file_size;
		printk("bmp->header.file_size = %d\n",bmp->header.file_size);
		bmpbuf=vmalloc(bmp_size);
		memcpy((unsigned char *)bmpbuf,(unsigned char *)bmp,bmp_size);
		lcd_display_bitmap(bmpbuf, fbinfo);
		unregister_framebuffer(fbinfo);	
		goto free_cpufreq;
	}	

#if defined(CONFIG_OF)
	p = devm_pinctrl_get_select_default(&pdev->dev);
#else
 #if defined(CONFIG_FB_LCD_16BIT_PIN)
	p = devm_pinctrl_get_select(&pdev->dev, "lcd-16bit");
 #elif defined(CONFIG_FB_LCD_18BIT_PIN)
	p = devm_pinctrl_get_select(&pdev->dev, "lcd-18bit"); 
 #endif
#endif

	if(IS_ERR(p)) {
		dev_err(&pdev->dev, "unable to reserve pin\n");
		ret = PTR_ERR(p);
	}
	printk(KERN_INFO "fb%d: %s frame buffer device\n",
		fbinfo->node, fbinfo->fix.id);

#ifdef CONFIG_ILI9431_MPU80_240x320
	init_ili9341(fbinfo);
#endif

#ifdef CONFIG_PM
	writel(readl(fbi->io + REG_LCM_DCCS) | LCM_DCCS_DISP_INT_EN, fbi->io + REG_LCM_DCCS);
	writel(readl(fbi->io +  REG_LCM_INT_CS) | LCM_INT_CS_DISP_F_EN, fbi->io + REG_LCM_INT_CS);
#endif

	printk("==========nuc970fb_probe==========end=====\r\n");	
	return 0;
free_cpufreq:
	nuc970fb_cpufreq_deregister(fbi);
/*
free_video_memory:
	nuc970fb_unmap_video_memory(fbinfo);
release_clock:	
	printk("look here\n");
	clk_disable(fbi->clk);
	clk_put(fbi->clk);
	free_irq(irq, fbi);
	*/
release_regs:
	iounmap(fbi->io);
release_mem_region:
	release_mem_region(res->start, size);
free_fb:
	framebuffer_release(fbinfo);
	goto reinit;
	return ret;
}

/*
 * shutdown the lcd controller
 */
static void nuc970fb_stop_lcd(struct fb_info *info)
{
	struct nuc970fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;

	writel((~LCM_DCCS_DISP_INT_EN) | (~LCM_DCCS_VA_EN) | (~LCM_DCCS_OSD_EN),
		regs + REG_LCM_DCCS);
}

/*
 *  Cleanup
 */
static int nuc970fb_remove(struct platform_device *pdev)
{
	struct fb_info *fbinfo = platform_get_drvdata(pdev);
	struct nuc970fb_info *fbi = fbinfo->par;
	int irq;

	nuc970fb_stop_lcd(fbinfo);
	msleep(1);

	unregister_framebuffer(fbinfo);
	nuc970fb_cpufreq_deregister(fbi);
	nuc970fb_unmap_video_memory(fbinfo);

	iounmap(fbi->io);

	irq = platform_get_irq(pdev, 0);
	free_irq(irq, fbi);

	release_resource(fbi->mem);
	kfree(fbi->mem);

#if defined(CONFIG_OF)
	devm_kfree(&pdev->dev, fbi->mach_info->displays);
	devm_kfree(&pdev->dev, fbi->mach_info);
#endif

	platform_set_drvdata(pdev, NULL);
	framebuffer_release(fbinfo);

	return 0;
}

#ifdef CONFIG_PM
/*
 *	suspend and resume support for the lcd controller
 */
#define NUC970_FB_TIMEOUT	(msecs_to_jiffies(5000))
static int nuc970fb_suspend(struct platform_device *dev, pm_message_t state)
{
	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
	struct nuc970fb_info *info = fbinfo->par;
	unsigned long timeout;

	printk(KERN_INFO "fb suspend\n");
	info->powerdown = 1;

	timeout = wait_for_completion_interruptible_timeout
			 (&info->completion, NUC970_FB_TIMEOUT);

	nuc970fb_stop_lcd(fbinfo);

	return 0;
}

static int nuc970fb_resume(struct platform_device *dev)
{
	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
	struct nuc970fb_info *info = fbinfo->par;

	printk(KERN_INFO "fb resume\n");

	info->powerdown = 0;

	nuc970fb_init_registers(fbinfo);
	nuc970fb_activate_var(fbinfo);

	writel(readl(info->io + REG_LCM_DCCS) | LCM_DCCS_DISP_INT_EN, info->io + REG_LCM_DCCS);
	writel(readl(info->io +  REG_LCM_INT_CS) | LCM_INT_CS_DISP_F_EN, info->io + REG_LCM_INT_CS);

#ifdef CONFIG_ILI9431_MPU80_240x320
	init_ili9341(fbinfo);
#endif
	return 0;
}

#else
#define nuc970fb_suspend NULL
#define nuc970fb_resume  NULL
#endif

#if defined(CONFIG_OF)
static const struct of_device_id nuc970_lcd_of_match[] = {
	{   .compatible = "nuvoton,nuc970-lcd"},
	{  },
};
MODULE_DEVICE_TABLE(of, nuc970_lcd_of_match);
#endif

static struct platform_driver nuc970fb_driver = {
	.probe		= nuc970fb_probe,
	.remove		= nuc970fb_remove,
	.suspend	= nuc970fb_suspend,
	.resume		= nuc970fb_resume,
	.driver		= {
		.name	= "nuc970-lcd",
		.owner	= THIS_MODULE,
#if defined(CONFIG_OF)
		.of_match_table = of_match_ptr(nuc970_lcd_of_match),
#endif
	},
};

module_platform_driver(nuc970fb_driver);

MODULE_DESCRIPTION("Framebuffer driver for the nuc970/N9H30");
MODULE_LICENSE("GPL");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ID太难取

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值