Table of Contents
一、应用程序访问驱动原理
二、LCD驱动编写步骤
2.1、.分配fb_info
2.2、初始化fb_info(var fix)
2.3、硬件寄存器操作
2.4、显存设置
2.5、注册
三、LCD驱动代码
一、应用程序访问驱动原理
LCD驱动程序
假设
app: open("/dev/fb0", ...) 主设备号: 29, 次设备号: 0
--------------------------------------------------------------
kernel:
fb_open
int fbidx = iminor(inode);
struct fb_info *info = = registered_fb[0];app: read()
---------------------------------------------------------------
kernel:
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);
src = (u32 __iomem *) (info->screen_base + p);
dst = buffer;
*dst++ = fb_readl(src++);
copy_to_user(buf, buffer, c)
问1. registered_fb在哪里被设置?
答1. register_framebuffer
二、LCD驱动编写步骤
怎么写LCD驱动程序? linux 自带LCD驱动框架(三)
1. 分配一个fb_info结构体: framebuffer_alloc2. 初始化填充fb_info 成员 重要结构体解析
3. 硬件相关的操作 LCD数据手册解析
4. 注册: register_framebuffer
2.1、.分配fb_info
s5pv210_lcd = framebuffer_alloc(0, NULL);
2.2、初始化fb_info(var fix)
// 3.初始化fb_info
// 3.1初始化屏幕的固定参数信息
strcpy(s5pv210_lcd->fix.id, "s5pv210_lcd");
s5pv210_lcd->fix.smem_len = 800*480*4; //480*272*2
s5pv210_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s5pv210_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
s5pv210_lcd->fix.line_length = 800*4; //480*2// 3.2初始化屏幕的可变参数信息
s5pv210_lcd->var.xres = 800; //480 开发板的屏幕是800*480 注释部分为480*272的屏幕
s5pv210_lcd->var.yres = 480; //272
s5pv210_lcd->var.xres_virtual = 800; //480
s5pv210_lcd->var.yres_virtual = 480; //272
s5pv210_lcd->var.bits_per_pixel = 32; //16
s5pv210_lcd->var.red.offset = 16; //11
s5pv210_lcd->var.red.length = 8;//5
s5pv210_lcd->var.green.offset = 8; //5
s5pv210_lcd->var.green.length = 8; //6
s5pv210_lcd->var.blue.offset = 0; //0
s5pv210_lcd->var.blue.length = 8; //5
s5pv210_lcd->var.activate = FB_ACTIVATE_NOW;
2.3、硬件寄存器操作
// 4.硬件初始化
// 4.1GPIO的复用处理
gpf0con = ioremap(0xE0200120,4);
gpf1con = ioremap(0xE0200140,4);
gpf2con = ioremap(0xE0200160,4);
gpf3con = ioremap(0xE0200180,4);
*gpf0con = 0x22222222; // GPF0[7:0]
*gpf1con = 0x22222222; // GPF1[7:0]
*gpf2con = 0x22222222; // GPF2[7:0]
*gpf3con = 0x22222222; // GPF3[7:0]// 4.2初始化LCD控制器相关的寄存器
vidcon0 = ioremap(0xF8000000,4);
vidcon1 = ioremap(0xF8000004,4);
wincon0 = ioremap(0xF8000020,4);
vidosd0a = ioremap(0xF8000040,4);
vidosd0b = ioremap(0xF8000044,4);
vidosd0c = ioremap(0xF8000048,4);
vidw00add0b0 = ioremap(0xF80000A0,4);
vidw00add1b0 = ioremap(0xF80000D0,4);
vidw00add2 = ioremap(0xF8000100,4);
vidtcon0 = ioremap(0xF8000010,4);
vidtcon1 = ioremap(0xF8000014,4);
vidtcon2 = ioremap(0xF8000018,4);
wpalcon = ioremap(0xF80001A0,4);
shadowcon = ioremap(0xF8000034,4);
*vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6) | (1<<2)); /* RGB I/F, RGB Parallel format, */
*vidcon0 |= ((4<<6) | (1<<4) ); /* Divided by CLKVAL_F,vclk== HCLK / (CLKVAL+1) = 166.75/5 = 33.35MHz */
*vidcon1 &= ~(1<<7);
*vidcon1 |= ((1<<6) | (1<<5));// 这个硬件时序初始化一定要会换算!
// 一旦屏幕发生变化,这些参数也要重新换算
*vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
*vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);*vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);
*wincon0 &= ~(0xf << 2);
*wincon0 |= (0xB<<2)/*|(1<<15)*/;
*vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);
*vidosd0b = (RightBotX<<11) | (RightBotY << 0);
*vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);
2.4、显存设置
// 3.5 让内核帮你分配显存的起始物理地址和对应的内核虚拟地址
//内核分配好以后,把显存的物理地址保存在fix.smem_start
//对应的显存的内核虚拟地址保存在screen_base
s5pv210_lcd->screen_base = dma_alloc_writecombine(NULL,
s5pv210_lcd->fix.smem_len,
(dma_addr_t *)&s5pv210_lcd->fix.smem_start,
GFP_KERNEL);//告诉CPU显存的起始物理地址和结束物理地址
*vidw00add0b0 = s5pv210_lcd->fix.smem_start;
*vidw00add1b0 = s5pv210_lcd->fix.smem_start
+ s5pv210_lcd->fix.smem_len;
2.5、注册
//向核心层注册分配初始化好的fb_info
register_framebuffer(s5pv210_lcd);
三、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
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#include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/cpufreq.h> #include <asm/io.h> #include <asm/div64.h> #include <asm/mach/map.h> #include <mach/regs-gpio.h> #define MHZ (1000*1000) #define PRINT_MHZ(m) ((m) / MHZ), ((m / 1000) % 1000) #define VSPW 9 #define VBPD 13 #define LINEVAL 479 #define VFPD 21 #define HSPW 19 #define HBPD 25 #define HOZVAL 799 #define HFPD 209 #define LeftTopX 0 #define LeftTopY 0 #define RightBotX 799 #define RightBotY 479 static unsigned long *vidcon0; /* video control 0 */ static unsigned long *vidcon1; /* video control 1 */ //static unsigned long *vidcon2; /* video control 2 */ static unsigned long *vidtcon0; /* video time control 0 */ static unsigned long *vidtcon1; /* video time control 1 */ static unsigned long *vidtcon2; /* video time control 2 */ static unsigned long *wincon0; /* window control 0 */ static unsigned long *vidosd0a; /* video window 0 position control */ static unsigned long *vidosd0b; /* video window 0 position control1 */ static unsigned long *vidosd0c; /* video window 0 position control */ static unsigned long *vidw00add0b0; /* window 0 buffer start address, buffer 0 */ static unsigned long *vidw00add1b0; /* window 0 buffer end address, buffer 0 */ static unsigned long *vidw00add2; /* window 0 buffer size */ static unsigned long *wpalcon; static unsigned long *shadowcon; static unsigned long *gpf0con; static unsigned long *gpf1con; static unsigned long *gpf2con; static unsigned long *gpf3con; //static unsigned long *gpd0con; //static unsigned long *gpd0dat; //static unsigned long *clk_gate_block; //static unsigned long *display_control; static struct fb_info *s5pv210_lcd; static struct fb_ops s5pv210_lcdfb_ops = { .owner = THIS_MODULE, .fb_fillrect = cfb_fillrect, //填充矩形 .fb_copyarea = cfb_copyarea, //显存区域的拷贝 .fb_imageblit = cfb_imageblit, //处理图像 }; static int s5pv210_lcd_init(void) { // 1.获取LCD控制器的时钟,并且启动LCD控制器时钟 struct clk *s5pv210_clk; s5pv210_clk = clk_get(NULL, "lcd"); clk_enable(s5pv210_clk); // 2.分配fb_info s5pv210_lcd = framebuffer_alloc(0, NULL); // 3.初始化fb_info // 3.1初始化屏幕的固定参数信息 strcpy(s5pv210_lcd->fix.id, "s5pv210_lcd"); s5pv210_lcd->fix.smem_len = 800*480*4; //480*272*2 s5pv210_lcd->fix.type = FB_TYPE_PACKED_PIXELS; s5pv210_lcd->fix.visual = FB_VISUAL_TRUECOLOR; s5pv210_lcd->fix.line_length = 800*4; //480*2 // 3.2初始化屏幕的可变参数信息 s5pv210_lcd->var.xres = 800; //480 s5pv210_lcd->var.yres = 480; //272 s5pv210_lcd->var.xres_virtual = 800; //480 s5pv210_lcd->var.yres_virtual = 480; //272 s5pv210_lcd->var.bits_per_pixel = 32; //16 s5pv210_lcd->var.red.offset = 16; //11 s5pv210_lcd->var.red.length = 8;//5 s5pv210_lcd->var.green.offset = 8; //5 s5pv210_lcd->var.green.length = 8; //6 s5pv210_lcd->var.blue.offset = 0; //0 s5pv210_lcd->var.blue.length = 8; //5 s5pv210_lcd->var.activate = FB_ACTIVATE_NOW; // 3.3提供操作显存的接口 s5pv210_lcd->fbops = &s5pv210_lcdfb_ops; // 3.4 初始化屏幕的大小 s5pv210_lcd->screen_size = 800*480*4; // 4.硬件初始化 // 4.1GPIO的复用处理 gpf0con = ioremap(0xE0200120,4); gpf1con = ioremap(0xE0200140,4); gpf2con = ioremap(0xE0200160,4); gpf3con = ioremap(0xE0200180,4); *gpf0con = 0x22222222; // GPF0[7:0] *gpf1con = 0x22222222; // GPF1[7:0] *gpf2con = 0x22222222; // GPF2[7:0] *gpf3con = 0x22222222; // GPF3[7:0] // 4.2初始化LCD控制器相关的寄存器 vidcon0 = ioremap(0xF8000000,4); vidcon1 = ioremap(0xF8000004,4); wincon0 = ioremap(0xF8000020,4); vidosd0a = ioremap(0xF8000040,4); vidosd0b = ioremap(0xF8000044,4); vidosd0c = ioremap(0xF8000048,4); vidw00add0b0 = ioremap(0xF80000A0,4); vidw00add1b0 = ioremap(0xF80000D0,4); vidw00add2 = ioremap(0xF8000100,4); vidtcon0 = ioremap(0xF8000010,4); vidtcon1 = ioremap(0xF8000014,4); vidtcon2 = ioremap(0xF8000018,4); wpalcon = ioremap(0xF80001A0,4); shadowcon = ioremap(0xF8000034,4); *vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6) | (1<<2)); /* RGB I/F, RGB Parallel format, */ *vidcon0 |= ((4<<6) | (1<<4) ); /* Divided by CLKVAL_F,vclk== HCLK / (CLKVAL+1) = 166.75/5 = 33.35MHz */ *vidcon1 &= ~(1<<7); *vidcon1 |= ((1<<6) | (1<<5)); // 这个硬件时序初始化一定要会换算! // 一旦屏幕发生变化,这些参数也要重新换算 *vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0); *vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0); *vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0); *wincon0 &= ~(0xf << 2); *wincon0 |= (0xB<<2)/*|(1<<15)*/; *vidosd0a = (LeftTopX<<11) | (LeftTopY << 0); *vidosd0b = (RightBotX<<11) | (RightBotY << 0); *vidosd0c = (LINEVAL + 1) * (HOZVAL + 1); // 3.5 让内核帮你分配显存的起始物理地址和对应的内核虚拟地址 //内核分配好以后,把显存的物理地址保存在fix.smem_start //对应的显存的内核虚拟地址保存在screen_base s5pv210_lcd->screen_base = dma_alloc_writecombine(NULL, s5pv210_lcd->fix.smem_len, (dma_addr_t *)&s5pv210_lcd->fix.smem_start, GFP_KERNEL); //告诉CPU显存的起始物理地址和结束物理地址 *vidw00add0b0 = s5pv210_lcd->fix.smem_start; *vidw00add1b0 = s5pv210_lcd->fix.smem_start + s5pv210_lcd->fix.smem_len; *shadowcon = 0x1; *vidcon0 |= 0x3; *wincon0 |= 1; //向核心层注册分配初始化好的fb_info register_framebuffer(s5pv210_lcd); return 0; } static void s5pv210_lcd_exit(void) { unregister_framebuffer(s5pv210_lcd); dma_free_writecombine(NULL, s5pv210_lcd->fix.smem_len, s5pv210_lcd->screen_base, s5pv210_lcd->fix.smem_start); iounmap(gpf0con); iounmap(gpf1con); iounmap(gpf2con); iounmap(gpf3con); //iounmap(gpd0con); //iounmap(gpd0dat); //iounmap(display_control); iounmap(vidcon0); iounmap(vidcon1); iounmap(vidtcon2); iounmap(wincon0); iounmap(vidosd0a); iounmap(vidosd0b); iounmap(vidosd0c); iounmap(vidw00add0b0); iounmap(vidw00add1b0); iounmap(vidw00add2); iounmap(vidtcon0); iounmap(vidtcon1); iounmap(shadowcon); framebuffer_release(s5pv210_lcd); } module_init(s5pv210_lcd_init); module_exit(s5pv210_lcd_exit); MODULE_LICENSE("GPL");
最后
以上就是喜悦野狼最近收集整理的关于linux LCD 驱动编写(四)一、应用程序访问驱动原理二、LCD驱动编写步骤三、LCD驱动代码的全部内容,更多相关linux内容请搜索靠谱客的其他文章。
发表评论 取消回复