Chinaunix首页 | 论坛 | 博客
  • 博客访问: 292494
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

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_screeninfostruct 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)设置参数:

    1. 设置固定参数:就是设置fb_info中的 fb_var_screeninfo结构体,在这个例子中就是s3c_lcd->fix.smem_lens3c_lcd->fix.line_length等。

    2. 设置可变参数:就是设置fb_info中的 fb_fix_screeninfo结构体,在这个例子中就是s3c_lcd->var.xress3c_lcd->var.bits_per_pixel等参数。

    3. 设置颜色位域:属于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;

    1. 其他的设置:后面再说。

3)硬件相关的操作:

    1. 配置GPIO用于LCD。主要是进行ioremap的操作。在这里,建立一个数组来一起操作,从而避免了一个一个设置,太麻烦。

    2. 根据 LCD 手册设置 LCD 控制器,比如 VCLK 的频率,时序等方面的操作 。

    3. 分配显存(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里面的寄存器)

    4. 启动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 --->

S3C2410 LCD framebuffer 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

9cat lcd.ko > /dev/fb0,可以看到屏幕花屏。

10)还可以修改/etc/inittab 添加一行:“tty1::askfirst:-/bin/sh”,然后开发板就可以从按键上面接收输入。这时候安装输入子系统里面的8th_drv.ko,然后就可以从开发板的按键上面输入命令。


刚开始从nfs下载的时候,提示无法下载,因为之前配置nfs服务的时候,没有添加/work/nfs_root文件夹,添加以后,重启nfs服务即可。

阅读(1015) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~