这个程序来自于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驱动程序
测试程序在上一章,可以显示图像。