S3C2440,Linux,LCD驱动
扫描二维码
随时随地手机看文章
到了神秘的LCD驱动了,信息还真有点胆怯,但是还是不得不走下去。对刚刚学习的linux驱动坐一下总结,毕竟是Linux内核当中的东东,而且是那么的繁琐。做一总结,等用笔记把学过东西几下来,这样就不会忘了。哈哈!
那就开始!!!
在编写裸机LCD程序的时候,首先就是硬件初始化操作。有一个寄存器当中存放了帧缓冲的起始地址。这个参数是非常重要的。当配置好硬件后,帧缓冲中的数据能够脱离CPU不停地将真缓冲当中的数据写入到LCD屏。如果我们要现实一个图片的话只需要将图片数据放到帧缓冲当中,这样就非常的方便了。
在linux当中,把整个LCD驱动分为两层:LCD帧缓冲区层和LCD硬件驱动层。LCD帧缓冲区层其实就是将内核中的一部分空间当作一个字符型设备,通过操作字符型设备的接口函数就可以操作这段帧缓冲区。而LCD硬件驱动层是对LCD硬件的初始化,LCD控制器在硬件驱动层被看作一个平台设备。
LCD帧缓冲区层对应的文件是fbmem.c,LCD硬件驱动层对应的文件是S32440fb.c。
从最底层开始说吧,那当然是LCD硬件驱动层了。
LCD硬件驱动层(S32440fb.c):
前面提到了LCD控制器在LCD硬件驱动层被当看作是一个平台设备,没有定义字符设备所以不可能通过应用层来访问硬件。
既然是平台设备,那么我们就按照平台设备的执行流程来分析。
首先来看一下,在S32440fb.c文件中定义的平台设备驱动变量。如下:
staticstructplatform_drivers3c2410fb_driver={
.probe =s3c2410fb_probe,
.remove =s3c2410fb_remove,
.suspend =s3c2410fb_suspend,
.resume =s3c2410fb_resume,
.driver ={
.name ="s3c2410-lcd",
.owner =THIS_MODULE,
},
};
后面两个是功耗操作函数,而第一函数才是最重要的呀!因为平台设备驱动在向平台总线注册时如果与挂接在该总线上的设备之一与该平台设备驱动的匹配(文件名或者ID,通常时文件名),那么就会指向探测函数s3c2410fb_probe(为什么会执行,在平台设备总结文章中),探测函数只有一个语句即调用s3c24xxfb_probe函数,这两个函数的参数是一样的
该参数是对应的与之匹配的平台设备结构体,此设备在devs.c中定义,定义代码如下
staticu64s3c_device_lcd_dmamask=0xffffffffUL;
structplatform_devices3c_device_lcd={
.name ="s3c2410-lcd",
.id =-1,
.num_resources =ARRAY_SIZE(s3c_lcd_resource),
.resource =s3c_lcd_resource,
.dev={
.dma_mask =&s3c_device_lcd_dmamask,
.coherent_dma_mask =0xffffffffUL
}
};
s3c_lcd_resource是LCD驱动所使用到的资源,源的定义如下:
staticstructresources3c_lcd_resource[]={
[0]={
.start=S3C24XX_PA_LCD,
.end=S3C24XX_PA_LCD+S3C24XX_SZ_LCD-1,
.flags=IORESOURCE_MEM,
},
[1]={
.start=IRQ_LCD,
.end=IRQ_LCD,
.flags=IORESOURCE_IRQ,
}
};
第一个元素为配置LCD寄存器时所用的寄存器的空间,第二个参数是在LCD硬件驱动中用到了LCD中断,虽然定义了当时没有真正的用到。
进入s3c24xxfb_prob函数,里面有一句代码mach_info=pdev->dev.platform_data;platform_data虽然在定义的时候没有赋值,但是在进入smdk2440_machine_init函数时,第一个语句调用了s3c24xx_fb_set_platdata函数,此函数的作用就是将smdk2440_fb_info变量赋值给s3c_device_lcd.dev.platform_data(具体请看源码),smdk2440_fb_info的定义如下:
staticstructs3c2410fb_mach_infosmdk2440_fb_info__initdata={
.displays =&smdk2440_lcd_cfg,
.num_displays =1,
.default_display=0,
.gpccon =0xaaaaaaaa,
.gpccon_mask =0xffffffff,
.gpcup =0x0000ffff,
.gpcup_mask =0xffffffff,
.gpdcon =0xaaaaaaaa,
.gpdcon_mask =0xffffffff,
.gpdup =0x0000ffff,
.gpdup_mask =0xffffffff,
.lpcsel =0x00,
};
接下来应该注意一下 fbinfo->fbops =&s3c2410fb_ops;此语句代码。Fbinfo的fbops指向了一个操作在LCD硬件驱动层的一些硬件操作函数,然后再看ret=register_framebuffer(fbinfo); 这中间很多的语句就是对fbinfo中的成赋值,在此不具体分析。register_framebuffer函数不是在该文件中定义的,而是在LCD帧缓冲区层的fbmem.c当中定义,是由有LCD硬件驱动层使用的。它是LCD硬件驱动层和LCD帧缓冲区层的信息交流的通道,这个函数将fbops指针变量传给了LCD帧缓冲区,那么LCD帧缓冲区就可以通过此指针来访问LCD硬件驱动层中的操作函数了。稍后我们在LCD帧缓冲区层的实现代码fbmem.c中详细讲解。
LCD帧缓冲区层(fbmem.c):
LCD帧缓冲区实际上就是把内存当中的一段帧缓冲区抽象成了以字符设备。既然是字符设备,那么在模块初始化函数当中就执行注册字符设备函数,代码如下:
staticint__init
fbmem_init(void)
{
proc_create("fb",0,NULL,&fb_proc_fops);
if(register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unabletogetmajor%dforfbdevsn",FB_MAJOR);
fb_class=class_create(THIS_MODULE,"graphics");
if(IS_ERR(fb_class)){
printk(KERN_WARNING"Unabletocreatefbclass;errno=%ldn",PTR_ERR(fb_class));
fb_class=NULL;
}
return0;
}
设备的名字为"fb",主设备号是固定的,在/linux/Major被宏定义为29,系统启动后就已经固定了。后面的代码是用于创建结点的,在此不对其进行分析。
注册设备就好了,当然在字符设备当中还有一个文件结构体定义如下:
staticconststructfile_operationsfb_fops={
.owner= THIS_MODULE,
.read= fb_read,
.write= fb_write,
.unlocked_ioctl=fb_ioctl,
#ifdefCONFIG_COMPAT
.compat_ioctl=fb_compat_ioctl,
#endif
.mmap= fb_mmap,
.open= fb_open,
.release= fb_release,
#ifdefHAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area=get_fb_unmapped_area,
#endif
#ifdefCONFIG_FB_DEFERRED_IO
.fsync= fb_deferred_io_fsync,
#endif
};
在应用程序中若相对帧缓冲操作,那么必须先打开对应的结点,则对应要执行的函数当然是fb_open了。那就从fb_open开始分析吧!
进入fb_open函数,我们就就几个核心代码吧!
info=registered_fb[fbidx];这个语句是获得代表帧缓冲区的信息(信息存放在结构体当中)只要有帧缓冲区注册那么就会依次放到registered_fb这个数组(具体怎么被赋值到数组,稍后再注册函数中分析),这样就获得了一个帧缓冲区的信息了。
然后再看这段代码
file->private_data=info;
if(info->fbops->fb_open){
res=info->fbops->fb_open(info,1);
if(res)
module_put(info->fbops->owner);
}
看到了其中的res=info->fbops->fb_open(info,1);语句了吗?就是通过fbinfo指针的文件操作函数来访问LCD硬件驱动层的打开函数的吧!LCD帧缓冲驱动层的程序通过fbinfo中fops指针的变量来访问LCD硬件驱动层的东东。到此打开函数就执行完了。分析此函数的目的不仅是操作帧缓冲区必须先打开文件,还是为了显示出LCD帧缓冲区层怎样来访问LCD硬件驱动层的。
好了,接下来分析LCD帧缓冲区层和LCD硬件驱动层沟通的桥梁函数
现在进入register_framebuffer函数吧!
registered_fb[i]=fb_info;
看到这句代码就知道了上面提到registered_fb为什么会用到了吧!在registered_fb数组中存放着每一个注册代表帧缓冲区信息的结构体指针(structfb_info*)。也许你会有疑问那i的值是多少呐?那怎么就网上看,就看到了如下代码:
for(i=0;i if(!registered_fb[i]) break; 对registered_fb指针数组遍历,发现为空后就退出,然后注册的帧缓冲区指针就存放到这里,那么就可以实现当注册多个时依次在registered_fb数组中存放了。 如果你要移植到一个开发板上,要改的东西也很少,linux的LCD驱动设计的时候就是可以方便移植的。若硬件差别不大,你只需要更改几个结构体初始化数据,很方便的。 对于LCD的驱动就先讲到这里吧!以为后来用。