static int __init
fbmem_init(void)
{
proc_create("fb", 0, NULL, &fb_proc_fops);
if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) //注册了名字为fb的字符设备,相关操作为fb_fops
printk("unable to get major %d for fb devs\n", FB_MAJOR);
fb_class = class_create(THIS_MODULE, "graphics");//创建了一个名字为graphics的类
if (IS_ERR(fb_class)) {
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
fb_class = NULL;
}
return 0;
}
fb_fops为上层app提供了open,read,write,mmap,ioctrl的相关操作:
open操作:
app: open("/dev/fb0", ...) 主设备号: 29, 次设备号: 0
--------------------------------------------------------------
kernel:
fb_open
int fbidx = iminor(inode);
struct fb_info *info = = registered_fb[0]; // registered_fb很重要,从registered_fb数组中获取到fb_info,并且如果定义了info->fbops->fb_open,就会调用底层的fb_open
read操作:
app: read()
---------------------------------------------------------------
kernel:
fb_read
int fbidx = iminor(inode); //获取次设备号
struct fb_info *info = registered_fb[fbidx]; //和open一样,从registered_fb数组中获取fb_info
if (info->fbops->fb_read) //假如底层定义了info->fbops->fb_read,就使用底层的fb_read
return info->fbops->fb_read(info, buf, count, ppos);
//否则就直接copy_to_user
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
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
int
register_framebuffer(struct fb_info *fb_info)
{
。。。
fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), NULL, "fb%d", i); //生成/dev/下的fb节点
。。。
registered_fb[i] = fb_info; //将传进来的fb_info放入到registered_fb数组中
。。。
}
再查找register_framebuffer再哪被调用,发现6410的LCD驱动代码(硬件相关操作)路径为:
S3cfb.c (drivers\video),可以参考此代码编写驱动程序。
总结一下如何编写一个LCD驱动程序:
1. 分配一个fb_info结构体: framebuffer_alloc
2. 设置,fb_info相关
3. 注册: register_framebuffer
4. 硬件相关的操作:硬件设备寄存器相关
//以下为转载
应用程序要操作LCD,就操作设备节点(/dev/fb0由帧缓冲创建),fbmem.c是提供应用程序的操作接口,fbmem.c本身并不实现这些功能,这就需要下一层接口实现,就是XXXfb.c要实现的与lcd底层硬件相关的操作接口。具体可以参考s3c2410fb.c。
帧缓冲技术是与LCD驱动混在一起从而形成帧缓冲LCD设备驱动程序,主要是由下面几个重要数据结构起关联。
1、Struct fb_info
Struct fb_info这结构记录了帧缓冲设备的全部信息,包括设备的设置参数、状态、以及对底层硬件操作的函数指针。下面具体分析一下。
[cpp] view plaincopy
struct fb_info {
int node;
int flags;
struct fb_var_screeninfo var; /*LCD可变参数结构体 */
struct fb_fix_screeninfo fix; /* LCD固定参数结构体 */
struct fb_monspecs monspecs; /* LCD显示器标准 */
struct work_struct queue; /* 帧缓冲事件队列 */
struct fb_pixmap pixmap; /*图像硬件 mapper */
struct fb_pixmap sprite; /* 光标硬件mapper */
struct fb_cmap cmap; /*当前颜色表 */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* 当前的显示模式 */
ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev;/*对应背光设备*/
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/
endif
。。。。。。。。
struct fb_ops *fbops;/*对底层硬件操作的函数指针*/
……
struct device *dev; /* FB设备 */
……
ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /*图块 Blitting */
endif
char __iomem *screen_base; /* 虚拟基地址 */
unsigned long screen_size; /* LCD IO 映射的虚拟内存大小*/
void *pseudo_palette; /*伪16色颜色表 */
define FBINFO_STATE_RUNNING 0
define FBINFO_STATE_SUSPENDED 1
u32 state; /* LCD挂起或者恢复的状态*/
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;
下面各自分析一下
2、struct fb_var_screeninfo
struct fb_var_screeninfo 主要是记录用户可以修改的控制器可变参数
[cpp] view plaincopy
struct fb_var_screeninfo {
__u32 xres; /* 可见屏幕一行有多少个像素点*/
__u32 yres; /*可见屏幕一列有多少个像素点*/
__u32 xres_virtual; /*虚拟屏幕一列有多少个像素点*/
__u32 yres_virtual; /*虚拟屏幕一列有多少个像素点*/
__u32 xoffset; /* 虚拟到可见屏幕之间的行偏移*/
__u32 yoffset; /*虚拟到可见屏幕之间的列偏移 */
__u32 bits_per_pixel; /* 每个像素由多少位组成即BPP*/
__u32 grayscale; /* != 0 Graylevels instead of colors */
/fb缓存的RGB位域/
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; /*高度mm*/
__u32 width; /* 宽度 mm*/
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/*时间选择:除了像素时钟外,所有的值都以像素时钟为单位*/
/* Timing: All values in pixclocks(像素时钟), except pixclock (of course) */
__u32 pixclock; /* 像素时钟(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 */
};
3、struct fb_fix_screeninfo
而struct fb_fix_screeninfo fix;就是固定的控制器配置,比如屏幕缓冲区的物理地址和长度,定义如下:
[cpp] view plaincopy
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; /* 没硬件panning就置0zero if no hardware panning */
__u16 ypanstep; /*没硬件panning就置0 zero if no hardware panning */
__u16 ywrapstep; /*没硬件ywrap就置0 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 */
};
4、struct fb_ops
struct fb_ops,帧缓冲操作,关联硬件跟应用程序。
[cpp] view plaincopy
/*
Frame buffer operations
LOCKING NOTE: those functions must ALL be called with the console
semaphore held, this is the only suitable locking mechanism we have
in 2.6. Some may be called at interrupt time at this point though.
*/
struct fb_ops {
/* open/release and usage marking */
struct module *owner;
。。。。。。
/检查可变参数并进行设置/
/* 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);
//根据设置的值进行更新,根据info->var
/* 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);
};
5、帧缓冲设备以及设备资源
LCD帧缓冲设备在Linux里是作为一个平台设备,在内核arch/arm/plat-s3c24xx/devs.c中定义LCD相关平台设备如下:
[cpp] view plaincopy
/* LCD Controller */
static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C24XX_PA_LCD,//寄存器的开始地址
.end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,//长度
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}
};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),资源数量
.resource = s3c_lcd_resource,
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
EXPORT_SYMBOL(s3c_device_lcd);
这里导出s3c_device_lcd是为了在arch/asm/mach-s3c2410/mach-smdk2410.c里的smdk2410_devices[](或者其它smdk2440_devices[])中添加到平台设备列表中。
下面准备分析具体实例,但分析前还要了解LCD的特性以及读写时序。
实在不如别人做得漂亮,做得详细,大家还是去看原文吧,我这里就不接了。
二、LCD的硬件知识
1. LCD工作的硬件需求:
要使一块 LCD正常的显示文字或图像,不仅需要 LCD驱动器,而且还需要
相应的 LCD控制器。在通常情况下,生产厂商把LCD驱动器会以 COF/COG
的形式与 LCD玻璃基板制作在一起,而 LCD控制器则是由外部的电路来实现,
现在很多的 MCU内部都集成了 LCD控制器, 如 S3C2410/2440 等。 通过LCD
控制器就可以产生LCD驱动器所需要的控制信号来控制STN/TFT 屏了。
- S3C2440内部 LCD 控制器结构图:
我们根据数据手册来描述一下这个集成在 S3C2440 内部的 LCD控制器:
a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组
成;
b:REGBANK由 17个可编程的寄存器组和一块 256*16 的调色板内存组成,
它们用来配置 LCD控制器的;
c:LCDCDMA是一个专用的 DMA,它能自动地把在侦内存中的视频数据传送
到 LCD驱动器,通过使用这个 DMA通道,视频数据在不需要 CPU的干预的情
况下显示在 LCD屏上;
d:VIDPRCS接收来自 LCDCDMA的数据,将数据转换为合适的数据格式,比
如说 4/8 位单扫,4 位双扫显示模式,然后通过数据端口 VD[23:0]传送视频数
据到 LCD驱动器;
……
在这里我加上《S3c2410 LCD驱动学习心得》因为这里面分析如何确定驱动里的lcd配置参数写得很明白
S3C2410实验箱上的LCD是一款3.5寸TFT真彩LCD屏,分辩率为240*320,下图为该屏的时序要求。
图1.3
通过对比图1.2和图1.3,我们不难看出:
VSPW+1=2 -> VSPW=1
VBPD+1=2 -> VBPD=1
LINVAL+1=320-> LINVAL=319
VFPD+1=3 -> VFPD=2
HSPW+1=4 -> HSPW=3
HBPD+1=7 -> HBPW=6
HOZVAL+1=240-> HOZVAL=239
HFPD+1=31 -> HFPD=30
以上各参数,除了LINVAL和HOZVAL直接和屏的分辩率有关,其它的参数在实际操作过程中应以上面的为参考,不应偏差太多。
1.3 LCD控制器主要寄存器功能详解
图1.4
LINECNT :当前行扫描计数器值,标明当前扫描到了多少行。
CLKVAL :决定VCLK的分频比。LCD控制器输出的VCLK是直接由系统总线(AHB)的工作频率HCLK直接分频得到的。做为240*320的TFT屏,应保证得出的VCLK在5~10MHz之间。
MMODE :VM信号的触发模式(仅对STN屏有效,对TFT屏无意义)。
PNRMODE :选择当前的显示模式,对于TFT屏而言,应选择[11],即TFT LCD panel。
BPPMODE :选择色彩模式,对于真彩显示而言,选择16bpp(64K色)即可满足要求。
ENVID :使能LCD信号输出。
图1.5
VBPD , LINEVAL , VFPD , VSPW 的各项含义已经在前面的时序图中得到体现。
图1.6
HBPD , HOZVAL , HFPD 的各项含义已经在前面的时序图中得到体现。
图1.7
HSPW 的含义已经在前面的时序图中得到体现。
MVAL 只对 STN屏有效,对TFT屏无意义。
HSPW 的含义已经在前面的时序图中得到体现,这里不再赘述。
MVAL 只对 STN屏有效,对TFT屏无意义。
图1.8
VSTATUS :当前VSYNC信号扫描状态,指明当前VSYNC同步信号处于何种扫描阶段。
HSTATUS :当前HSYNC信号扫描状态,指明当前HSYNC同步信号处于何种扫描阶段。
BPP24BL :设定24bpp显示模式时,视频资料在显示缓冲区中的排列顺序(即低位有效还是高位有效)。对于16bpp的64K色显示模式,该设置位无意义。
FRM565 :对于16bpp显示模式,有2中形式,一种是RGB=5:5:5:1,另一种是5:6:5。后一种模式最为常用,它的含义是表示64K种色彩的16bit RGB资料中,红色(R)占了5bit,绿色(G)占了6bit,蓝色(B)占了5bit
INVVCLK , INVLINE , INVFRAME , INVVD :通过前面的时序图,我们知道,CPU的LCD控制器输出的时序默认是正脉冲,而LCD需要VSYNC(VFRAME)、VLINE(HSYNC)均为负脉冲,因此 INVLINE 和 INVFRAME 必须设为“1 ”,即选择反相输出。
INVVDEN , INVPWREN , INVLEND 的功能同前面的类似。
PWREN 为LCD电源使能控制。在CPU LCD控制器的输出信号中,有一个电源使能管脚LCD_PWREN,用来做为LCD屏电源的开关信号。
ENLEND 对普通的TFT屏无效,可以不考虑。
BSWP 和 HWSWP 为字节(Byte)或半字(Half-Word)交换使能。由于不同的GUI对FrameBuffer(显示缓冲区)的管理不同,必要时需要通过调整 BSWP 和 HWSWP 来适应GUI。
分析之后,我们能否把这LCD弄成模块?如果可以,又怎么弄?(这跟平台设备驱动有很大关系)
1、修改
linux-2.6.24/arch/arm/mach-s3c2410/mach-smdk2410.c
先加头文件