Linux 本身实现了FrameBuffer驱动(字符驱动)便于应用用于层调用,实现的文件是/drivers/video/fbmem.c文件:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
};
上层应用程序通过这些函数实现对lcd各种参数的控制,修改等,这与以前编写字符设备驱动的模式一样,这些函数都是linux内核已经实现好的,不需要驱动工程师去编写.那驱动工程师的任务是什么呢?获取平台设备的相关信息,并初始化相关的硬件设备,注册中断,开辟帧缓冲内存,注册帧缓冲设备,这就是/drivers/video/s3c2440fb.c中的主要内容了.里面有一个重要的数据结构:
static struct fb_ops s3c2410fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c2410fb_check_var,
.fb_set_par = s3c2410fb_set_par,
.fb_blank = s3c2410fb_blank,
.fb_setcolreg = s3c2410fb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
这里的函数是真正对底层硬件进行操作的函数了,都是需要驱动工程师根据具体的硬件平台去填写.那么这些底层函数与上层应用程序的接口是怎样对接起来的呢?其实,是通过表征帧缓存设备的数据结构struct fb_info来实现的,这个数据结构几乎包括了lcd控制器的所有信息:
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;
struct fb_pixmap sprite;
struct fb_cmap cmap;
struct list_head modelist;
struct fb_videomode *mode;
#ifdef CONFIG_FB_BACKLIGHT
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; // = &s3c2410fb_ops
struct device *device;
struct device *dev;
int class_flag;
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops;
#endif
char __iomem *screen_base;
unsigned long screen_size;
void *pseudo_palette;
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state;
void *fbcon_par;
void *par; // ---> struct s3c2410fb_info
};
其中有几个重要的成员.struct fb_var_screeninfo var表示的是lcd屏的可变参数,而struct fb_fix_screeninfo fix表示的是lcd屏的不变参数.struct fb_ops *fbops当然就是上面那个重要结构的指针了.char __iomem *screen_base是申请的帧缓存的首地址(虚拟地址),unsigned long screen_size是缓存的大小.还要关注一下void *par这个指针,在初始化中,它用来指向自定义的一个数据结构struct s3c2410fb_info,在以下的分析中会见到.
struct fb_info这个结构是怎样将上层的请求传递到下层的呢?其实就是static const struct file_operations fb_fops结构中的成员函数根据需要调用了struct fb_info结构中struct fb_ops *fbops成员(不要将两者混淆了)中的相关函数,从而实现对lcd的参数读取、修改等,而struct fb_info中包含了lcd屏的相关参数信息.struct fb_ops *fbops中的成员函数只是在系统初始化模块时注册到内核中去的,只是存在于内核当中,等待被上层调用而已. 整个的架构大致就是这样.用图表示出来就是:
/drivers/video/s3c2440fb.c 的硬件驱动为平台驱动,需要获取设备的信息(内存、中断、资源等),平台设备的注册在arch/arm/mach-s3c2440/mach-smdk2440.c中被定义:
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,
.width = 320,
.height = 240,
.pixclock = 156250,
.xres = 320,
.yres = 240,
.bpp = 16,
.left_margin = 20,//or8,
.right_margin = 38,//or5,
.hsync_len = 30,//or63,
.upper_margin = 12,//or15,
.lower_margin = 15,//or3,
.vsync_len =3,//or5,
};
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
.displays = &smdk2440_lcd_cfg,
.num_displays = 1,//或者ARRAY_SIZE(smdk2440_lcd_cfg),
.default_display = 0,
#if 0
/* currently setup by downloader */
.gpccon = 0xaa940659,
.gpccon_mask = 0xffffffff,
.gpcup = 0x0000ffff,
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaa84aaa0,
.gpdcon_mask = 0xffffffff,
.gpdup = 0x0000faff,
.gpdup_mask = 0xffffffff,
#endif .lpcsel =0,//((0xCE6) & ~7) | 1<<4,
};
LCD 的参数设定是需要根据LCD 的手册来设定arch/arm/mach-s3c2440/mach-smdk2440.c 里面
的s3c2410fb_display smdk2440_lcd_cfg 结构体,FL2440中详细的设置见: