linux2628下6410LCD驱动分析.docx
《linux2628下6410LCD驱动分析.docx》由会员分享,可在线阅读,更多相关《linux2628下6410LCD驱动分析.docx(27页珍藏版)》请在冰豆网上搜索。
![linux2628下6410LCD驱动分析.docx](https://file1.bdocx.com/fileroot1/2023-1/23/c05924e5-18ae-4758-975b-4a040605fa0e/c05924e5-18ae-4758-975b-4a040605fa0e1.gif)
linux2628下6410LCD驱动分析
linux2.6.28下6410LCD驱动分析
本文不详细分析lcd的每行代码 ,只是做简单的构架分析, 要详细的分析可以参考
LCD驱动是用了device和driver架构,首先看arch/arm/mach-s3c6410/mach-smdk6410.c文件,在353行有这样一个数组
staticstructplatform_device*smdk6410_devices[]__initdata={
……………………
&s3c_device_lcd,
……………………
}
我们在进里面了解详情,在arch/arm/palt-s3c64xx/devs.c文件里有s3c_device_lcd变量的定义
staticstructresources3c_lcd_resource[]={
[0]={
.start=S3C64XX_PA_LCD, //lcd寄存器的起始地址
.end =S3C64XX_PA_LCD+SZ_1M-1, //lcd寄存器的结束地址,
.flags=IORESOURCE_MEM,
},
[1]={
.start=IRQ_LCD_VSYNC, //lcd中断寄存器起始地址,在s3cfb_probe用
.end =IRQ_LCD_SYSTEM,
.flags=IORESOURCE_IRQ,
}
};
staticu64s3c_device_lcd_dmamask=0xffffffffUL;
structplatform_devices3c_device_lcd={
.name ="s3c-lcd", //这个会和driver中的name一样,driver
//根据这个进行匹配
.id =-1,
.num_resources =ARRAY_SIZE(s3c_lcd_resource),
.resource =s3c_lcd_resource,
.dev ={
.dma_mask =&s3c_device_lcd_dmamask,
.coherent_dma_mask =0xffffffffUL
}
};
在arch/arm/mach-s3c6410/mach-smdk6410.c文件的
staticvoid__initsmdk6410_machine_init(void)函数中有这么一句话
platform_add_devices(smdk6410_devices,ARRAY_SIZE(smdk6410_devices));
这个就是对device设备进行注册,在注册driver驱动时,会根据name找到相应的device进行匹配,然后会调用probe。
下面分析driver,在drivers/video/samsung/s3cfb.c文件的最下面有这样的一段代码
staticstructplatform_drivers3cfb_driver={
.probe =s3cfb_probe, //匹配的话会调用这个函数
.remove =s3cfb_remove,
.suspend =s3cfb_suspend,
.resume =s3cfb_resume,
.driver ={
.name ="s3c-lcd", //上面说的,内核会根据这个来找到相应的device
.owner =THIS_MODULE,
},
};
int__devinits3cfb_init(void)
{
returnplatform_driver_register(&s3cfb_driver);//进行drive注册
}
匹配后会掉用s3cfb_probe();
这个函数里面对fbinfo进行初始化,对相关寄存器进行初始化,在6410lcd驱动中,主要要修改的几个文件是:
drivers/video/samsung目录下的s3cfb.c,假设你在配置的时候即用makemenuconfig命令,在里面选择的是480WV,他对应的文件时s3cfb_lte480wv.c,s3cfb_lte480wv.c是和屏参数相关的文件。
还有个要改的文件是s3cfb_fimd4x.c,这个文件主要是涉及寄存器的一些参数的配置。
主要几个重点:
对构架的分析,理清楚各个文件的关系。
s3cfb.c中的s3cfb_probe主要是在根据s3cfb_fimd4x.c和s3cfb_lte480wv.c文件来初始fbinfo,并且写相关LCD寄存器。
s3cfb_lte480wv.c主要是记录屏的参数,在这里进行修改来满足不同的lcd屏。
s3cfb_fimd4x.c是对寄存器的值进行初始化,比如各个窗口的显示模式、颜色表寄存器的模式、各个窗口的控制寄存器,此文件还包含6410对于lcd的特殊功能,用ioctl来进行实现的。
fbmem.c这个是fb字符设备驱动,每个窗口都是一个字符设备,在s3cfb_probe中通过调用register_buffer进行注册,注意,里面有一个fb_notifier_call_chain,他最终会调用drivers/video/console/fbcon.c中的fbcon_init,里面会有对logo的调用;fbmem.c文件里还有通用的ioctl功能调用,适合所有带lcd控制器的处理器,这个文件时通用的,每个处理器的fbmem都是一样的,做成了和硬件无关。
lcd驱动简单构架图
abc
s3c2410_lcd&framebuffer驱动分析
int__inits3c2410fb_probe(structdevice*dev)
{
structs3c2410fb_info*info;
structfb_info *fbinfo;
structplatform_device*pdev=to_platform_device(dev);
structs3c2410fb_hw*mregs;
intret;
intirq;
inti;
mach_info=dev->platform_data;//获取lcd相关寄存器配置信息
if(mach_info==NULL){
dev_err(dev,"noplatformdataforlcd,cannotattach\n");
return-EINVAL;
}
mregs=&mach_info->regs;
irq=platform_get_irq(pdev,0);
if(irq<0){
dev_err(dev,"noirqfordevice\n");
return-ENOENT;
}
fbinfo=framebuffer_alloc(sizeof(structs3c2410fb_info),dev);//分配structfb_info和structs3c2410fb_info结构
if(!
fbinfo){
return-ENOMEM;
}
info=fbinfo->par;//调用framebuffer_alloc分配内存的时候,其实顺带分配了structs3c2410fb_info结构,紧跟structfb_info结构的后面,fbinfo->par指针处。
info->fb=fbinfo;
dev_set_drvdata(dev,fbinfo);
s3c2410fb_init_registers(info);//lcd相关寄存器配置
dprintk("devinit\n");
strcpy(fbinfo->fix.id,driver_name);
/*配置信息存储起来,以后用到*/
memcpy(&info->regs,&mach_info->regs,sizeof(info->regs));
info->mach_info =dev->platform_data;
/*以下配置,详细请参考网上资料*/
fbinfo->fix.type =FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux =0;
fbinfo->fix.xpanstep =0;
fbinfo->fix.ypanstep =0;
fbinfo->fix.ywrapstep =0;
fbinfo->fix.accel =FB_ACCEL_NONE;
fbinfo->var.nonstd =0;
fbinfo->var.activate =FB_ACTIVATE_NOW;
/*设置分辨率*/
fbinfo->var.height =mach_info->height;
fbinfo->var.width =mach_info->width;
fbinfo->var.accel_flags =0;
fbinfo->var.vmode =FB_VMODE_NONINTERLACED;
fbinfo->fbops =&s3c2410fb_ops;//重要的fop结构(fb设备本质上是字符设备)
fbinfo->flags =FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette =&info->pseudo_pal;
fbinfo->var.xres =mach_info->xres.defval;
fbinfo->var.xres_virtual =mach_info->xres.defval;
fbinfo->var.yres =mach_info->yres.defval;
fbinfo->var.yres_virtual =mach_info->yres.defval;
fbinfo->var.bits_per_pixel =mach_info->bpp.defval;
fbinfo->var.upper_margin =S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2)+1;
fbinfo->var.lower_margin =S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2)+1;
fbinfo->var.vsync_len =S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2)+1;
fbinfo->var.left_margin =S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3)+1;
fbinfo->var.right_margin =S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3)+1;
fbinfo->var.hsync_len =S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4)+1;
/*配色板设置(采用5、6、5模式)*/
fbinfo->var.red.offset =11;
fbinfo->var.green.offset =5;
fbinfo->var.blue.offset =0;
fbinfo->var.transp.offset =0;
fbinfo->var.red.length =5;
fbinfo->var.green.length =6;
fbinfo->var.blue.length =5;
fbinfo->var.transp.length =0;
/*计算需要申请的framerbuffer大小(以字节为单位)*/
fbinfo->fix.smem_len = mach_info->xres.max*
mach_info->yres.max*
mach_info->bpp.max/8;
for(i=0;i<256;i++)
info->palette_buffer[i]=PALETTE_BUFF_CLEAR;
if(!
request_mem_region((unsignedlong)S3C24XX_VA_LCD,SZ_1M,"s3c2410-lcd")){
ret=-EBUSY;
gotodealloc_fb;
}
dprintk("gotLCDregion\n");
ret=request_irq(irq,s3c2410fb_irq,SA_INTERRUPT,pdev->name,info);
if(ret){
dev_err(dev,"cannotgetirq%d-err%d\n",irq,ret);
ret=-EBUSY;
gotorelease_mem;
}
info->clk=clk_get(NULL,"lcd");
if(!
info->clk||IS_ERR(info->clk)){
printk(KERN_ERR"failedtogetlcdclocksource\n");
ret=-ENOENT;
gotorelease_irq;
}
clk_use(info->clk);
clk_enable(info->clk);
dprintk("gotandenabledclock\n");
msleep
(1);
/*Initializevideomemory*/
ret=s3c2410fb_map_video_memory(info);
if(ret){
printk(KERN_ERR"FailedtoallocatevideoRAM:
%d\n",ret);
ret=-ENOMEM;
gotorelease_clock;
}
dprintk("gotvideomemory\n");
ret=s3c2410fb_init_registers(info);//怎么再次初始化lcd相关寄存器了?
ret=s3c2410fb_check_var(&fbinfo->var,fbinfo);//注册framebuffer前的一个例行检查
ret=register_framebuffer(fbinfo);
if(ret<0){
printk(KERN_ERR"Failedtoregisterframebufferdevice:
%d\n",ret);
gotofree_video_memory;
}
/*createdevicefiles*/
device_create_file(dev,&dev_attr_debug);
printk(KERN_INFO"fb%d:
%sframebufferdevice\n",
fbinfo->node,fbinfo->fix.id);
return0;
free_video_memory:
s3c2410fb_unmap_video_memory(info);
release_clock:
clk_disable(info->clk);
clk_unuse(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq,info);
release_mem:
release_mem_region((unsignedlong)S3C24XX_VA_LCD,S3C24XX_SZ_LCD);
dealloc_fb:
framebuffer_release(fbinfo);
returnret;
}
/*
*s3c2410fb_map_video_memory():
* AllocatestheDRAMmemoryfortheframebuffer. Thisbufferis
* remappedintoanon-cached,non-buffered,memoryregionto
* allowpaletteandpixelwritestooccurwithoutflushingthe
* cache. Oncethisareaisremapped,allvirtualmemory
* accesstothevideomemoryshouldoccuratthenewregion.
*/
staticint__inits3c2410fb_map_video_memory(structs3c2410fb_info*fbi)
{
dprintk("map_video_memory(fbi=%p)\n",fbi);
fbi->map_size=PAGE_ALIGN(fbi->fb->fix.smem_len+PAGE_SIZE);//页对齐
fbi->map_cpu =dma_alloc_writecombine(fbi->dev,fbi->map_size,
&fbi->map_dma,GFP_KERNEL);//目前我只知道用于分配可供dma使用的内存,其中返回地址fbi->map_cpu为虚拟地址,fbi->map_dma为物理地址。
fbi->map_size=fbi->fb->fix.smem_len;//恢复真实的大小
if(fbi->map_cpu){
/*preventinitialgarbageonscreen*/
dprintk("map_video_memory:
clear%p:
%08x\n",
fbi->map_cpu,fbi->map_size);
memset(fbi->map_cpu,0xf0,fbi->map_size);
fbi->screen_dma =fbi->map_dma;//物理地址
fbi->fb->screen_base =fbi->map_cpu;//虚拟地址
fbi->fb->fix.smem_start =fbi->screen_dma;//物理地址
dprintk("map_video_memory:
dma=%08xcpu=%psize=%08x\n",
fbi->map_dma,fbi->map_cpu,fbi->fb->fix.smem_len);
}
returnfbi->map_cpu?
0:
-ENOMEM;
}
staticstructs3c2410fb_mach_infosbc2410_lcdcfg__initdata={
.fixed_syncs=0,
.regs={
.lcdcon1=S3C2410_LCDCON1_TFT16BPP|\
S3C2410_LCDCON1_TFT|\
S3C2410_LCDCON1_CLKVAL(6),
.lcdcon2=S3C2410_LCDCON2_VBPD
(2)|\
S3C2410_LCDCON2_LINEVAL(319)|\
S3C2410_LCDCON2_VFPD(0)|\
S3C2410_LCDCON2_