分类: 嵌入式
2015-06-01 19:15:22
LCD驱动程序
15年5月22日21:26:20
1 程序如下:
2
#include
3
#include
4
#include
5
#include
6
#include
7
#include
8
#include
9
#include
10
#include
11
#include
12
#include
13
#include
14
#include
15
#include
16
#include
17
18
#include
19
#include
20
#include
21
22
#include
23
#include
24
#include
25
#include
26
27 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
28 unsigned int green, unsigned int blue,
29 unsigned int transp, struct fb_info *info);
30
31 static struct fb_info *s3c_lcd;
32
33 static volatile unsigned long *gpbcon;
34 static volatile unsigned long *gpbdat;
35 static volatile unsigned long *gpccon;
36 static volatile unsigned long *gpdcon;
37 static volatile unsigned long *gpgcon;
38
39 static u32 pseudo_palette[16];
40
41 static struct lcd_regs{
42 unsigned long lcdcon1;
43 unsigned long lcdcon2;
44 unsigned long lcdcon3;
45 unsigned long lcdcon4;
46 unsigned long lcdcon5;
47 unsigned long lcdsaddr1;
48 unsigned long lcdsaddr2;
49 unsigned long lcdsaddr3;
50 unsigned long redlut;
51 unsigned long greenlut;
52 unsigned long bluelut;
53 unsigned long reserved[9];
54 unsigned long dithmode;
55 unsigned long tpal;
56 unsigned long lcdintpnd;
57 unsigned long lcdsrcpnd;
58 unsigned long lcdintmsk;
59 unsigned long lpcsel;
60 };
61
62 static volatile struct lcd_regs *lcd_reg;
63
64 static struct fb_ops s3c_lcdfb_ops = {
65 .owner = THIS_MODULE,
66 .fb_setcolreg = s3c_lcdfb_setcolreg,
67 .fb_fillrect = cfb_fillrect,
68 .fb_copyarea = cfb_copyarea,
69 .fb_imageblit = cfb_imageblit,
70 };
71 /* from pxafb.c */
72 static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
73 {
74 chan &= 0xffff;
75 chan >>= 16 - bf->length;
76 return chan << bf->offset;
77 }
78
79 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
80 unsigned int green, unsigned int blue,
81 unsigned int transp, struct fb_info *info)
82 {
83 unsigned long val;
84
85 if (regno > 16)
86 return 1;
87 /* 用red,green,blue三原色构造出val */
88 val = chan_to_field(red, &info->var.red);
89 val |= chan_to_field(green, &info->var.green);
90 val |= chan_to_field(blue, &info->var.blue);
91
92 pseudo_palette[regno] = val;
93 return 0;
94
95 }
96 static int lcd_init()
97 {
98 /* 1. 分配一个 fb_info 结构体 */
99 s3c_lcd = framebuffer_alloc(0, NULL);
100
101 /* 2. 设置 */
102 /* 2.1 设置固定参数 */
103 strcpy(s3c_lcd->fix.id, "mylcd");
104 s3c_lcd->fix.smem_len = 240*320*16/8;
105 s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
106 s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
107 s3c_lcd->fix.line_length = 240*2;
108
109 /* 2.2 设置可变参数 */
110 s3c_lcd->var.xres = 240;
111 s3c_lcd->var.yres = 320;
112 s3c_lcd->var.xres_virtual= 240;
113 s3c_lcd->var.yres_virtual= 320;
114 s3c_lcd->var.bits_per_pixel = 16;
115
116 /* RGB:565, 从右边开始 */
117 s3c_lcd->var.red.offset = 11;
118 s3c_lcd->var.red.length = 5;
119
120 s3c_lcd->var.green.offset = 5;
121 s3c_lcd->var.green.length = 6;
122
123 s3c_lcd->var.blue.offset = 0;
124 s3c_lcd->var.blue.length = 5;
125
126 s3c_lcd->var.activate = FB_ACTIVATE_NOW;
127
128 /* 2.3 设置操作函数 */
129 s3c_lcd->fbops = &s3c_lcdfb_ops;
130
131 /* 2.4 其他的设置 */
132 s3c_lcd->pseudo_palette = pseudo_palette;
133 //s3c_lcd->screen_base = ; /* xian cun de xu ni di zhi */
134 s3c_lcd->screen_size = 240*320*16/8;
135
136 /* 3. 硬件相关操作 */
137 /* 3.1 配置 GPIO 用于 LCD */
138 gpbcon = ioremap(0x56000010, 8);
139 gpbdat = gpbcon + 1;
140 gpccon = ioremap(0x56000020, 4);
141 gpdcon = ioremap(0x56000030, 4);
142 gpgcon = ioremap(0x56000060, 4);
143
144 *gpccon = 0xaaaaaaaa; /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
145 *gpdcon = 0xaaaaaaaa; /* GPIO管脚用于VD[23:8] */
146
147 *gpbcon &= ~(3); /* GPB0设置为输出引脚 */
148 *gpbcon |= 1;
149 *gpbdat &= ~1; /* 输出低电平 */
150
151 *gpgcon |= (3 << 8);
152
153
154 /* 3.2 根据 LCD 手册设置 LCD 控制器,比如 VCLK 的频率等 */
155 lcd_reg = ioremap(0x4D000000, sizeof(struct lcd_regs));
/* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2], LCD手册P14
* 10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
* CLKVAL = 4
* bit[6:5]: 0b11, TFT LCD
* bit[4:1]: 0b1100, 16 bpp for TFT
* bit[0] : 0 = Disable the video output and the LCD control signal.
*/
156 lcd_reg->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1);
/* 垂直方向的时间参数
* bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册 T0-T2-T1=4
* VBPD=3
* bit[23:14]: 多少行, 320, 所以LINEVAL=320-1=319
* bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC
* LCD手册T2-T5=322-320=2, 所以VFPD=2-1=1
* bit[5:0] : VSPW, VSYNC信号的脉冲宽度, LCD手册T1=1, 所以VSPW=1-1=0
*/
157 lcd_reg->lcdcon2 = (3<<24) | (319<<14) | (1<<6) | (0<<0);
/* 水平方向的时间参数
* bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册 T6-T7-T8=17
* HBPD=16
* bit[18:8]: 多少列, 240, 所以HOZVAL=240-1=239
* bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
* LCD手册T8-T11=251-240=11, 所以HFPD=11-1=10
*/
158 lcd_reg->lcdcon3 = (16<<19) | (239<<8) | (10<<0);
/* 水平方向的同步信号
* bit[7:0] : HSPW, HSYNC信号的脉冲宽度, LCD手册T7=5, 所以HSPW=5-1=4
*/
159 lcd_reg->lcdcon4 = 4;
/* 信号的极性
* bit[11]: 1=565 format
* bit[10]: 0 = The video data is fetched at VCLK falling edge
* bit[9] : 1 = HSYNC信号要反转,即低电平有效
* bit[8] : 1 = VSYNC信号要反转,即低电平有效
* bit[6] : 0 = VDEN不用反转
* bit[3] : 0 = PWREN输出0
* bit[1] : 0 = BSWP
* bit[0] : 1 = HWSWP 2440手册P413
*/
160 lcd_reg->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);
161
162
163 /* 3.3 分配显存(framebuffer), 并把地址告诉 LCD 控制器 */
164 s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
165 lcd_reg->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);
166 lcd_reg->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
167 lcd_reg->lcdsaddr3 = (240*16/16);
168
169 /* 启动 LCD */
170 lcd_reg->lcdcon1 |= (1<<0); /* 使能LCD控制器 */
171 lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本身 */
172 *gpbdat |= 1; /* 输出高电平, 使能背光 */
173 //s3c_lcd->fix.smem_start = xxx;
174 /* 4. 注册 */
175 register_framebuffer(s3c_lcd);
176
177 return 0;
178 }
179
180 static void lcd_exit()
181 {
182 unregister_framebuffer(s3c_lcd);
183 lcd_reg->lcdcon1 &= ~(1<<0);
184 *gpbdat &= ~1;
185 dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
186 iounmap(lcd_reg);
187 iounmap(gpbcon);
188 iounmap(gpccon);
189 iounmap(gpdcon);
190 iounmap(gpgcon);
191
192 framebuffer_release(s3c_lcd);
193 }
194
195 module_init(lcd_init);
196 module_exit(lcd_exit);
197
198 MODULE_LICENSE("GPL");
199
1. 帧缓冲最重要的一个结构体是fb_info结构体,下面列出这个结构体:
struct fb_info {
int node;
int flags;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* 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; /* This is the parent */
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; /* Virtual address */
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;
};
这个结构体里面记录了帧缓冲设备的全部信息,包括设备的设置参数,状态,以及操作函数指针,这几个结构体或函数我标注出来了,其中包括:struct fb_var_screeninfo var; 可变参数,struct fb_fix_screeninfo fix;固定参数,struct fb_ops *fbops; 操作结构体,char __iomem *screen_base; 虚拟基地址,unsigned long screen_size; ioremapped的虚拟地址大小, void *pseudo_palette; 伪16色颜色表。每一个帧缓冲设备对应一个这样的结构体。
下面分别列出这几个结构体:
(1) fb_var_screeninfo和 struct fb_fix_screeninfo结构体:
fb_var_screeninfo结构体包含用户可以修改的显示控制器参数,包括屏幕的分辨率和每个像素点的比特数。xres定义了一行有多少个点,yres定义了一列有多少个点。 xres_virtual定义虚拟屏幕一行有多少个点等信息,其他成员如下所示:
struct fb_var_screeninfo {
__u32 xres; /* visible resolution */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* != 0 Graylevels instead of colors */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 reserved[5]; /* Reserved for future compatibility */
};
fb_fix_screeninfo结构体记录了用户不能修改的显示控制器的参数,如屏幕缓冲区的物理地址,长度等,结构体其他成员如下所示:
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 reserved[3]; /* Reserved for future compatibility */
};
(2)还有一个类似字符设备驱动程序的结构体:struct fb_ops *fbops 。
struct fb_ops {
/* open/release and usage marking */
struct module *owner;
int (*fb_open) (struct fb_info *info, int user);
int (*fb_release) (struct fb_info *info, int user);
/* For framebuffers with strange non linear layouts or that do not
* work with normal memory mapped access
*/
ssize_t (*fb_read) (struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
ssize_t (*fb_write) (struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos);
/* checks var and eventually tweaks it to something supported,
* DO NOT MODIFY PAR */
int (*fb_check_var) (struct fb_var_screeninfo *var, struct fb_info *info);
/* set the video mode according to info->var */
int (*fb_set_par) (struct fb_info *info);
/* set color register */
int (*fb_setcolreg) (unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
/* set color registers in batch */
int (*fb_setcmap) (struct fb_cmap *cmap, struct fb_info *info);
/* blank display */
int (*fb_blank) (int blank, struct fb_info *info);
/* pan display */
int (*fb_pan_display) (struct fb_var_screeninfo *var, struct fb_info *info);
/* Draws a rectangle */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* Copy data from area to another */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* Draws a image to the display */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* Draws cursor */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* Rotates the display */
void (*fb_rotate) (struct fb_info *info, int angle);
/* wait for blit idle, optional */
int (*fb_sync) (struct fb_info *info);
/* perform fb specific ioctl (optional) */
int (*fb_ioctl) (struct fb_info *info, unsigned int cmd,
unsigned long arg);
/* Handle 32bit compat ioctl (optional) */
int (*fb_compat_ioctl) (struct fb_info *info, unsigned cmd,
unsigned long arg);
/* perform fb specific mmap */
int (*fb_mmap) (struct fb_info *info, struct vm_area_struct *vma);
/* save current hardware state */
void (*fb_save_state) (struct fb_info *info);
/* restore saved state */
void (*fb_restore_state) (struct fb_info *info);
/* get capability given var */
void (*fb_get_caps) (struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
};
下面总结写LCD驱动程序的步骤:
(1)在入口函数中先分配一个fb_info结构体。
先用static struct fb_info *s3c_lcd;来申请。申请完以后还需要内核来分配,用s3c_lcd = framebuffer_alloc(0, NULL);来分配。同样,在出口函数中用framebuffer_release(s3c_lcd);来注销。
(2)设置参数:
设置固定参数:就是设置fb_info中的 fb_var_screeninfo结构体,在这个例子中就是s3c_lcd->fix.smem_len,s3c_lcd->fix.line_length等。
设置可变参数:就是设置fb_info中的 fb_fix_screeninfo结构体,在这个例子中就是s3c_lcd->var.xres,s3c_lcd->var.bits_per_pixel等参数。
设置颜色位域:属于2.2的一部分,如下所示:
/* RGB:565, 从右边开始 */
s3c_lcd->var.red.offset = 11;
s3c_lcd->var.red.length = 5;
s3c_lcd->var.green.offset = 5;
s3c_lcd->var.green.length = 6;
s3c_lcd->var.blue.offset = 0;
s3c_lcd->var.blue.length = 5;
s3c_lcd->var.activate = FB_ACTIVATE_NOW;
2.4设置操作函数,s3c_lcd->fbops = &s3c_lcdfb_ops;
其他的设置:后面再说。
(3)硬件相关的操作:
配置GPIO用于LCD。主要是进行ioremap的操作。在这里,建立一个数组来一起操作,从而避免了一个一个设置,太麻烦。
根据 LCD 手册设置 LCD 控制器,比如 VCLK 的频率,时序等方面的操作 。
分配显存(framebuffer), 并把地址告诉 LCD 控制器 。 主要就是 dma_alloc_writecombine函数的应用,本例中我们用s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);来分配。在出口函数中用dma_free_writecombine来卸载。然后就是配置LCD的寄存器了。(lcdsaddr1,lcdsaddr2等都是2440里面的寄存器)
启动LCD,使能lcd,并且PWREN使能。
(4)注册 framebuffer。
register_framebuffer(s3c_lcd);
在出口函数中用unregister_framebuffer(s3c_lcd);来注销。
下面再来看前面2.5所说的其他方面的配置,就是fb_info结构体里面的 pseudo_palette函数(调色板)。 s3c_lcd->pseudo_palette = pseudo_palette; 而这个函数用到chan_to_field函数。这两个函数仔细看看。这一块需要仔细研究研究。
对于fb_ops函数里面fb_fillrect(),fb_copyarea(),fb_imageblit()这三个函数,直接调用通用的cfb_fillrect(),cfb_copyarea(),cfb_imageblit()函数即可。需要在编译内核的时候,make modules生成模块文件,位于内核目录下/drivers/video/下面。
下面是测试的步骤:
(1)首先,编译内核,把内核里面原来的LCD驱动去掉,“make menuconfig”:
Device Drivers --->
Graphics support --->
编译成模块。
(2)然后生成uImage,“make uImage”。
(3)这时候生成的uImage位于 arch/arm/boot/文件夹下面,把它拷到/work/nfs_root/下面。
用“cp arch/arm/boot/uImage /work/nfs_root/uImage_nolcd”
(4)编译模块 ”make modules“。主要是想生成上面提到的cfb_fillrect(),cfb_copyarea(),cfb_imageblit()这三个模块的ko文件。一会要用到。然后进入/drivers/video/下面,“cp cfb*.ko /work/nfs_root/first_fs”。
(5)使用新内核来启动。
先通过网络下载uImage_nolcd,在uboot界面,输入”nfs 30000000 192.168.1.131:/work/nfs_root/uImage_nolcd”
(6)下载完后“bootm 30000000”,就是用新内核启动。
(7)开发板启动以后,先安装3个模块。“insmod cfb_fillrect.ko ”“insmod cfb_copyarea .ko”“insmod cfb_imageblit .ko”,然后“insmod 10th_lcd.ko”,然后就可以看到屏幕亮起来了。
(8)“echo hello > /dev/tty1”,可以看到屏幕上面输出hello。
(9)cat lcd.ko > /dev/fb0,可以看到屏幕花屏。
(10)还可以修改/etc/inittab 添加一行:“tty1::askfirst:-/bin/sh”,然后开发板就可以从按键上面接收输入。这时候安装输入子系统里面的8th_drv.ko,然后就可以从开发板的按键上面输入命令。
刚开始从nfs下载的时候,提示无法下载,因为之前配置nfs服务的时候,没有添加/work/nfs_root文件夹,添加以后,重启nfs服务即可。