我是靠谱客的博主 仁爱鸡翅,这篇文章主要介绍LCD驱动程序,现在分享给大家,希望可以做个参考。

学习目标:熟悉TFT LCD的概念,分层驱动工作原理和程序编写。

一、LCD 概念

1.  显示器数据组织格式

1)一幅图像成为一帧,每帧由多行组成,每行由多个像素组成。每个像素的颜色由若干位表示,对于256色LCD,每个像素由8位表示,称为8BPP。

2)显示器呈Z字行的路线进行扫描显示,使用HSYNC、VSYNC控制扫描和跳转的路径;

2、操作过程

1)设置LCD的HSYNC、VSYNCVCLK等信号的参数,并将帧内存的地址告诉LCD控制器,塔克自动的发起DMA传输,从帧内存中得到图像数据,出现在数据总线VD[23:0]上。我们只需要将显示的图像数据写入帧内存中即可。

2)图像数据的存储:

例如:由三原色组建的256色(8BPP)显示模式,使用8位数据表示一个像素的颜色。但特殊的是,这8位数据用于表示在调色板中的索引值。这里的调色板使用256*16的内存,即使用16BPP的显示格式来表示对应各个索引值的颜色。因此,最终在LCD显示的仍为16BPP的数据。

内存数据和像素对应的关系为:

其中,P1、P2...为一个个的像素。

像素在调色板中的数据存放模式16BPP分为两种格式:5:6:5和5:5:5:1.即:

二、LCD驱动

1、帧缓冲设备

     frambuffer设备层是对显示设备的一种抽象。其中,帧缓冲是Linux为显示设备提供的一个接口,它把一些显示设备描述成一个缓冲区,允许应用程序通过FrameBuffer定义好的接口访问这些图形设备,从而不用去关心具体的硬件细节。对于帧缓冲设备而言,只要在显示缓冲区与显示点对应的区域写入颜色值,对应的颜色就会自动的在屏幕上显示。

2、LCD作为一种帧缓冲设备,也是一种标准的字符型设备,对应于文件系统下/dev/fb%d设备文件。

3、驱动结构

首先分析一下driver/video/fbmem.c

1)进入__init fbmem_init(入口函数),主要创建了字符设备“fb”和类,利用cat 命令查看(cat /proc/devices),可看到该目录下的fb,主设备号为29。由于还没有注册LCD驱动,所以没有设备节点,

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1 static int __init fbmem_init(void) 2 { 3 create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL); 4 //创建字符设备"fb" 5 if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) 6 printk("unable to get major %d for fb devsn", FB_MAJOR); 7 8 fb_class = class_create(THIS_MODULE, "graphics"); //创建类graphics" 9 if (IS_ERR(fb_class)) { 10 printk(KERN_WARNING "Unable to create fb class; errno = %ldn", PTR_ERR(fb_class)); 11 fb_class = NULL; 12 } 13 return 0; 14 }

2)fb_fops结构体及open函数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1 static const struct file_operations fb_fops = { 2 .owner = THIS_MODULE, 3 .read = fb_read, 4 .write = fb_write, 5 .ioctl = fb_ioctl, 6 #ifdef CONFIG_COMPAT 7 .compat_ioctl = fb_compat_ioctl, 8 #endif 9 .mmap = fb_mmap, 10 .open = fb_open, 11 .release = fb_release, 12 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA 13 .get_unmapped_area = get_fb_unmapped_area, 14 #endif 15 #ifdef CONFIG_FB_DEFERRED_IO 16 .fsync = fb_deferred_io_fsync, 17 #endif 18 };

-->fb_open函数:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
1 static int fb_open(struct inode *inode, struct file *file) 2 { 3 int fbidx = iminor(inode); //取出设备次设备号 4 struct fb_info *info; //定义一个fb_info结构体 5 int res = 0; 6 7 if (fbidx >= FB_MAX) 8 return -ENODEV; 9 #ifdef CONFIG_KMOD 10 if (!(info = registered_fb[fbidx])) // 在次设备里面得到fb_info结构信息(lcd的驱动信息)赋值给info
11 try_to_load(fbidx); 12 #endif /* CONFIG_KMOD */ 13 if (!(info = registered_fb[fbidx])) 14 return -ENODEV; 15 if (!try_module_get(info->fbops->owner)) 16 return -ENODEV; 17 file->private_data = info; 18 if (info->fbops->fb_open) { //如果该设备info结构体有open函数,就执行registered_fb[fbidx]->fbops->fb_open
19 res = info->fbops->fb_open(info,1); 20 if (res) 21 module_put(info->fbops->owner); 22 } 23 return res; 24 }

由于fb设备(帧缓冲设备)主设备号固定,不同设备以次设备号进行区分,执行该设备open函数时,最终指向的是对应设备的open函数。

接下来看一下fb_read函数:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
1 static ssize_t 2 fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 3 { 4 unsigned long p = *ppos; 5 struct inode *inode = file->f_path.dentry->d_inode; 6 int fbidx = iminor(inode);  //取出设备次设备号 7 struct fb_info *info = registered_fb[fbidx]; //定义并获取设备的fb_info结构体 8 u32 *buffer, *dst; 9 u32 __iomem *src; 10 int c, i, cnt = 0, err = 0; 11 unsigned long total_size; 12 13 if (!info || ! info->screen_base) 14 return -ENODEV; 15 16 if (info->state != FBINFO_STATE_RUNNING) 17 return -EPERM; 18 19 if (info->fbops->fb_read) 20 return info->fbops->fb_read(info, buf, count, ppos); //如果该设备fb_info结构体有read函数,就执行registered_fb[fbidx]->fbops->fb_read 21 22 total_size = info->screen_size; 23 24 if (total_size == 0) 25 total_size = info->fix.smem_len; 26 27 if (p >= total_size) 28 return 0; 29 30 if (count >= total_size) 31 count = total_size; 32 33 if (count + p > total_size) 34 count = total_size - p; 35 36 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, 37 GFP_KERNEL); 38 if (!buffer) 39 return -ENOMEM; 40 41 src = (u32 __iomem *) (info->screen_base + p); 42 43 if (info->fbops->fb_sync) 44 info->fbops->fb_sync(info); 45 46 while (count) { 47 c = (count > PAGE_SIZE) ? PAGE_SIZE : count; 48 dst = buffer; 49 for (i = c >> 2; i--; ) 50 *dst++ = fb_readl(src++); 51 if (c & 3) { 52 u8 *dst8 = (u8 *) dst; 53 u8 __iomem *src8 = (u8 __iomem *) src; 54 55 for (i = c & 3; i--;) 56 *dst8++ = fb_readb(src8++); 57 58 src = (u32 __iomem *) src8; 59 } 60 61 if (copy_to_user(buf, buffer, c)) { 62 err = -EFAULT; 63 break; 64 } 65 *ppos += c; 66 buf += c; 67 cnt += c; 68 count -= c; 69 } 71 kfree(buffer); 73 return (err) ? err : cnt; 74 }

由以上程序可知,read的调用和open类似。都依赖于对应设备的fb_info结构体info,在程序中是由registered_fb[fbidx]数组获取的。
3)最后,看一下registered_fb[fbidx]数组的定义,位于register_framebuffer函数中。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
1 int register_framebuffer(struct fb_info *fb_info) 2 { 3 int i; 4 struct fb_event event; 5 struct fb_videomode mode; 6 7 if (num_registered_fb == FB_MAX) 8 return -ENXIO; 9 num_registered_fb++; 10 for (i = 0 ; i < FB_MAX; i++) 11 if (!registered_fb[i]) 12 break; 13 fb_info->node = i; 14 15 fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), "fb%d", i);//创建设备节点,名称为fbi,主设备号为FB_MAJOR 29,次设备号为i 17 if (IS_ERR(fb_info->dev)) { 18 /* Not fatal */ 19 printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ldn", i, PTR_ERR(fb_info->dev)); 20 fb_info->dev = NULL; 21 } else 22 fb_init_device(fb_info); 23 24 if (fb_info->pixmap.addr == NULL) { 25 fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); 26 if (fb_info->pixmap.addr) { 27 fb_info->pixmap.size = FBPIXMAPSIZE; 28 fb_info->pixmap.buf_align = 1; 29 fb_info->pixmap.scan_align = 1; 30 fb_info->pixmap.access_align = 32; 31 fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; 32 } 33 } 34 fb_info->pixmap.offset = 0; 35 36 if (!fb_info->pixmap.blit_x) 37 fb_info->pixmap.blit_x = ~(u32)0; 38 39 if (!fb_info->pixmap.blit_y) 40 fb_info->pixmap.blit_y = ~(u32)0; 41 42 if (!fb_info->modelist.prev || !fb_info->modelist.next) 43 INIT_LIST_HEAD(&fb_info->modelist); 44 45 fb_var_to_videomode(&mode, &fb_info->var); 46 fb_add_videomode(&mode, &fb_info->modelist); 47 registered_fb[i] = fb_info; //赋值到registered_fb[i]数组中 48 49 event.info = fb_info; 50 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); 51 return 0; 52 }

接下来看一下fb硬件驱动程序,以/drivers/video/s3c2410fb.c为例。

1)驱动入口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1 static struct platform_driver s3c2410fb_driver = { 2 .probe = s3c2410fb_probe, 3 .remove = s3c2410fb_remove, 4 .suspend = s3c2410fb_suspend, 5 .resume = s3c2410fb_resume, 6 .driver = { 7 .name = "s3c2410-lcd", 8 .owner = THIS_MODULE, 9 }, 10 }; 11 12 int __devinit s3c2410fb_init(void) 13 { 14 return platform_driver_register(&s3c2410fb_driver); 15 } 16 17 static void __exit s3c2410fb_cleanup(void) 18 { 19 platform_driver_unregister(&s3c2410fb_driver); 20 }

2)当平台设备的驱动和设备匹配后,会直接调用prob函数。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
1 static int __init s3c2410fb_probe(struct platform_device *pdev) 2 { 3 struct s3c2410fb_info *info; 4 struct fb_info *fbinfo; 5 struct s3c2410fb_hw *mregs; 6 int ret; 7 int irq; 8 int i; 9 u32 lcdcon1; 10 11 mach_info = pdev->dev.platform_data; //获取lcd设备信息 12 if (mach_info == NULL) { 13 dev_err(&pdev->dev,"no platform data for lcd, cannot attachn"); 14 return -EINVAL; 15 } 25 fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); //分配fb_info结构体 26 if (!fbinfo) { 27 return -ENOMEM; 28 }
     //设置fb_info结构体
31 info = fbinfo->par; 32 info->fb = fbinfo; 33 info->dev = &pdev->dev; 34 35 platform_set_drvdata(pdev, fbinfo); 37 dprintk("devinitn"); 39 strcpy(fbinfo->fix.id, driver_name); 41 memcpy(&info->regs, &mach_info->regs, sizeof(info->regs)); 43 /* Stop the video and unset ENVID if set */ 44 info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID; 45 lcdcon1 = readl(S3C2410_LCDCON1); 46 writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); 47 48 info->mach_info = pdev->dev.platform_data; 49 50 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 51 fbinfo->fix.type_aux = 0; 52 fbinfo->fix.xpanstep = 0; 53 fbinfo->fix.ypanstep = 0; 54 fbinfo->fix.ywrapstep = 0; 55 fbinfo->fix.accel = FB_ACCEL_NONE;
64 fbinfo->fbops = &s3c2410fb_ops; 65 fbinfo->flags = FBINFO_FLAG_DEFAULT; 66 fbinfo->pseudo_palette = &info->pseudo_pal; 67 73 74 fbinfo->var.upper_margin = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1; 75 fbinfo->var.lower_margin = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1; 76 fbinfo->var.vsync_len = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1; 77 78 fbinfo->var.left_margin = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1; 79 fbinfo->var.right_margin = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1; 80 fbinfo->var.hsync_len = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1; 81 90 fbinfo->fix.smem_len = mach_info->xres.max * 91 mach_info->yres.max * 92 mach_info->bpp.max / 8; 93 94 for (i = 0; i < 256; i++) 95 info->palette_buffer[i] = PALETTE_BUFF_CLEAR; 96 97 if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) { 98 ret = -EBUSY; 99 goto dealloc_fb; 100 } 103 dprintk("got LCD regionn"); 104 //硬件相关的操作,中断、时钟.... 105 ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); 106 if (ret) { 107 dev_err(&pdev->dev, "cannot get irq %d - err %dn", irq, ret); 108 ret = -EBUSY; 109 goto release_mem; 110 } 111 112 info->clk = clk_get(NULL, "lcd"); 113 if (!info->clk || IS_ERR(info->clk)) { 114 printk(KERN_ERR "failed to get lcd clock sourcen"); 115 ret = -ENOENT; 116 goto release_irq; 117 } 118 119 clk_enable(info->clk); 120 dprintk("got and enabled clockn"); 121 122 msleep(1); 123 124 /* Initialize video memory */ 125 ret = s3c2410fb_map_video_memory(info); 126 if (ret) { 127 printk( KERN_ERR "Failed to allocate video RAM: %dn", ret); 128 ret = -ENOMEM; 129 goto release_clock; 130 } 137 ret = register_framebuffer(fbinfo);//注册fb_info结构体 138 if (ret < 0) { 139 printk(KERN_ERR "Failed to register framebuffer device: %dn", ret); 140 goto free_video_memory; 141 } 142 143 /* create device files */ 144 device_create_file(&pdev->dev, &dev_attr_debug); 145 146 printk(KERN_INFO "fb%d: %s frame buffer devicen", 147 fbinfo->node, fbinfo->fix.id); 148 149 return 0; 150 151 free_video_memory: 152 s3c2410fb_unmap_video_memory(info); 153 release_clock: 154 clk_disable(info->clk); 155 clk_put(info->clk); 156 release_irq: 157 free_irq(irq,info); 158 release_mem: 159 release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD); 160 dealloc_fb: 161 framebuffer_release(fbinfo); 162 return ret; 163 }

小结:

根据驱动结构和程序源码分析可知,lcd驱动程序需要完成以下几部分:

1)分配一个fb_info结构体:由函数framebuffer_alloc() 完成 ;

2)设置fb_info结构体;

3)注册fb_inforegister_framebuffer();

4)硬件相关的操作 

转载于:https://www.cnblogs.com/lxl-lennie/p/10248889.html

最后

以上就是仁爱鸡翅最近收集整理的关于LCD驱动程序的全部内容,更多相关LCD驱动程序内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(106)

评论列表共有 0 条评论

立即
投稿
返回
顶部