这个程序来自于linux内核,加以自己改编,还是内核的程序好写一点
比较长,都贴出来 。以后自己留看
主要是framebuffer各个结构的初始化,以及注册,probe函数最关键
可以单独编译,使用通用的module makefile编译成.ko格式。在操作系统没有lcd驱动的情况下加载,但是有驱动的情况下,就加载不上去了。
程序列表编译成功。加载上去使用不成功。成功的例程 可以去 /liunx/drivers/video/s3c2410fb.c 下查看
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "s3c2410fb.h"
#include
#include
#include
#include
#include
#include
#include
#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;inum_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<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;inum_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_lenfix.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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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驱动程序
测试程序在上一章,可以显示图像。