内核LCD框架分析
最近在看韦佬的LCD驱动部分的视频,韦佬对内核自带的LCD驱动框架的讲解较少,所以在此先分析一下内核自带的LCD驱动框架。
内核自带的LCD驱动框架依然符合分层分离驱动模型,不再复述分离分层驱动模型,直接分析LCD的驱动框架。
1 相关文件
- 核心层
fbmem.c - driver
s3c2410fb.c - device
mach-smdk2440.c
fb.h
2 核心层 fbmem.c
核心层提供了标准的lcd接口函数和注册函数
- fbmem_init()(驱动入口函数)
if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) //注册字符设备驱动
printk("unable to get major %d for fb devs\n", FB_MAJOR);
fb_class = class_create(THIS_MODULE, "graphics");
if (IS_ERR(fb_class)) {
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
fb_class = NULL;
/* 此时没有创建设备节点 */
- fb_open()
根据次设备号查找registered_fb[fbidx]里面的info结构体,如果info结构体的fops里面有open函数,则调用。
int fbidx = iminor(inode);
struct fb_info *info;
info = registered_fb[fbidx] // 从registered_fb中获取到LCD信息
if (info->fbops->fb_open) {
res = info->fbops->fb_open(info,1);
if (res)
module_put(info->fbops->owner);
/* 调用info结构体里面的open函数 */
/* lcd.c里面没有实现open函数 */
- fb_read()
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
- register_framebuffer
提供注册函数,创建设备节点
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i; //查找空数组
fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), "fb%d", i); // 此时创建设备节点
...
registered_fb[i] = fb_info; // 注册fb_info结构体,即放入数组registered_fb[i]
3 driver层 s3c2410fb.c
平台驱动,驱动中稳定的部分
- s3c2410fb_init :
platform_driver_register(&s3c2410fb_driver); //注册平台设备
- 构造platform_driver结构体
static struct platform_driver s3c2410fb_driver = {
.probe = s3c2410fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = {
.name = "s3c2410-lcd",
.owner = THIS_MODULE,
},
}; // 按照分离分层驱动模型,是按照名字与device进行匹配
/* 分离分层驱动模型 : driver,device匹配上就执行probe函数 */
- s3c2410fb_probe(struct platform_device *pdev)
1分配一个info结构体
2设置info结构体
3硬件相关的操作
4注册info结构体
mach_info = pdev->dev.platform_data; //获取lcd数据
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
/*分配一个fbinfo结构体*/
strcpy(fbinfo->fix.id, driver_name);
fbinfo->fbops = &s3c2410fb_ops;
...
/*设置info结构体 名字,操作函数等*/
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
info->clk = clk_get(NULL, "lcd");
/* 硬件操作 */
ret = register_framebuffer(fbinfo);
/* 注册info结构体,同时创建设备节点*/
4 device 层 mach-smdk2440.c fb.h
平台设备,提供设备信息
static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {
.regs = {
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
S3C2410_LCDCON1_TFT |
S3C2410_LCDCON1_CLKVAL(0x04),
.lcdcon2 = S3C2410_LCDCON2_VBPD(7) |
S3C2410_LCDCON2_LINEVAL(319) |
S3C2410_LCDCON2_VFPD(6) |
S3C2410_LCDCON2_VSPW(3),
.lcdcon3 = S3C2410_LCDCON3_HBPD(19) |
S3C2410_LCDCON3_HOZVAL(239) |
S3C2410_LCDCON3_HFPD(7),
.lcdcon4 = S3C2410_LCDCON4_MVAL(0) |
S3C2410_LCDCON4_HSPW(3),
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
},
...
};
5 fb_info结构体
struct fb_info {
atomic_t count;
int node; //子设备号
int flags;
struct mutex lock; //互斥锁
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; //当前缓冲区的可变参数
struct fb_fix_screeninfo fix; //固定参数
struct fb_monspecs monspecs; //当前显示器标志
struct work_struct queue; //帧缓冲事件队列
struct fb_pixmap pixmap; //图像硬件mapper
struct fb_pixmap sprite; //光标硬件mapper
struct fb_cmap cmap; //当前的调色板
struct list_head modelist; /* mode list */
struct fb_videomode *mode; //当前的视频模式
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device *//支持lcd背光的设置
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops; //帧缓冲操作函数集
struct device *device; //父设备
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; //虚拟基地址
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
如何自己写LCD驱动程序
基本是按照probe函数里面的步骤
1 分配一个fb_info结构体,framebuffer_alloc
2 设置
3 注册 : register_framebuffer
4 硬件相关的操作
LCD硬件操作
LCD硬件操作的步骤:设置芯片的LCD控制器部分的寄存器,对照LCD的芯片手册设置即可
1 设置水平方向时间参数
- HBPD : HSYNC之后过多长时间发出第1个像素的数据(左黑边)
- 多少列:HOZVAL
- HFPD : 发出一行里最后一个像素之后再过多长时间才发出HSYNC(右黑边)
- HSPW :HSYNC信号的脉冲宽度
2 设置垂直方向的时间参数
- VBPD : VSYNC之后过多长时间发出第一行数据(上黑边)
- 多少行:LINEVAL
- VFPD 发出最后一行数据之后再过多长时间才发出VSYNC(下黑边)
- VSPW: VSYNC信号的脉冲宽度
3 设置LCD的其他参数
- VCLK : 设置LCD的时钟
- bpp : 设置像素的位数
- 设置framebuffer的数据格式
4 设置信号的极性
- HYSNC反转
- VYSNC反转
- video data 反转
- VDEN 反转
- ...
5 分配framebuffer
- dma_alloc_writecombine分配内存
- 物理地址赋值给芯片的寄存器
6 启动LCD
- 使能LCD控制器
- 使能LCD本身
- 使能背光
网友评论