驱动之LCD驱动框架和实现
总览
- 嵌入式linux学习目录
- 驱动之input子系统
- 驱动之platform概念
- 驱动之RTC分析
- 驱动之LCD驱动框架和实现
- 驱动之触摸屏驱动框架和实现
- 驱动之USB基础概念和框架
- 驱动之USB设备驱动程序
本文使用 linux-2.6.22.6 内核, 使用jz2440开发板.
LCD驱动框架分析
字符驱动基本步骤
根据之前写的驱动, 已经对linux驱动基本步骤比较熟悉了.
- 所有的驱动都会调用
module_init和module_exit, 从module_init开始看比较好. - 定义并设置
file_operations结构体, 然后实现里面的函数, 如open等. - 获取
主设备号, 可以手动分配, 也可以由系统自动分配 - 用
register_chrdev注册字符设备. 核心过程如下:__register_chrdev_region注册/申请主设备号, 并申请子设备号范围.cdev_init用file_operations结构体初始化一个字符设备cdev_add用设备号向系统添加字符设备.
- 如果要用mdev自动加载驱动, 还需要在init里实现如下函数
class_create, 创建一个设备类. 可以在/sys/class/看到设备类名称device_create, 创建和注册设备. 可以在/dev/看到设备名称class_device_create是低版本Linux的函数. 本质就是device_create
LCD驱动框架分析
Linux的LCD驱动用了分层分离的思想, 用到了platform框架.
/drivers/video/fbmem.cframe buffer memory, 显存操作相关subsys_initcall(fbmem_init);fbmem的初始化.register_chrdev(FB_MAJOR,"fb",&fb_fops)注册字符设备,fb_class = class_create(THIS_MODULE, "graphics");注册 graphics 设备类- 可以去
/sys/class/graphics看看, 下面有 fb0 和 fbcon 两个文件. - 这里没有注册设备, 因为视频控制器和具体硬件相关
registered_fb是具体设备给fbmem.c提供信息的关键!fb_readfb_write里都可以看到struct fb_info *info = registered_fb[fbidx];- 然后, 函数根据 info 信息, 决定是进一步调用具体设备的 read write等函数, 还是使用默认代码.
register_framebuffer(struct fb_info *fb_info)供LCD设备调用, 提交registered_fb信息并注册设备.device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), "fb%d", i)- 真正注册一个LCD设备, 名字是fb0, fb1这样递加上去. 可以在
/dev/里找到.
/drviers/video/s3c2410fb.c具体硬件的LCD驱动.- 这里用到了platform框架.
s3c2410fb.c是硬件相关的通用操作, 属于platform_driver module_init里, 直接就是platform_driver_register.- 我们知道platform框架里,
probe函数是很关键的, 在drive和device匹配时, 就会调用它. probe函数里, 初始化后, 可看到register_framebuffer(fbinfo);将LCD设备信息提交给fbmem.c, 并注册设备.
- 这里用到了platform框架.
/arch/arm/mach-s3c2440/mach-smdk2440.c配置硬件参数的地方.- 这里是platform框架的
platform_device. smdk2440_machine_init初始化里s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);将LCD配置信息拷贝到s3c_device_lcdplatform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));注册 platform_device 设备.smdk2440_devices里就包含了s3c_device_lcd
- 如果硬件平台不变, 只是换屏的话, 只需要修改
mach-smdk2440.c即可. 这就是分层分离概念的意义所在.
- 这里是platform框架的
补充说明 fbmem.c 的上层:
/drivers/video/console/fbcon.c在lcd上显示终端, 此文件和tty1关联.class_device_create(fb_class, NULL, MKDEV(0, 0), NULL, "fbcon");注册fbcon设备fbcon_start和 fb设备对接, 开始显示.
- app层调用
open("/dev/fb0", ...), 主设备号为29, 次设备号为0- 会对应到kernel层
fbmem.c的fb_open函数: int fbidx = iminor(inode);struct fb_info *info = = registered_fb[0];
- 会对应到kernel层
- app层调用
read()- 会对应到kernel层
fbmem.c的fb_read函数: registered_fb由register_framebuffer设置.
- 会对应到kernel层
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { |
LCD驱动源码
现在尝试忽略 /drviers/video/s3c2410fb.c 使用的platform框架.
直接自己写一个 LCD 驱动, 和 /drivers/video/fbmem.c 进行对接.
此处只是为了练习, 实际项目不建议这样使用
驱动的核心步骤如下:
- 分配一个fb_info:
s3c_lcd = framebuffer_alloc(0, NULL); - 设置fb_info
2.1 设置固定的参数,struct fb_fix_screeninfo
2.2 设置可变的参数,struct fb_var_screeninfo
2.3 设置操作函数,fbops
2.4 其他的设置 - 硬件相关的操作
3.1 配置GPIO用于LCD
3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等
3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 - 注册
register_framebuffer(s3c_lcd);
测试, 原系统
在使用自己写的LCD驱动源码之前, 先用系统提供的LCD框架驱动测试一下显示屏
# 开发板端 |
lcd.c
|
Makefile
obj-m := lcd.o |
测试
这个测试比较复杂, 需要去掉自带的LCD驱动, 重新编译和烧录内核.
# Ubuntu 主机端 |
参考资料
原创于 DRA&PHO