平台:
Linux :2.6.31
U-Boot:u-boot-2010.06
BusyBox: BusyBox 1.15.2
交叉编译器:arm-linux-v4.3.3
由上到下剖析LCD驱动相关的知识。
1、帧缓冲知识:
帧缓冲设备(frame buffer device)是图像硬件底层之上的一个抽象接口层。它屏蔽底层实现的差异,向上层应用程序提供定义好的、操作硬件的接口(well-defined interface),使得应用程序在图形模式下可以直接对显示缓冲区进行读写。用户在显示缓冲区写入图像数据,图像就会自动在LCD对应区域被显示出来。
帧缓冲设备是标准的字符设备,主设备号是29(major 29),次设备号表示不同的帧缓冲号码。
帧缓冲设备通过设备节点进行访问,该节点在/dev目录下,格式是/dev/fbn,n是次设备号,默认取值0~31:
0 = /dev/fb0First frame buffer
1 = /dev/fb1Second frame buffer
...
31 = /dev/fb31 32nd frame buffer
此外,帧缓冲设备还可以被当做普通的内存设备(memory devices)来访问,可读可写(You can read it, write it, seek to some location in it and mmap() it (the main usage)),比如:
cp /dev/fb0 myfile
2、帧缓冲数据结构和函数:
帧缓冲设备的数据结构在内核include/linux/fb.h文件中,关键的有fb_info、fb_ops、fb_var_screeninfo、fb_fix_screeninfo等。相互关系如下图:
fb_info结构体包含了帧缓冲设备的所有属性和操作,具体内容参考源码;
fb_ops结构体包含指向底层操作的函数指针,这些函数需要我们编写;
fb_var_screeninfo结构体包含用户来修改的LCD参数;
fb_fix_screeninfo结构体包含固定的、用户不能修改的参数,当对帧缓冲设备进行映射操作的时候,就从该结构体中取得缓冲区的物理地址。
3、帧缓冲设备的操作实现:
帧缓冲设备的实现在/linux/drivers/vedio/fbmem.c文件中,其文件操作结构体如下:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
};
结构体中的函数在fbmem.c已经实现,没有特殊情况则不需修改。
4、帧缓冲驱动框架:
s3c2410fb.c实现如下函数并完成封装:
static struct fb_ops s3c2410fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c2410fb_check_var,
.fb_set_par = s3c2410fb_set_par,
.fb_blank = s3c2410fb_blank,
.fb_setcolreg = s3c2410fb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
s3c2410fb_ops结构体由函数s3c24xxfb_probe调用:
fbinfo->fbops = &s3c2410fb_ops;
s3c24xxfb_probe有函数s3c2410fb_probe调用:
static int __init s3c2410fb_probe(struct platform_device *pdev)
{
return s3c24xxfb_probe(pdev, DRV_S3C2410);
}
然后s3c2410fb_probe函数被加到平台驱动里面:
static struct platform_driver s3c2412fb_driver = {
.probe = s3c2412fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = {
.name = "s3c2412-lcd",
.owner = THIS_MODULE,
},
};
最终在函数s3c2410fb_init里面完成驱动的注册:
int __init s3c2410fb_init(void)
{
int ret = platform_driver_register(&s3c2410fb_driver);
if (ret == 0)
ret = platform_driver_register(&s3c2412fb_driver);;
return ret;
}
注:
如果动态加载的话,执行insmod xxx.ko命令调用module_init并最终由module_init宏进行加载():
module_init(s3c2410fb_init);
参考资料:
[1] linux-2.6.31\Documentation\fb\framebuffer.txt
[2] 宋宝华.Linux 设备驱动开发详解