LCD驱动程序
LCD的一些重要函数定义
- fb_info结构体
包含了关于帧缓冲设备属性和操作的完整过程
fb_info中记录了帧缓冲设备的全部信息,包括设备的设置参数,状态以及操作函数指针。每一个帧缓冲设备都必须对应一个fb_info
struct fb_info {
int node;
int flags;
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; /* 模式列表 */
struct fb_videomode *mode; /* 目前的video模式 */
#ifdef CONFIG_FB_BACKLIGHT
/* 对应的背光设备 */
/* 在framebuffer注册之前设置,
注销后移除 */
struct backlight_device *bl_dev;
/* 背光调整 */
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; /*fb_ops帧缓冲设备操作*/
struct device *device; /* 父设备 */
struct device *dev; /* fb设备 */
int class_flag; /*私有sysfs标志 */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* 图块Blitting */
#endif
char __iomem *screen_base; /* 虚拟基地址 */
unsigned long screen_size; /* ioremapped的虚拟内存大小 */
void *pseudo_palette; /* 伪16色颜色表 */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* 硬件状态如挂起 */
void *fbcon_par; /* 仅使用fbcon区域 */
/*这里的一切都是依赖设备的 */
void *par;
};
- fb_ops结构体
fb_info的成员变量fb_ops为指向底层操作的函数的指针,这些函数是需要驱动程序人员编写的
/*
* 帧缓冲操作
*/
struct fb_ops {
/* 打开/释放 */
struct module *owner;
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
/*
* 对于非线性布局的/常规内存映射无法工作的帧缓冲设备需要
*/
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);
/*
* 检测可变参数,并调整到支持的值
*/
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
/* 根据info->var设置video模式 */
int (*fb_set_par)(struct fb_info *info);
/* 设置color寄存器*/
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
/* 批量设置color寄存器,设置颜色表 */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* 显示空白 */
int (*fb_blank)(int blank, struct fb_info *info);
/* pan显示 */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* 矩形填充 */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* 数据复制到另一个区域 */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* 图形填充 */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* 绘制光标 */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* 旋转显示 */
void (*fb_rotate)(struct fb_info *info, int angle);
/*等待blit空闲(可选) */
int (*fb_sync)(struct fb_info *info);
/*fb特定的ictol(可选) */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
/* 处理32位的compat ioctl (可选) */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
unsigned long arg);
/* fb特定的mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* 保存目前的硬件状态 */
void (*fb_save_state)(struct fb_info *info);
/* 恢复被保存的硬件状态 */
void (*fb_restore_state)(struct fb_info *info);
/* 通过fb_info获得framebuffer的能力 */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
};
- fb_ops的fb_check_var()成员函数用于检查可以修改的屏幕参数并调整到合适的值,而fb_set_par()则使得用户设置的屏幕参数在硬件上有效
- b_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) */
fb缓存的开始位置
__u32 smem_len;/* Length of frame buffer mem */
fb缓存的长度
__u32 type; /* see FB_TYPE_* */
FB_TYPE_*类型
__u32 type_aux;/* Interleave for interleaved Planes */
分界
__u32 visual; /* see FB_VISUAL_* */
屏幕使用的色彩模式
__u16 xpanstep;/* zero if no hardware panning */
如果没有硬件panning ,赋0
__u16 ypanstep;/* zero if no hardware panning */
__u16 ywrapstep;/* zero if no hardware ywrap */
1行的字节数
__u32 line_length;/* length of a line in bytes */
内存映射I/O的开始位置
unsigned long mmio_start;/* Start of Memory Mapped I/O */
/* (physical address) */
内存映射I/O的长度
__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 */
};
- fb_var_screeninfo结构体中存储了用户可以修改的显示器控制参数,例如屏幕分辨率、透明度等等。
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 */
每像素位数,BPP
__u32 grayscale;/* != 0 Graylevels instead of colors */
非0时指灰度
struct fb_bitfield red;/* bitfield in fb mem if true color, */
struct fb_bitfield green;/* else only length is significant */
struct fb_bitfield blue;
fb缓存的R\G\B位域
struct fb_bitfield transp;/* transparency */
透明度
__u32 nonstd; /* != 0 Non standard pixel format */
!= 0 非标准像素格式
__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 */
fb_info的标志
/* 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_cmap结构体中记录了颜色板信息,即调色板信息。,用户空间可以通过ioctl()的FBIOGETCMAP和 FBIOPUTCMAP命令读取或设定颜色表。
struct fb_cmap {
__u32 start; /* First entry */第一个元素的入口
__u32 len; /* Number of entries */元素个数
__u16 *red; /* Red values */红色、绿色、蓝色
__u16 *green;
__u16 *blue;
__u16 *transp;/* transparency, can be NULL */透明度
};
怎么写LCD驱动程序?
- 分配一个fb_info结构体: framebuffer_alloc
- 设置参数
- 注册: register_framebuffer
- 硬件相关的操作
static int lcd_init(void)
{
/* 1. 分配一个fb_info */
s3c_lcd = framebuffer_alloc(0, NULL);
/* 2. 设置 */
/* 2.1 设置固定的参数 */
strcpy(s3c_lcd->fix.id, "mylcd");
s3c_lcd->fix.smem_len = 240*320*16/8;
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; /* TFT */
s3c_lcd->fix.line_length = 240*2;
/* 2.2 设置可变的参数 */
s3c_lcd->var.xres = 240;
s3c_lcd->var.yres = 320;
s3c_lcd->var.xres_virtual = 240;
s3c_lcd->var.yres_virtual = 320;
s3c_lcd->var.bits_per_pixel = 16;
/* 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.3 设置操作函数 */
s3c_lcd->fbops = &s3c_lcdfb_ops;
/* 2.4 其他的设置 */
//s3c_lcd->pseudo_palette =; //
//s3c_lcd->screen_base = ; /* 显存的虚拟地址 */
s3c_lcd->screen_size = 240*324*16/8;
/* 3. 硬件相关的操作 */
/* 3.1 配置GPIO用于LCD */
gpbcon = ioremap(0x56000010, 8);
gpbdat = gpbcon+1;
gpccon = ioremap(0x56000020, 4);
gpdcon = ioremap(0x56000030, 4);
gpgcon = ioremap(0x56000060, 4);
*gpccon = 0xaaaaaaaa; /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
*gpdcon = 0xaaaaaaaa; /* GPIO管脚用于VD[23:8] */
*gpbcon &= ~(3); /* GPB0设置为输出引脚 */
*gpbcon |= 1;
*gpbdat &= ~1; /* 输出低电平 */
*gpgcon |= (3<<8); /* GPG4用作LCD_PWREN */
/* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */
lcd_regs = 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.
*/
lcd_regs->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
*/
lcd_regs->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
*/
lcd_regs->lcdcon3 = (16<<19) | (239<<8) | (10<<0);
/* 水平方向的同步信号
* bit[7:0] : HSPW, HSYNC信号的脉冲宽度, LCD手册T7=5, 所以HSPW=5-1=4
*/
lcd_regs->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
*/
lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);
/* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
/参数说明 设备类型NULL 显存空间大小 存放取值物理地址 标记
lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30); //起始地址最高两位清0
lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff; //结束地址 A21 到 1 左移一位
lcd_regs->lcdsaddr3 = (240*16/16); /* 一行的长度(单位: 2字节)半字 = 2字节 */
//s3c_lcd->fix.smem_start = xxx; /* 显存的物理地址 */
/* 启动LCD */
lcd_regs->lc dcon1 |= (1<<0); /* 使能LCD本身 */
*gpbdat |= 1; /* 输出高电平, 使能背光 */
/* 4. 注册 */
register_framebuffer(s3c_lcd);
return 0;
注册显存空间
static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)
dma_alloc_writecombine//开辟一块显存空间
为了省显存
假如一个像素我只想给8位数据 但是lcd本身是16位 如何解决
lcd控制器取到一个字节如何转换成16位数据呢 如何转换
通过设置调色板
原理就像数组一样 8位通过调色板数组去查询16位定义一个假的调色板
static u32 pseudo_palette[16];
调色板处理函数
static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
unsigned int val;
if ( > 16)
return 1;
/* 用red,green,blue三原色构造出val */
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
//((u32 *)(info->pseudo_palette))[regno] = val;
pseudo_palette[regno] = val;
return 0;
}
//红绿蓝只用到了里面16位
/* from pxafb.c */
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff; //只保留低16位
chan >>= 16 - bf->length; //假设这个长度是5的话,那么16 - 5 =11 ,那就右移11位把低11位全部清掉 自保留高5位
return chan << bf->offset; //左移后这个值就可以使用了
}
基本的安装完后 设置调色板
s3c_lcd->pseudo_palette =
测试:
1
make menuconfig去掉原来的驱动程序
-> Device Drivers
-> Graphics support //进入图形支持
S3C2410 LCD framebuffer support //配置为M 代表是模块
2
make uImage
make modules //编译模块
3
使用新的uImage启动开发板:
4
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko
echo hello > /dev/tty1 // 可以在LCD上看见hello
cat lcd.ko > /dev/fb0 // 花屏
5
修改 /etc/inittab
tty1::askfirst:-/bin/sh
用新内核重启开发板
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko
insmod buttons.ko