framebuffer驱动框架:
先看一张图:图片来源于网络
驱动框架部分:
1.drivers/video/fbmem.c。主要任务:1、创建graphics类、注册FB的字符设备驱动、提供register_framebuffer接口给具体framebuffer驱动编写着来注册fb设备的。本文件相对于fb来说,地位和作用和misc.c文件相对于杂散类设备来说一样的,结构和分析方法也是类似的。
下面三个文件都是被 fbmem.c所调用的。
2.drivers/video/fbsys.c。这个文件是处理fb在/sys目录下的一些属性文件的
3.drivers/video/modedb.c。这个是和时序相关的,以及显示模式
4.drivers/video/fb_notify.c 通知相关的
驱动部分:
(1)drivers/video/samsung/s3cfb.c,驱动主体
(2)drivers/video/samsung/s3cfb_fimd6x.c,里面有很多LCD硬件操作的函数
(2)arch/arm/mach-s5pv210/mach-x210.c,负责提供platform_device的
(3)arch/arm/plat-s5p/devs.c,为platform_device提供一些硬件描述信息的
framebuffer驱动框架分析:
1.#ifndef MODULE
define MODULE
#endif
这个意思是不是反正就是要定义MODULE
2.proc下创建fb,还提供fb_proc_fops,可以让我们去open这个设备吗?
3.register_chrdev注册fb设备,主设备号29
4.class_create创建graphics类
5.fb_fops
(1)read/write/mmap/ioctl
(2)registered_fb和num_registered_fb
(3)struct fb_info
分析:从FB_MAX宏可以分析出,fb最多支持32个次设备号,0-31,所以理论上最多有32个字符设备节点。
extern struct fb_info *registered_fb[FB_MAX]; 已经注册的fb设备都存在一个registered_fb的数组里面,大小为32。数组类型为 struct fb_info *,那么我们注册的时候,应该是要传这个结构体进来的。
驱动框架里面的read/write等函数,最终都还是要调用我们具体驱动的read/write函数的。
**struct fb_info结构体:来源于:https://www.cnblogs.com/EaIE099/p/5175979.html
1 struct fb_info {
2 atomic_t count;
3 int node; ///次设备号
4 int flags;
5 struct mutex lock; //用于open、release、ioctrl功能的锁
6 struct mutex mm_lock; / Lock for fb_mmap and smem_ fields /
7 struct fb_var_screeninfo var; //LCD 可变参数
8 struct fb_fix_screeninfo fix; //LCD 固定参数
9 struct fb_monspecs monspecs; / Current Monitor specs *///LCD 显示器标准
10 struct work_struct queue; //帧缓冲事件队列
11 struct fb_pixmap pixmap; ///图像硬件 mapper
12 struct fb_pixmap sprite; //光标硬件 mapper
13 struct fb_cmap cmap; //当前颜色表
14 struct list_head modelist; //模式列表
15 struct fb_videomode mode; //当前的显示模式
16
17 #ifdef CONFIG_FB_BACKLIGHT
18 / assigned backlight device /
19 / set before framebuffer registration,
20 remove after unregister */
21 struct backlight_device bl_dev;///对应的背光设备
22
23 / Backlight level curve */
24 struct mutex bl_curve_mutex;
25 u8 bl_curve[FB_BACKLIGHT_LEVELS];///背光调整
26 #endif
27 #ifdef CONFIG_FB_DEFERRED_IO
28 struct delayed_work deferred_work;
29 struct fb_deferred_io *fbdefio;
30 #endif
31
32 struct fb_ops *fbops;///---->对底层硬件设备操作的函数指针
33 struct device device; / This is the parent *父设备节点
34 struct device dev; / This is this fb device 当前的帧缓冲设备
35 int class_flag; / private sysfs flags */
36 #ifdef CONFIG_FB_TILEBLITTING
37 struct fb_tile_ops tileops; / Tile Blitting *///图块Blitting ?
38 #endif
39 char __iomem screen_base; / Virtual address ///虚拟地址
40 unsigned long screen_size; / Amount of ioremapped VRAM or 0 */ ///LCD IO映射的虚拟内存大小
41 void pseudo_palette; / Fake palette of 16 colors ///伪16色 颜色表
42 #define FBINFO_STATE_RUNNING 0
43 #define FBINFO_STATE_SUSPENDED 1
44 u32 state; / Hardware state i.e suspend *LCD 挂起或复位的状态
45 void fbcon_par; / fbcon use-only private area /
46 / From here on everything is device dependent */
47 void par;
48 / we need the PCI or similar aperture base/size not
49 smem_start/size as smem_start may just be an object
50 allocated inside the aperture so may not actually overlap */
51 struct apertures_struct {
52 unsigned int count;
53 struct aperture {
54 resource_size_t base;
55 resource_size_t size;
56 } ranges[0];
57 } apertures;
58
59 bool skip_vt_switch; / no VT switch on suspend/resume required */
60 };
总结一下:fb_info结构体是一个非常重要的结构体,我们使用register_framebuffer函数来注册fb设备,就需要传这个结构体进来,同时这个结构体里面还有两个很重要的成员struct fb_var_screeninfo var; //可变参数,struct fb_fix_screeninfo fix; //固定参数。
int register_framebuffer(struct fb_info *fb_info),此函数是fb驱动框架开放给驱动编写着的注册接口。
1.fb_check_foreignness:fb设备大小端的一个检查
2.remove_conflicting_framebuffers:去除有冲突的fb设备
3.device_create:在sys/class下面创建device,例如fb0,以及在/dev/目录下面去创建设备节点。
4.fb_init_device:fb设备的初始化
5.fb_init_device函数中有定义:fb在sysfs中的接口,例如在:/sys/class/graphics/fb0/下面有很多文件,例如:bits_per_pixel、virtual_size等。都是在此device_attrs结构体数组中定义的。
6.dev_set_drvdata和dev_get_drvdata:
例如:dev_set_drvdata(fb_info->dev, fb_info); 是把fb_info的指针放到struct device 的private的driver_data中。因为struct device经常用来传参,所以这样就可以找到fb_info结构体的指针的。
7.fb的mode
fb_var_to_videomode(&mode, &fb_info->var); 时序相关参数的设置,所以需要我们自己去var.mode里面自己填充。
fb_add_videomode:添加mode到fb_info->modelist链表中。
8.registered_fb[i] = fb_info; 把注册的fb_info放到registered_fb数组中。
9.fb_notifier_call_chain:通知别的模块有一个FB_EVENT_FB_REGISTERED的event发生了。
大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。
通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调用signal的思想差不多。
驱动分析:
framebuffer驱动分析
1.见drivers/video/samsung/s3cfb.c
platform_driver_register(&s3cfb_driver);使用的platform平台总线
name = “s3cfb”
2.platform_device在arch/arm/plat-s5p/devs.c中定义:
struct platform_device s3c_device_fb = {
.name = “s3cfb”,
.id = -1,
.num_resources = ARRAY_SIZE(s3cfb_resource),
.resource = s3cfb_resource,
.dev = {
.dma_mask = &fb_dma_mask,
.coherent_dma_mask = 0xffffffffUL
}
};
s3cfb_resource:这个platform_device中没有platform_data,使用的是s3cfb_resource。
static struct resource s3cfb_resource[] = {
[0] = {
.start = S5P_PA_LCD,
.end = S5P_PA_LCD + S5P_SZ_LCD - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD1,
.end = IRQ_LCD1,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_LCD0,
.end = IRQ_LCD0,
.flags = IORESOURCE_IRQ,
},
};
类型:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
3.在mach-x210.c的smdkc110_devices数组中使用,进行platform_device_register,所以当platform_driver_register注册之后,driver就找到了device,就会去执行driver的probe函数
4.s3cfb.c的probe函数分析:
下面的是struct s3c_platform_fb的定义,在mach-x210.c中:
static struct s3c_platform_fb ek070tn93_fb_data __initdata = {
.hw_ver = 0x62,
.nr_wins = 5,
.default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,
.swap = FB_SWAP_WORD | FB_SWAP_HWORD,
.lcd = &ek070tn93,
.cfg_gpio = ek070tn93_cfg_gpio,
.backlight_on = ek070tn93_backlight_on,
.backlight_onoff = ek070tn93_backlight_off,
.reset_lcd = ek070tn93_reset_lcd,
};
smdkc110_machine_init
调用:
s3cfb_set_platdata(&ek070tn93_fb_data);
设置:s3c_device_fb.dev.platform_data = npd;
这个结构体ek070tn93_fb_data 是fb的platform_data结构体,这个结构体变量就是platform设备的私有数据,这个数据在platform_device.device.platform_data中存储。在mach文件中去准备并填充这些数据,在probe函数中通过传参的platform_device指针取出来。
5.struct s3cfb_global 这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的。在probe函数中进行内存的分配。作为参数传到s3cfb_fimd6x.c中的。
6.struct resource *res;这个res将来要获取devs.c里面所定义的resource数组,如上面2所述。
总结一下:在probe函数中,有3个比较重要的结构体: struct s3c_platform_fb *pdata; :在mach-x210.c文件中定义的,也是在mach-x210.c文件中设置,设置到platform_device.dev.platform_data
struct s3cfb_global *fbdev; :probe函数中分配内存和填充
struct resource *res; 在devs.c文件中定义的,并且已经填充到platform_device中了。
s3cfb_probe函数分析:
1.给s3cfb_global结构体分配内存,赋值:
fbdev->dev = &pdev->dev; 把platform_device里面的dev赋值给它的dev
2.regulator_get、regulator_enable电源管理相关的。
3.pdata = to_fb_plat(&pdev->dev); 获取mach-x210.c中定义的data,我们在这里定义的platform_data是:s3c_platform_fb类型的。
4.fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;,取的是platform_data里面的lcd结构体:
static struct s3cfb_lcd ek070tn93 = {
.width = S5PV210_LCD_WIDTH,
.height = S5PV210_LCD_HEIGHT,
.bpp = 32,
.freq = 60,
.timing = {
.h_fp = 210,
.h_bp = 38,
.h_sw = 10,
.v_fp = 22,
.v_fpe = 1,
.v_bp = 18,
.v_bpe = 1,
.v_sw = 7,
},
.polarity = {
.rise_vclk = 0,
.inv_hsync = 1,
.inv_vsync = 1,
.inv_vden = 0,
},
};
5.配置引脚,调用platform_data里面的cfg_gpio函数ek070tn93_cfg_gpio。
看原理图:
ek070tn93_cfg_gpio
step1:把GPF0_0 到 GPF0_8、GPF1_0 到 GPF1_8、GPF2_0 到 GPF2_8、
GPF3_0 到 GPF3_3、设置为LCD相关模式,并禁止掉上下拉。
step2:虚拟地址0x00107008对应:0xE0107008,选择的是LCD类型。也就是要告诉我们LCD控制器FIMD要支持哪些LCD。写了一个2进去。代表:RGB=FIMD I80=FIMD ITU=FIMD
step3:
writel(0xffffffff, S5PV210_GPF0_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);
把所设置的引脚的驱动能力开到最大,对应4x。
6.打开时钟:
在函数:s3cfb_set_platdata中设置的clk_on函数s3cfb_clk_on。
在此函数中设置时钟为:166750000HZ。把打开了时钟。
7.platform_get_resource,的到之前定义在devs.c中的的resource资源。得到的是数组里面的第一个元素。start = 0xF8000000 end = 0xF8100000这块物理内存。
8.request_mem_region 和 ioremap得到source里面的那块物理内存的操作权限。
9.s3cfb_set_vsync_interrupt: Indicate the Video interrupt control
Specifies the Video Frame Interrupt at start of:BACK Porch(后肩)
00 = BACK Porch
10.s3cfb_set_global_interrupt
Enables Video Frame Interrupt
Enables Video Interrupt
11.做了很多关于硬件的操作:s3cfb_init_global,有很多寄存器的配置,包括输出模式、时序等相关。
ctrl->output = OUTPUT_RGB;
ctrl->rgb_mode = MODE_RGB_P;
init_completion(&ctrl->fb_complete);
mutex_init(&ctrl->lock);
s3cfb_set_output(ctrl);
s3cfb_set_display_mode(ctrl);
s3cfb_set_polarity(ctrl);
s3cfb_set_timing(ctrl);
s3cfb_set_lcd_size(ctrl);
12.分配了多个fb_info的内存,分别是fb0-fb4,然后在s3cfb_init_fbinfo这个函数中进行初始化。在s3cfb_register_framebuffer函数中进行注册。
13.s3cfb_set_clock设置像素时钟,s3cfb_set_window设置显示窗口,s3cfb_display_on打开显示。
13.申请一个irq中断,绑定中断处理任务:s3cfb_irq_frame
14.准备logo,打开背光
logo相关调用:
s3cfb_probe
fb_prepare_logo
fb_find_logo 真正查找logo文件
fb_show_logo
fb_show_logo_line 真正显示logo
fb_do_show_logo
info->fbops->fb_imageblit 实际操作硬件fb进行显示工作的函数
自定义内核启动logo:
1.sudo apt-get install netpbm
2.pngtopnm logo.png | ppmquant -fs 224 | pnmtoplainpnm > logo_linux_clut224.ppm
3.用制作好的logo_linux_clut224.ppm,替换源码目录drivers/video/logo/logo_x210_clut224.ppm,然后重新编译内核
4.启动新内核,就能看到新的启动logo了。
5.让logo显示在屏幕中央
// image.dx = 0; 显示在左上角
// image.dy = y;
image.dx = (info->var.xres - logo->width) / 2; 显示在中间
image.dy = (info->var.yres - logo->height) / 2;
image.width = logo->width;
image.height = logo->height;
有关结构体的一些分析:
http://blog.chinaunix.net/uid-28755622-id-3716102.html