lcd驱动详细程序

这个程序来自于linux内核,加以自己改编,还是内核的程序好写一点

比较长,都贴出来 。以后自己留看

主要是framebuffer各个结构的初始化,以及注册,probe函数最关键

可以单独编译,使用通用的module  makefile编译成.ko格式。在操作系统没有lcd驱动的情况下加载,但是有驱动的情况下,就加载不上去了。
程序列表编译成功。加载上去使用不成功。成功的例程 可以去  /liunx/drivers/video/s3c2410fb.c  下查看

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/fb.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/mm.h> 
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/div64.h>
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>
#include <linux/pm.h> 

#include "s3c2410fb.h"


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/fb.h>
#include <linux/slab.h>
#include <asm/types.h>
#include <asm/io.h>
#include "fb_draw.h"


#ifdef CONFIG_FB_S3C2410_DEBUG
 static int debug	 = 1;
#else
 static int debug	 = 0;
#endif

#define dprintk(msg...)	if (debug) { printk(KERN_DEBUG "s3c2410fb: " msg); }


  struct jakillfb_var {
  				int lcd_irq_no;
				struct clk *lcd_clock;
				struct  resource *lcd_men;
				void __iomem * lcd_base;
				
				struct device *dev;
				struct s3c2410fb_hw regs;
				u32 paletee_buffer[256];
				u32 pseudo_pal[16];
				unsigned int paletee_ready;
  	};



//lcdaddr1 ~3  install
//this fuc is hard to write,because didn't remenber that
//saddr1 store in fbinfo->fix.smen_start and so on
//this
static void jakill2410fb_set_addr(struct fb_info *fbinfo)
{
	unsigned saddr1 ,saddr2,saddr3;
	struct jakillfb_var *fbvar=fbinfo->par;
	void __iomem *regs=fbvar->lcd_base;
	saddr1=fbinfo->fix.smem_start>>1;
	//addr2=start+linelength*yres
	saddr2=fbinfo->fix.smem_start+
		fbinfo->fix.line_length*fbinfo->var.yres;
	saddr2>>=1;
	//low 10 bits
	saddr3=S3C2410_OFFSIZE(0)|S3C2410_PAGEWIDTH((fbinfo->fix.line_length/2)&0x3ff);
	writel(saddr1,regs+S3C2410_LCDSADDR1);
	writel(saddr2,regs+S3C2410_LCDSADDR2);
	writel(saddr3,regs+S3C2410_LCDSADDR3);
	
}
//pixclk=1/VCLK,fbvar->lcd_clcok=HCLK;
//clkdiv=HCLK/VCLK,ze  clkdiv-1=CLKVAL
static int jakill2410fb_cal_pixclk(struct jakillfb_var *fbvar,unsigned long long pixclk)
{
	
	unsigned long long lcd_clk=clk_get_rate(fbvar->lcd_clock);
	unsigned long long div=lcd_clk*pixclk;
	div>>=12;
	do_div(div,625*625UL*625);
	return div;
	
}

/*
//set lcdcon 1~5, for detial in BPP,VFPD,VSPW,HFPD,HBPD,HSPW
//with jakill2410_activat_var together to activate the var of fb_info
*/
//check the varibla figures in fbinfo
//mach_info=fbinfo->dev->platform_data
//the displays +default_display+displays in struct mach_info ,store the display mode for var
//var can chouse some in it
static int jakillfb_check_var(struct fb_info *fbinfo)
{
	unsigned i;
	struct fb_var_screeninfo *var=&fbinfo->var;
	struct jakillfb_var *fbvar=fbinfo->par;
	struct s3c2410fb_mach_info  *mach_info=fbinfo->dev->platform_data;
	struct s3c2410fb_display * display=NULL;
	struct s3c2410fb_display *default_display=mach_info->displays+mach_info->default_display;
	int type=default_display->type;
	if(var->yres==default_display->yres&&
		var->xres==default_display->xres&&
		var->bits_per_pixel==default_display->bpp)
		{
			display=default_display;
			
		}
	else{
			for(i=0;i<mach_info->num_displays;i++)
				{
					if(type=mach_info->displays[i].type&&
						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)
		{
			return -EINVAL;
			
		}
	//fbvar->regs.lcdcon1=display->type;
	//fbvar->regs.lcdcon5=display->lcdcon5;
	var->xres_virtual=display->xres;
	var->yres_virtual=display->yres;
	var->height=display->height;
	var->width=display->width;

	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;
	switch(var->bits_per_pixel)
		{
			case 1:
			case 2:
			case 4:
				var->red.offset=0;
				var->red.length=var->bits_per_pixel;
				var->green=  var->red;
				var->blue=var->blue;
			case 8: //8bpp 332
				if(display->type==S3C2410_LCDCON1_TFT)
					{
						var->red.offset=5;
						var->red.length=3;
						var->green.length=3;
						var->green.offset=2;
						var->blue.length=2;
						var->blue.offset=0;
					}
				else{
						  var->red.offset     = 0;
               				 var->red.length     = 8;
               				 var->green          = var->red;
               				 var->blue           = var->red;

					}
				break;
			case 12:/* 12 bpp 444 */
			            var->red.length         = 4;
			            var->red.offset         = 8;
			            var->green.length       = 4;
			            var->green.offset       = 4;
			            var->blue.length        = 4;
			            var->blue.offset        = 0;
			            break;
        		case 16:/* 16 bpp */
			            if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) 
			            {
			                /* 565 format */
			                var->red.offset      = 11;
			                var->green.offset    = 5;
			                var->blue.offset     = 0;
			                var->red.length      = 5;
			                var->green.length    = 6;
			                var->blue.length     = 5;
			            } else {
			                /* 5551 format */
			                var->red.offset      = 11;
			                var->green.offset    = 6;
			                var->blue.offset     = 1;

					var->red.length      = 5;
			                var->green.length    = 5;
			                var->blue.length     = 5;
			            }
			            break;
        		case 32:/* 24 bpp 888 and 8 dummy */
			            var->red.length        = 8;
			            var->red.offset        = 16;
			            var->green.length      = 8;
			            var->green.offset      = 8;
			            var->blue.length       = 8;
			            var->blue.offset       = 0;
			            break;

				
				
		}
	
	
	
}


static void jakillfb_config_lcd_tft_regs(struct fb_info *fbinfo ,struct s3c2410fb_hw *regs)
{	
	struct jakillfb_var *fbvar=fbinfo->par;
	struct fb_var_screeninfo  *var=&fbinfo->var;
	//lcdcon1,5 ,for bpp
	switch(var->bits_per_pixel)
		{
			case 1:
				fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_TFT1BPP;
				break;
			case 2:
				fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_TFT1BPP;
				break;
			case 4:
				fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_TFT4BPP;
				break;	
			case 8:
				//SWAP enable
				fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_TFT8BPP;
				fbvar->regs.lcdcon5 |=S3C2410_LCDCON5_BSWP |S3C2410_LCDCON5_FRM565;
				fbvar->regs.lcdcon5 &=~S3C2410_LCDCON5_HWSWP;
				break;
			case 16:
				fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_TFT16BPP;
				fbvar->regs.lcdcon5 |=S3C2410_LCDCON5_HWSWP;
				fbvar->regs.lcdcon5 &=~S3C2410_LCDCON5_BSWP;
				break;
			case 32:
				fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_TFT24BPP;
				fbvar->regs.lcdcon5 &=~(S3C2410_LCDCON5_BSWP|S3C2410_LCDCON5_HWSWP|
					S3C2410_LCDCON5_BPP24BL);
				break;			
			default:
				dev_err(fbvar->dev,"invalid bpp %d",var->bits_per_pixel);
				break;
				
		}
	regs->lcdcon2=S3C2410_LCDCON2_LINEVAL(var->yres-1)|
				S3C2410_LCDCON2_VBPD(var->upper_margin-1)|
				S3C2410_LCDCON2_VFPD(var->lower_margin-1)|
				S3C2410_LCDCON2_VSPW(var->vsync_len-1);
	regs->lcdcon3=
					S3C2410_LCDCON3_HBPD(var->right_margin-1)|
					S3C2410_LCDCON3_HFPD(var->left_margin-1)|
					S3C2410_LCDCON3_HOZVAL(var->xres-1);
	regs->lcdcon4=S3C2410_LCDCON4_HSPW(var->hsync_len-1);
	
	
}
 static void jakillfb_config_lcd_stn_regs(struct fb_info *fbinfo,struct s3c2410fb_hw *regs)
{	//attention  ,here is a  linked list struct
	//fbvar chaged,then fb_info will chaged at the same time
	struct jakillfb_var *fbvar=fbinfo->par;
	struct fb_var_screeninfo  *var=&fbinfo->var;
	int type=regs->lcdcon1 &=~S3C2410_LCDCON1_TFT;
	int hs=var->xres>>2;
	unsigned wdly=(var->left_margin>>4)-1;
	unsigned wlh=(var->hsync_len>>4)-1;
	  if (type != S3C2410_LCDCON1_STN4)
    {
        hs >>= 1;
    }

    /*¸ù¾ÝɫλģʽÉèÖÃLCD¿ØÖƼĴæÆ÷1£¬²Î¿¼Êý¾ÝÊÖ²á*/
    switch (var->bits_per_pixel) 
    {
        case 1:/*1BPP*/
            regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP;
            break;
        case 2:/*2BPP*/
            regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY;
            break;
        case 4:/*4BPP*/
            regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY;
            break;
        case 8:/*8BPP*/
            regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP;
            hs *= 3;
            break;
        case 12:/*12BPP*/
            regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP;
            hs *= 3;
            break;
        default:/*ÎÞЧµÄBPP*/
            dev_err(fbvar->dev, "invalid bpp %d\n", var->bits_per_pixel);
    }
    
    /*ÉèÖÃLCDÅäÖüĴæÆ÷2¡¢3¡¢4, ²Î¿¼Êý¾ÝÊÖ²á*/
    if (wdly > 3) wdly = 3;
    if (wlh > 3) wlh = 3;
    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1);

    regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) |
            S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) |
            S3C2410_LCDCON3_HOZVAL(hs - 1);

    regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh);
}



//fbvar shi jakill2410fb_var data struct ;while var is fb_var_screeninfo type
//purts var->regs into s3c2410 regs;
//lcdcon1~5,clkdiv,lcdaddr1~3,enable lcd
//with jakill2410fb_config_lcd_config_tft, fullly make the registers of LCDCON full
//thus the LCD controller can work
static void  jakillfb_activate_var(struct fb_info *fbinfo)
{
	struct jakillfb_var *fbvar=fbinfo->par;//fb_info  tail chouseble datastruct
	void __iomem *regs=fbvar->lcd_base;//for writel,important
	
	struct fb_var_screeninfo *var=&fbinfo->var;
	int clkdiv=jakill2410fb_cal_pixclk(fbvar,var->pixclock);
	int type=fbvar->regs.lcdcon1&S3C2410_LCDCON1_TFT;
	
	if(type==S3C2410_LCDCON1_TFT)
		{
			jakillfb_config_lcd_tft_regs(fbinfo,&fbvar->regs);
			--clkdiv;
			if(clkdiv<0)
				clkdiv=0;
		
			
		}
	else{
			jakillfb_config_lcd_stn_regs(fbinfo,&fbvar->regs);
			if(clkdiv<2)
				clkdiv=2;
			
		}
		fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_CLKVAL(clkdiv);
		writel(fbvar->regs.lcdcon1,regs+S3C2410_LCDCON1);
		writel(fbvar->regs.lcdcon2,regs+S3C2410_LCDCON2);
		writel(fbvar->regs.lcdcon3,regs+S3C2410_LCDCON3);
		writel(fbvar->regs.lcdcon4,regs+S3C2410_LCDCON4);
		writel(fbvar->regs.lcdcon5,regs+S3C2410_LCDCON5);
		jakill2410fb_set_addr(fbinfo); //write by myself
		fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_ENVID;//shi neng lcd kongzhiqi,Enable the lcd
		
	
}



static int jakill2410fb_set_var(struct fb_info *fbinfo)
{	
	struct fb_var_screeninfo *var=&fbinfo->var;
	switch(var->bits_per_pixel)
		{
			case 32:
			case 16:
				fbinfo->fix.visual=FB_VISUAL_TRUECOLOR;
				break;
			case 12:
			case 1:
				fbinfo->fix.visual=FB_VISUAL_MONO01;
				break;
			default:
				fbinfo->fix.visual=FB_VISUAL_PSEUDOCOLOR;

				break;
		}
	fbinfo->fix.line_length=(var->bits_per_pixel*var->xres_virtual)/8;
	jakillfb_activate_var(fbinfo);//write by myself
	
	
}




//update the paletee
static void schedule_paletee_update(struct  jakillfb_var *fbvar,unsigned int regno,unsigned int val)
{
	unsigned long flags;
	unsigned long irqen;
//suspend irq base addr
//lcd_iqr_base=0x4D00 0000,S3C24XX_LCDINTBASE=0X54
//then lcd_base=0x4D00 000
	void __iomem *lcd_irq_base=fbvar->lcd_base +S3C2410_LCDINTBASE;
//mask irq and save cpu stat into flags
	local_irq_save(flags);
	fbvar->paletee_buffer[regno]=val;
	//S3C2410_LCDINTMSK =0x5C,then  lcd_irq_base=0x4D00 0000
	if(!fbvar->paletee_ready)
		{
			fbvar->paletee_ready=1;
			//S3C24XX_LCDINTMSK=0x08
			//lcd_irq_base=0x4D00 0000
			irqen=readl(lcd_irq_base+S3C24XX_LCDINTMSK);
			//intiruptible  for lcd  frambuffer is availble
			irqen&=~S3C2410_LCDINT_FRSYNC;
			writel(irqen,lcd_irq_base+S3C24XX_LCDINTMSK);
			
		}
	//resume the cpu register state
	local_irq_restore(flags);
}


//make the color like red and so on into the right field int 16bits
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  jakill2410fb_set_colreg(unsigned regno,unsigned red,unsigned  green,unsigned blue,
						unsigned transp,struct fb_info * fbinfo)
{
	unsigned int val;
	struct jakillfb_var *fbvar=fbinfo->par;
	void __iomem *regs=fbvar->lcd_base;
	switch(fbinfo->fix.visual)
		{
			case FB_VISUAL_TRUECOLOR:
				if (regno<16)
					{
						u32 *pal=fbvar->pseudo_pal;
						val=chan_to_field(red,&fbinfo->var.red);
						val|=chan_to_field(green,&fbinfo->var.green);
						val|=chan_to_field(blue,&fbinfo->var.blue);
						pal[regno]=val;
					}
				break;
			case FB_VISUAL_PSEUDOCOLOR:
				if(regno<256)
					{
						val=(red>>0)&0xf800;
						val|=(green>>5)&0x07e0;
						val|=(blue>>11)&0x001f;
						//insert the var into paletee int s3c2410,and
				//the start addr of s3c2410 is 0x4D00 0400
				//from datasheet s3c2440 416
						writel(val,regs+S3C2410_TFTPAL(regno));
						schedule_paletee_update(fbvar,regno,val);
					}
			default:
				return 1;
				
		}
	return 0;
}

//enable the lcd
static void jakillfb_lcd_enable(struct jakillfb_var * fbvar,int enable)
{
	unsigned long flags;
	local_irq_save(flags);
	if(enable)
		{
			fbvar->regs.lcdcon1 |=S3C2410_LCDCON1_ENVID;
			
		}
	else{
			fbvar->regs.lcdcon1 &=~S3C2410_LCDCON1_ENVID;
			
		}
	local_irq_restore(flags);
}

//MAKE BLAK MODULE according with the blankmode
static void jakill2410fb_blank(int blankmode,struct fb_info *fbinfo)
{
	struct jakillfb_var * fbvar=fbinfo->par;
	void __iomem *regs=fbvar->lcd_base;
	

			if(blankmode ==FB_BLANK_POWERDOWN)
				jakillfb_lcd_enable(fbvar,0);
			else
				jakillfb_lcd_enable(fbvar,1);
			
			if(blankmode==FB_BLANK_UNBLANK)
				//disable paletee_register 
				writel(0,regs+S3C2410_TPAL);
			else
				writel(1,regs+S3C2410_TPAL);
			

}



static int s3c2410fb_debug_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off");
}
//µ÷ÓÃÕß  DEVICE_ATTR
static int s3c2410fb_debug_store(struct device *dev,
				 struct device_attribute *attr,
				 const char *buf, size_t len)
{
	if (len < 1)
		return -EINVAL;

	if (strnicmp(buf, "on", 2) == 0 ||
	    strnicmp(buf, "1", 1) == 0) {
		debug = 1;
		printk(KERN_DEBUG "s3c2410fb: Debug On");
	} else if (strnicmp(buf, "off", 3) == 0 ||
		   strnicmp(buf, "0", 1) == 0) {
		debug = 0;
		printk(KERN_DEBUG "s3c2410fb: Debug Off");
	} else {
		return -EINVAL;
	}

	return len;
}

static DEVICE_ATTR(debug, 0666, s3c2410fb_debug_show, s3c2410fb_debug_store);


static struct fb_ops jakillfb_ops=
{
		.owner=THIS_MODULE,
		.fb_check_var=jakillfb_check_var,
		.fb_set_par=jakill2410fb_set_var,
		.fb_blank=jakill2410fb_blank,
		.fb_setcolreg=jakill2410fb_set_colreg,
		.fb_fillrect=cfb_fillrect,
		.fb_copyarea=cfb_copyarea,
		.fb_imageblit=cfb_imageblit,
};

//mmap for video in fbinfo
static int __init jakillfb_map_video_memory(struct fb_info *fbinfo)
{
	dma_addr_t    map_dma;
	unsigned mapsize=PAGE_ALIGN(fbinfo->fix.smem_len);
	//screen _base is vitual addr
	//allco a lenth of addr  of mapsize,and the dma_addr is  map_dma
	//so for it 
	//fbinfo->screen_base is vitural ;while   &map_dma,is physical,the two addr are the same ,write every one
	//will change the mem-data;
	fbinfo->screen_base=dma_alloc_writecombine(fbinfo->dev,mapsize,&map_dma,GFP_KERNEL);
	if(fbinfo->screen_base)
		{
                	memset(fbinfo->screen_base,0x00,mapsize);
			//set map_dma  to  fbinfo->fix.smen_len
			//map_dma is  system   write dma_addr;
			//when write dma,the data will be save int the vitual addr,
			//and int the end   write into the phy addr
			//fbinfo->fix.sem_start is pys-addr
			fbinfo->fix.smem_start=map_dma;
			
		}
	return 0;
}
static __inline void jakillfb_umap_video_memory(struct fb_info  *fbinfo)
{
		unsigned mapsize=PAGE_ALIGN(fbinfo->fix.smem_len);
	//fbinfo->fix.screen_base is vittual addr ,while  fbinfo->fix.smen_start is physical addr
	dma_free_writecombine(fbinfo->dev,mapsize,fbinfo->screen_base,fbinfo->fix.smem_start);
	}
//insertcolor red,green ,blue to val,and write val in the adaptible register



/*----------------------------------------------------------------------------
-----------------------------------------------------------------------------------
*********************************************************************************
*********************************************************************************
		second part  jakill2410fb_probe,init_registers,map_video_memory,register_framebuffer
		device_create_file
		irq_fb_irq,write_paletee,modify_gpio,
*********************************************************************************
*/
//base probe
//modify gpio base func
static inline void modify_gpio(void __iomem  *regs,unsigned long set,unsigned long mask)
{
	unsigned long temp;
	temp=readl(regs)&(~mask);
	writel(temp|set,regs);
	
}
static void jakillfb_init_registers(struct fb_info *fbinfo)
{
	struct jakillfb_var* fbvar=fbinfo->par;
	unsigned long flags;
	struct s3c2410fb_mach_info *mach_info=fbvar->dev->platform_data;
	void __iomem *tpal;
	void __iomem *lpcsel;
	
	tpal=fbvar->lcd_base+S3C2410_TPAL;

	lpcsel=fbvar->lcd_base+S3C2410_LPCSEL;
	local_irq_save(flags);
	
	modify_gpio(S3C2410_GPCUP,mach_info->gpcup,mach_info->gpcup_mask);
	modify_gpio(S3C2410_GPCCON,mach_info->gpccon,mach_info->gpccon_mask);
	modify_gpio(S3C2410_GPDUP,mach_info->gpdup,mach_info->gpdup_mask);
	modify_gpio(S3C2410_GPDCON,mach_info->gpdcon,mach_info->gpdcon_mask);
	local_irq_restore(flags);
	writel(0x00,tpal);
	writel(mach_info->lpcsel,lpcsel);
	
}

static void jakillfb_write_paletee(struct jakillfb_var * fbvar)
{
	unsigned long i;
	unsigned long  ent;
	void __iomem  *reg=fbvar->lcd_base;
	fbvar->paletee_ready=0;
	for(i=0;i<256;i++)
		{
			ent=fbvar->paletee_buffer[i];
			if(ent==PALETTE_BUFF_CLEAR)
				{
					continue;
				}
			writel(ent,reg+S3C2410_TFTPAL(i));
			//when we have truelly write the register
			//we should clear the s3c2410 fbvar->paletee_buffer[i];
			if(readw(reg+S3C2410_TFTPAL(i))==ent)
				{
					fbvar->paletee_buffer[i]=PALETTE_BUFF_CLEAR;
					
				}
			else
				fbvar->paletee_ready=1;
			
		}
}


static irqreturn_t  jakillfb_lcd_irq(int irq ,void *dev_id)
{
	struct  jakillfb_var *fbvar=dev_id;
	unsigned long  lcdirq;
	void __iomem *lcd_irq_base =fbvar->lcd_base+S3C2410_LCDINTBASE;
	lcdirq=readl(lcd_irq_base+S3C2410_LCDINTPND);
	if(lcdirq&S3C2410_LCDINT_FRSYNC)
		{
			if(fbvar->paletee_ready)
				{
					jakillfb_write_paletee(fbvar);
				}
			writel(S3C2410_LCDINT_FRSYNC,lcd_irq_base+S3C24XX_LCDINTPND);
			writel(S3C2410_LCDINT_FRSYNC,lcd_irq_base+S3C24XX_LCDSRCPND);
		}
	return   IRQ_HANDLED;
	
	
}
 static  char  driver_name[]="jakilllcd_driver";//remove the device


 static int  __devinit jakilllcd_fb_probe(struct platform_device *pdev)
{
	int i;
	int ret;
	struct resource *res;
	struct fb_info *fbinfo;
	struct s3c2410fb_display *display;
	struct s3c2410fb_mach_info *mach_info;
	struct jakillfb_var * fbvar;
	//mach_info
	mach_info=pdev->dev.platform_data;
	if(mach_info==NULL)
		{
			dev_err(&pdev->dev,"get platform_data erro\n");
			return -EINVAL;
			
		}
	display=mach_info->displays+mach_info->default_display;
	if(display==NULL)
		{
			dev_err(&pdev->dev,"get display erro\n");
			return -EINVAL;
		}
	//when request for memory,framebuffer_alloc will argv[1]+fbinfo
	fbinfo=framebuffer_alloc(sizeof(struct jakillfb_var),&pdev->dev);
	if(fbinfo==NULL)
		{
			dev_err(&pdev->dev,"framebuffer_alloc failed\n");
			ret=-ENOMEM;
			goto err_noirq;
			
		}
	
	//pdev->dev->drive_data =fbinfo
	//insert the fbinfo into pdev 
	platform_set_drvdata(pdev,fbinfo);
/**************************************************************************
******************************initilaze  fbvar*******************************
************************************************************************/
	fbvar=fbinfo->par;
	fbvar->dev=&pdev->dev;
	//return struct resource r->start,IO resource
	//irq number
	fbvar->lcd_irq_no=platform_get_irq(pdev,0);
	if(fbvar->lcd_irq_no<0)
		{
			dev_err(&pdev->dev,"no irq for lcd\n");
			return -ENOENT;
		}
	
	res=platform_get_resource(pdev,IORESOURCE_MEM,0);
	if(res==NULL)
		{
			dev_err(&pdev->dev,"no resource for lcd\n");
			return -ENOENT;
		}
	//request lcd mem_region
	fbvar->lcd_men=request_mem_region(res->start,res->end-res->start+1,pdev->name);
	if(fbvar->lcd_men==NULL)
		{
			dev_err(&pdev->dev,"failed to get resource region \n");
			return -ENOENT;
		}
	
	fbvar->lcd_base=ioremap(res->start,res->end-res->start+1);
	if(fbvar->lcd_base==NULL)
		{
			dev_err(&pdev->dev,"ioremap of register erro\n");
			return -EINVAL;
			goto  err_nomem;
		}
	fbvar->lcd_clock=clk_get(NULL,"lcd");
	if(!fbvar->lcd_clock)
		{
			dev_err(&pdev->dev,"failed to get lcd_clock \n");
			return -ENOENT;
			goto  err_nomap;
		}
	clk_enable(fbvar->lcd_clock);
	//request for irq
	ret=request_irq(fbvar->lcd_irq_no,jakillfb_lcd_irq,IRQF_DISABLED,pdev->name,fbvar);
	if(ret)
		{
			dev_err(&pdev->dev,"IRQ %d request erro %d\n",fbvar->lcd_irq_no,ret);
			ret=-EBUSY;
			goto err_nolock;
		}
	
/*
*******************************************************************************
*******************************************************************************
				now fill the fbinfo with data
*******************************************************************************
*******************************************************************************
*/
	strcpy(fbinfo->fix.id,driver_name);
	fbinfo->fix.type=FB_TYPE_PACKED_PIXELS;
	fbinfo->fix.ypanstep=0;
	fbinfo->fix.xpanstep=0;
	fbinfo->fix.type_aux=0;
	fbinfo->fix.ywrapstep=0;
	fbinfo->fix.accel=FB_ACCEL_NONE;
	//initiliaze  the var
	fbinfo->var.nonstd=0;
	fbinfo->var.activate=FB_ACTIVATE_NOW;
	fbinfo->var.accel_flags=0;
	fbinfo->var.vmode=FB_VMODE_NONINTERLACED;
	fbinfo->var.xres=display->xres;
	fbinfo->var.yres=display->yres;
	fbinfo->var.bits_per_pixel=display->bpp;
	fbinfo->fbops=&jakillfb_ops;
	fbinfo->flags=FBINFO_FLAG_DEFAULT;
	fbinfo->pseudo_palette=&fbvar->pseudo_pal;

	//initiliaze the paletee_buffer;
	for(i=0;i<256;i++)
		{
			fbvar->paletee_buffer[i]=PALETTE_BUFF_CLEAR;
			
		}
	//calculate the display_men
	//caltulate the frame buffer 'largest  length
	for(i=0;i<mach_info->num_displays;i++)
		{
			unsigned long sem_len=(mach_info->displays[i].xres*mach_info->displays[i].yres*
					mach_info->displays[i].bpp);
			if(fbinfo->fix.smem_len<sem_len)
				
				{
					fbinfo->fix.smem_len=sem_len;
					
					
				}
		}
	
	msleep(1);
	jakillfb_init_registers(fbinfo);
	jakillfb_check_var(fbinfo);
	ret=jakillfb_map_video_memory(fbinfo);
	if(ret)
		{
			dev_err(&pdev->dev,"failed to allocate video memory %d\n",ret);
			return -ENOMEM;
			goto  err_nofb;
			
			
		}
	ret=register_framebuffer(fbinfo);
	if(!ret)
		{
			dev_err(&pdev->dev,"register frambuffer erro  %d\n",ret);
			goto err_video_nomem;
		
			
		}
		ret=device_create_file(&pdev->dev,&dev_attr_debug);
		if(ret)
		{
			dev_err(&pdev->dev,"creat device fil e failed \n %d",ret);
			
		}
		return 0;
err_nomem:
		release_resource(fbvar->lcd_men);
		kfree(fbvar->lcd_men);

err_nomap:
		iounmap(fbvar->lcd_base);
err_nolock:
		clk_disable(fbvar->lcd_clock);
		clk_put(fbvar->lcd_clock);
err_noirq:
		free_irq(fbvar->lcd_irq_no,fbvar);
err_nofb:
		platform_set_drvdata(pdev,NULL);
err_video_nomem:
		jakillfb_umap_video_memory(fbinfo);
		

	
	
}


static  int __devexit jakilllcd_fb_remove(struct platform_device *pdev)
{
	struct fb_info *fbinfo =platform_get_drvdata(pdev);
	struct jakillfb_var *fbvar=fbinfo->par;
	//first  releas  framebufer
	//second  disable  lcd
	//then umap  video
	//and set_plat_form_drvdate   NULL
	//then  free_irq
	//then   free_clock
	//then   free  _ioresource
	//then
	unregister_framebuffer(fbinfo);
	jakillfb_lcd_enable(fbvar,0);
	jakillfb_umap_video_memory(fbinfo);
	platform_set_drvdata(pdev,NULL);

//didn't know quitely about  lcd_irq_no
	free_irq(fbvar->lcd_irq_no,fbvar);
	if(fbvar->lcd_clock)
		{
			clk_disable(fbvar->lcd_clock);
			//re-put the lcd_clock into the struct list
			clk_put(fbvar->lcd_clock);
			fbvar->lcd_clock=NULL;
			
		}
	iounmap(fbvar->lcd_base);
	
	release_resource(fbvar->lcd_men);
	kfree(fbvar->lcd_men);
	return 0;
	
	
}

#ifdef  CONFIG_PM
//suspend
//disable the clock,enable lcd will be okay
	static int jakilllcd_fb_suspend(struct platform_device *pdev,pm_message_t state)
{
	//pdev->dev->driver_data
	struct fb_info *fbinfo=platform_get_drvdata(pdev);
	struct jakillfb_var *fbvar=fbinfo->par;
	//stop the lcd
	jakillfb_lcd_enable(fbvar,0);
	msleep(1);
	local_irq_restore(flags);
		
	
}
//resume
static int jakilllcd_fb_resume(struct platform_device *pdev)
{
	struct fb_info *fbinfo=platform_get_drvdata(pdev);
	struct jakillfb_var *fbvar=fbinfo->par;
	clk_enable(fbvar->lcd_clock);
	
	msleep(1);
	jakillfb_init_registers(fbinfo);
	jakillfb_activate_var(fbinfo);
	jakill2410fb_blank(FB_BLANK_UNBLANK,fbinfo)
}
#else
#define    jakilllcd_fb_suspend   NULL
#define   jakilllcd_fb_resume    NULL
#endif








//write paletee  into s3c2410 phy register,not in fbinfo

//init registers,use to init the tpal,lpcsel
// and set the GPCON,GPDCOND,physically
//

/*
********************************************************************************************
					first part,register
********************************************************************************************
*/
static  struct  platform_driver  jakillfb_lcd_driver={
	.probe=jakilllcd_fb_probe,
	.remove=jakilllcd_fb_remove,
	.suspend=jakilllcd_fb_suspend,
	.resume=jakilllcd_fb_resume,
	.driver={
		.name="jakill2410_lcd",
		.owner=THIS_MODULE,
	},
};
static int __init jakilllcd_init(void)
{
	return   platform_driver_register(&jakillfb_lcd_driver);
}
static void __exit jakilllcd_exit(void)
{
	return platform_driver_unregister(&jakillfb_lcd_driver);
}
module_init(jakilllcd_init);
module_exit(jakilllcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jakill");
MODULE_DESCRIPTION("jakill-s3c2410 lcd driver");
下面贴一个可用的lcd简易驱动程序:

#include <linux/module.h>
#include <linux/kernel.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 <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>

#include <linux/string.h>
#include <linux/slab.h>
#include <asm/types.h>
#include <asm/io.h>

struct lcd_regs {   //为了避免麻烦,我们用一个结构体将所有的寄存器封装在一起,然后一起映射
	unsigned long	lcdcon1;
	unsigned long	lcdcon2;
	unsigned long	lcdcon3;
	unsigned long	lcdcon4;
	unsigned long	lcdcon5;
    unsigned long	lcdsaddr1;
    unsigned long	lcdsaddr2;
    unsigned long	lcdsaddr3;
    unsigned long	redlut;
    unsigned long	greenlut;
    unsigned long	bluelut;
    unsigned long	reserved[9];  //这段内存没有使用,所以需要空出来,但不能忽略
    unsigned long	dithmode;
    unsigned long	tpal;
    unsigned long	lcdintpnd;
    unsigned long	lcdsrcpnd;
    unsigned long	lcdintmsk;
    unsigned long	lpcsel;
};

static struct fb_info *s3clcdfb_info;
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;
static volatile struct lcd_regs* lcd_regs;
static u32 pseudo_palette[16];

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 s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
			     unsigned int green, unsigned int blue,
			     unsigned int transp, struct fb_info *info)
{
	unsigned int val;
	
	if (regno > 16)
		return 1;

	/* 用red,green,blue三原色构造出val */
	val  = chan_to_field(red,	&info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue,	&info->var.blue);
	
	//((u32 *)(info->pseudo_palette))[regno] = val;
	pseudo_palette[regno] = val;
	return 0;
}
static struct fb_ops s3c_lcdfb_ops = {  //操作函数
	.owner		= THIS_MODULE,
	.fb_setcolreg	= s3c_lcdfb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};

static int __init lcd_init(void)
 	{
 	 s3clcdfb_info=	framebuffer_alloc(0,NULL);               // 1. 分配一个fb_info,分配的内存大小在该函数内部会自动指定
 	 
 	 /* 2. 设置 */
	 /* 2.1 设置固定的参数 */
	 strcpy(s3clcdfb_info->fix.id,"lcd");                    //字符串形式的标识符
	// s3clcdfb_info->fix.smem_start=                        //fb缓冲内存的开始地址(物理地址)
	s3clcdfb_info->fix.smem_len     = 480*272*16/8;          //fb缓冲的长度max_xres *max_yres*max_bpp/8 , TQ2440的LCD位宽是24,但是2440里会分配4字节即32位(浪费1字节)           
	s3clcdfb_info->fix.type         =FB_TYPE_PACKED_PIXELS;  //查看宏 FB_TYPE_    FB_TYPE_PACKED_PIXELS=0   
	s3clcdfb_info->fix.type_aux     =0;
	s3clcdfb_info->fix.visual       =FB_VISUAL_TRUECOLOR;    //查看宏FB_VISUAL_,用于记录屏幕使用的色彩模式,一般是FB_VISUAL_TRUECOLOR(真彩色)
	s3clcdfb_info->fix.xpanstep     =0;                      //如果没有硬件 panning,=0
	s3clcdfb_info->fix.ypanstep     =0;                      //如果没有硬件 panning,=0
	s3clcdfb_info->fix.ywrapstep    =0;                      //如果没有硬件 panning,=0
	s3clcdfb_info->fix.line_length  =480*16/8;               //1行的字节数,TQ2440的LCD位宽是24,但是2440里会分配4字节即32位(浪费1字节)
	s3clcdfb_info->fix.accel        = FB_ACCEL_NONE;
	
	/* 2.2 设置可变的参数 */
	s3clcdfb_info->var.xres            =480;   //定义屏幕一行有多少个点
	s3clcdfb_info->var.yres            =272;   //定义屏幕一列由多少个点
	s3clcdfb_info->var.xres_virtual    =480;   //虚拟屏幕一行有多少个点
	s3clcdfb_info->var.yres_virtual    =272;   //虚拟屏幕一列由多少个点
	s3clcdfb_info->var.xoffset         =0;     //虚拟到可见(实际)之间的行方向偏移
	s3clcdfb_info->var.yoffset         =0;     //虚拟到可见(实际)之间的列方向偏移
	s3clcdfb_info->var.bits_per_pixel  =16;
	s3clcdfb_info->var.grayscale       =0;
	/* RGB:565 */
	s3clcdfb_info->var.red.offset     = 11;   //位域偏移
	s3clcdfb_info->var.red.length     = 5;    //位域长度
	s3clcdfb_info->var.green.offset   = 5;    //位域偏移
	s3clcdfb_info->var.green.length   = 6;    //位域长度
	s3clcdfb_info->var.blue.offset    = 0;    //位域偏移 
	s3clcdfb_info->var.blue.length    = 5;    //位域长度
	
	/* 2.3 设置操作函数 */
	s3clcdfb_info->fbops           =&s3c_lcdfb_ops; //设置操作函数
	s3clcdfb_info->flags           =FBINFO_FLAG_DEFAULT;
	s3clcdfb_info->pseudo_palette  =pseudo_palette;   //伪16色调色板
	s3clcdfb_info->screen_base     = dma_alloc_writecombine(NULL, s3clcdfb_info->fix.smem_len, &(dma_addr_t *)s3clcdfb_info->fix.smem_start, GFP_KERNEL); //虚拟基地址
	s3clcdfb_info->screen_size     = 480*272*16/8;   //ioremap 的虚拟内存大小
	
	/* 2.4 其他的设置 */
	s3clcdfb_info->var.nonstd        =0;                         //标准像素格式 
	s3clcdfb_info->var.activate      =FB_ACTIVATE_NOW;
	s3clcdfb_info->var.accel_flags   =0;
	s3clcdfb_info->var.pixclock      =100000;  //像素时钟(皮秒),pixclock=1/Dclk=
	s3clcdfb_info->var.left_margin   =2;                         //行切换,从同步到绘图之间的延迟, Hsyn front-porch,查看LCD数据手册
	s3clcdfb_info->var.right_margin  =2;                         //行切换,从绘图到同步之间的延迟  Hsyn back-porch
	s3clcdfb_info->var.upper_margin  =2;                         //帧切换,从同步到绘图之间的延迟  Vsyn front-porch
	s3clcdfb_info->var.lower_margin  =2;                         //帧切换,从绘图到同步之间的延迟  Vsyn back-porch
	s3clcdfb_info->var.hsync_len     =41;                        //水平同步的长度    Hsyn pulse Width
	s3clcdfb_info->var.vsync_len     =10;                        //垂直同步的长度    Vsyn pulse Width
	s3clcdfb_info->var.vmode         = FB_VMODE_NONINTERLACED;
	
	/* 3. 硬件相关的操作 */
	/* 3.1 配置GPIO用于LCD */
	gpbcon = ioremap(0x56000010, 8);
	gpbdat = gpbcon+1;
	gpccon = ioremap(0x56000020, 4);
	gpdcon = ioremap(0x56000030, 4);
	gpgcon = ioremap(0x56000060, 4);	
	*gpccon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
	*gpdcon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[23:8] */
	*gpgcon |= (3<<8); /* GPG4用作LCD_PWREN */
	
	/* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */
	lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
	/* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2], LCD手册P5 (Dclk=9MHz~15MHz)
	 *            10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
	 *            CLKVAL = 4
	 * bit[6:5]: 0b11, TFT LCD
	 * bit[4:1]: 0b1101, 24 bpp for TFT
	 * bit[0]  : 0 = 禁止视频输出LCD控制信号
	 */
	lcd_regs->lcdcon1  = (4<<8) | (3<<5) | (0x0c<<1);
	
		/* 垂直方向的时间参数
	 * bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据
	 *             LCD手册 tvb=2
	 *             VBPD=2-1=1
	 * bit[23:14]: 多少行, 272, 所以LINEVAL=272-1=271
	 * bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC
	 *             LCD手册tvf=2, 所以VFPD=2-1=1
	 * bit[5:0]  : VSPW, VSYNC信号的脉冲宽度, LCD手册tvp=10, 所以VSPW=10-1=9
	 */
	lcd_regs->lcdcon2  = (1<<24) | (271<<14) | (1<<6) | (9<<0);
	
	/* 水平方向的时间参数
	 * bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
	 *             LCD手册 thb=2
	 *             HBPD=1
	 * bit[18:8]: 多少列, 480, 所以HOZVAL=480-1=479
	 * bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
	 *             LCD手册thf=2, 所以HFPD=2-1=1
	 */
	lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1<<0);
	
	/* 水平方向的同步信号
	 * bit[7:0]	: HSPW, HSYNC信号的脉冲宽度, LCD手册Thp=41, 所以HSPW=41-1=40
	 */	
	lcd_regs->lcdcon4 = 40;
	
	/* 信号的极性 
	 * bit[11]: 1=565 format, 对于24bpp这个不用设
	 * bit[10]: 0 = VCLK下降沿取视频数据
	 * bit[9] : 1 = HSYNC信号要反转,即低电平有效 
	 * bit[8] : 1 = VSYNC信号要反转,即低电平有效 
	 * bit[6] : 0 = VDEN不用反转
	 * bit[3] : 0 = PWREN输出0
	 *
	 * BSWP = 0, HWSWP = 0, BPP24BL = 0 : 当bpp=24时,2440会给每一个象素分配32位即4字节,哪一个字节是不使用的? 看2440手册P412
         * bit[12]: 0, LSB valid, 即最高字节不使用
	 * bit[1] : 0 = BSWP
	 * bit[0] : 0 = HWSWP
	 */
	lcd_regs->lcdcon5 = (0<<10) | (1<<9) | (1<<8) | (1<<11) | (0<<1) | (1<<0);
	
	lcd_regs->lcdsaddr1  = (s3clcdfb_info->fix.smem_start >> 1) & ~(3<<30);
	lcd_regs->lcdsaddr2  = ((s3clcdfb_info->fix.smem_start +s3clcdfb_info->fix.smem_len ) >> 1) & 0x1fffff;
	lcd_regs->lcdsaddr3  = (480*16/16);  // 一行的长度(单位: 2字节) 
	
	lcd_regs->lcdcon1 |= (1<<0);         //使能LCD控制器 
	
	lcd_regs->lcdcon5 |= (1<<3);        //使能LCD本身: LCD_PWREN 
	
	
	register_framebuffer(s3clcdfb_info);	// 4. 注册 
  return 0;	
 	}
static void __exit lcd_exit(void)
	{
	unregister_framebuffer(s3clcdfb_info);  //释放fb_info
	lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD控制器 */
	lcd_regs->lcdcon1 &= ~(1<<3); /* 关闭LCD本身 */
	dma_free_writecombine(NULL, s3clcdfb_info->fix.smem_len, s3clcdfb_info->screen_base, s3clcdfb_info->fix.smem_start);  //释放缓冲区
	iounmap(lcd_regs);  //取消映射
	iounmap(gpbcon);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);
	framebuffer_release(s3clcdfb_info);  //注销
	}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_AUTHOR("jakill"); //描述模块作者
MODULE_LICENSE("Dual BSD/GPL");//指定代码使用双重许可证
MODULE_VERSION("v1.0");        //模块版本
MODULE_DESCRIPTION("A lcd operation module"); //说明模块用途
MODULE_ALIAS("lcd");    //模块别名




直接在数据结构里面把固定参数定义好。省得那么多麻烦。

加载的时候先要把 /linux-2.6.30.6/drives/video/目录下 ,在编译内核的时候编译好的3个东东先加载

1.cfbcopyarea.ko

2cfbfillrect.ko

3cfbimgblt.ko
依次使用insmod加载。最后加载  lcd驱动程序

测试程序在上一章,可以显示图像。


你可能感兴趣的:(lcd驱动详细程序)