5.7.framebuffer驱动详解
5.7.1.framebuffer介绍
5.7.1.1、什么是framebuffer
(1)裸机中如何操作LCD
(2)OS下操作LCD的难点
显存就是一段内存,直接映射到LCD显示器中。驱动部分负责把显存的东西丢到LCD中,让其显示,应用部分负责把显示的东西丢到显存中。
(3)framebuffer帧缓冲(简称fb)是linux内核中虚拟出的一个设备,用这个设备来虚拟出LCD的硬件基础和软件信息。
(4)framebuffer向应用层提供一个统一标准接口的显示设备。
请记住也可以多个进程同时操作LCD显示,就可以达到电视上的字符和视频同时出现的效果,这种操作可能是对不同的显示区域进行了填充。
(5)从驱动来看,fb是一个典型的字符设备,而且创建了一个类/sys/class/graphics
5.7.1.2、framebuffer的使用
(1)设备文件 /dev/fb0--------------------------第一步也就是打开设备文件
(2)获取设备信息 #include <linux/fb.h>--------------------第二步是获取设备信息
(3)mmap做映射---------------------第三步做设备的显存的LCD的映射------有了这个映射,就有这个物理地址对应的虚拟地址了。
(4)填充framebuffer------------------------ 第四步应用层去填充显存
5.7.2.framebuffer应用编程实践1
5.7.2.1、打开设备
5.7.2.2、获取设备信息
(1)不可变信息FSCREENINFO,使用ioctl的FBIOGET_FSCREENINFO名
(2)可变信息VSCREENINFO,使用ioctl的FBIOGET_VSCREENINFO名
5.7.3.framebuffer应用编程实践2
5.7.3.1、mmap做映射
做完了mmap后fb在当前进程中就已经就绪了,随时可以去读写LCD显示器了。
5.7.3.2、fb显示之刷背景
5.7.4.framebuffer应用编程实践3
5.7.4.1、设置分辨率
(1)实验失败,实验结果是只能修改虚拟分辨率,不能修改可视分辨率。原因要去驱动里找。
(2)正确的做法是在驱动中去修改参数,然后重新编译运行,才能解决。
5.7.4.2、写字、画线、图片显示等
5.7.5.framebuffer驱动框架总览
5.7.5.1、驱动框架部分
(1)drivers/video/fbmem.c。主要任务:1、创建graphics类、注册FB的字符设备驱动、提供register_framebuffer(这个函数中里面就调用了device_create创建设备文件)接口给具体framebuffer驱动编写着来注册fb设备的。本文件相对于fb来说,地位和作用和misc.c文件相对于杂散类设备来说一样的,结构和分析方法也是类似的。
(2)drivers/video/fbsys.c。这个文件是处理fb在/sys目录下的一些属性文件的。就是类似下图中的那些属性文件。
(3)drivers/video/modedb.c。这个文件是管理显示模式(譬如VGA、720P等就是显示模式)的
(4)drivers/video/fb_notify.c
5.7.5.2、驱动部分
(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提供一些硬件描述信息的
5.7.5.3、如何分析
(1)经验
(2)分析menuconfig、Makefile、Kconfig等
(3)内核编译后检查编译结果中的.o文件
5.7.6.framebuffer驱动框架分析1
5.7.6.1、fbmem_init函数
(1)#ifdef MODULE
(2)fb_proc_fops和fb在proc文件系统中的表现
fb_proc_fops–》proc_fb_open–》proc_fb_seq_ops—》fb_seq_show—即可打印出下面的信息,是此设备号加framebuffer的名称
(3)register_chrdev注册fb设备
(4)class_create创建graphics类
(5)fbmem_exit的对应
5.7.6.2、fb_fops
(1)read/write/mmap/ioctl
(2)registered_fb和num_registered_fb
struct fb_info *registered_fb[FB_MAX] __read_mostly;
int num_registered_fb __read_mostly;
上面代码中结构体就相当于一个模板,当每次创建一个framebuffer设备时,都是通过填充这个结构体而生成一个设备。这是提个结构体类型指针。47行代表已经做了的几个数组的参数。
static ssize_t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode); //次设备号
struct fb_info *info = registered_fb[fbidx]; //用次设备号做结构体数组的下标
u32 *buffer, *dst;
u32 __iomem *src;
int c, i, cnt = 0, err = 0;
unsigned long total_size;
(3)struct fb_info
5.7.7.framebuffer驱动框架分析2
5.7.7.1、register_framebuffer
(1)fb驱动框架开放给驱动编写着的注册接口
(2)fb_check_foreignness
(3)remove_conflicting_framebuffers
(4)device_create
(5)fb_init_device-------------源代码分析的时候,注意要进入到这个函数当中
fb_init_device—>dev->p->driver_data = data;这是最重要的一句话
struct device_private {
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
void *driver_data; //设备对应的驱动的数据
struct device *device;
};
5.7.7.2、fb在sysfs中的接口
(1)device_attrs
static struct device_attribute device_attrs[] = {
__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
__ATTR(name, S_IRUGO, show_name, NULL),
__ATTR(stride, S_IRUGO, show_stride, NULL),
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
#ifdef CONFIG_FB_BACKLIGHT
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
#endif
};
(2)dev_set_drvdata和 dev_get_drvdata
5.7.8.framebuffer驱动框架分析3
5.7.8.1、fb的mode
(1)什么是mode
(2)fb_var_to_videomode
(3)fb_add_videomode
5.7.8.2、注册登记该fb设备
(1)registered_fb[i] = fb_info;
**
这一句就是相当关键的就是把填充好的 fb_info丢到registered_fb[i]这个数组中去了,也就实现了真正的想内核注册。
(2)结合fb_read等函数中对fb_info的使用
(3)关键点:数据如何封装、数据由谁准备由谁消费、数据如何传递
5.7.8.3、fb_notifier_call_chain
5.7.9.framebuffer驱动分析1
5.7.9.1、s3cfb.c
(1)注意目录结构的组织
(2)s3cfb_driver
在这个文件中我们会看到下图所示的LCD的驱动也是通过platform平台总线来管理的,下码中是对应的LCD平台总线的驱动部分,那么LCD平台总线的设备,也就是mach-x210.c中的s3c_device_fb,这样也就完美的实现了设备和驱动完全分离的思想。
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,
.remove = __devexit_p(s3cfb_remove),
.driver = {
.name = S3CFB_NAME,
.owner = THIS_MODULE,
},
};
static int __init s3cfb_register(void)
{
platform_driver_register(&s3cfb_driver);
return 0;
}
static void __exit s3cfb_unregister(void)
{
platform_driver_unregister(&s3cfb_driver);
}
module_init(s3cfb_register);
module_exit(s3cfb_unregister);
MODULE_AUTHOR("Jonghun, Han <jonghun.han@samsung.com>");
MODULE_AUTHOR("Jinsung, Yang <jsgood.yang@samsung.com>");
MODULE_DESCRIPTION("Samsung Display Controller (FIMD) driver");
MODULE_LICENSE("GPL");
static struct platform_device *smdkc110_devices[] __initdata = {
#ifdef CONFIG_FIQ_DEBUGGER
&s5pv210_device_fiqdbg_uart2,
#endif
#ifdef CONFIG_MTD_ONENAND
&s5pc110_device_onenand,
#endif
#ifdef CONFIG_MTD_NAND
&s3c_device_nand,
#endif
&s5p_device_rtc,
#ifdef CONFIG_SND_S3C64XX_SOC_I2S_V4
&s5pv210_device_iis0,
&s5pv210_device_iis1,
#endif
#ifdef CONFIG_SND_S3C_SOC_AC97
&s5pv210_device_ac97,
#endif
#ifdef CONFIG_SND_S3C_SOC_PCM
&s5pv210_device_pcm0,
#endif
#ifdef CONFIG_SND_SOC_SPDIF
&s5pv210_device_spdif,
#endif
&s3c_device_wdt,
#ifdef CONFIG_FB_S3C
&s3c_device_fb,
#endif
5.7.9.2、s3c_device_fb
(1)mach-x210.c中,被使用
(2)devs.c中
(3)resource的定义和作用
这里的资源就是对应的LCD相关的寄存器,这里采用的基地址加总长的方式
5.7.9.2、s3c_device_fb
(1)mach-x210.c中,被使用
(2)devs.c中
(3)resource的定义和作用
这里的资源就是对应的LCD相关的寄存器,这里采用的基地址加总长的方式
5.7.10.framebuffer驱动分析1
5.7.10.1、probe函数分析—回到S3cfb.c驱动主体文件中
(1)struct s3c_platform_fb 这个结构体是fb的platform_data结构体,这个结构体变量就是platform设备的私有数据,这个数据在platform_device.device.platform_data中存储。在mach文件中去准备并填充这些数据,在probe函数中通过传参的platform_device指针取出来。
(2)struct s3cfb_global 这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的。
(3)struct resource
(4)regulator------------是个整流器,关于硬件的电流电压什么的
5.7.10.2、platform_data的传递过程
(1)to_fb_plat----这一句就是从platform_device中获取platform_data
(2)s3cfb_set_platdata
通过我们LED的platform_data数据的 传递原理,其实是我们应该或者说必须要知道platform_data在下码所示的结构体中,但是下图中并没有,我们在这里文件中往下翻
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
}
};
(4)smdkc110_machine_init
那么通过上图中的函数就能找到下面的定义,下图中的函数采用了宏定义的方式决定了s3cfb_set_platdata是否执行。
#ifdef CONFIG_FB_S3C_EK070TN93
static void smdkv210_backlight_off(void)
{
/* backlight enable pin low level */
s3c_gpio_cfgpin(S5PV210_GPH2(4), S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPH2(4), S3C_GPIO_PULL_UP);
gpio_set_value(S5PV210_GPH2(4), 0);
}
#endif
5.7.11.framebuffer驱动分析2-----------继续回到s3cfb.c的942行继续分析
5.7.11.1、struct s3cfb_lcd------------显示器硬件的描述,主要就是这个结构表征了很多LCD参数
#if(DISP_MODE==VGA_800X600)
static struct s3cfb_lcd ek070tn93 = {
.width = S5PV210_LCD_WIDTH,
.height = S5PV210_LCD_HEIGHT,
.bpp = 32,
.freq = 60,
.timing = {
.h_fp = 40,
.h_bp = 88,
.h_sw = 128,
.v_fp = 1,
.v_fpe = 1,
.v_bp = 23,
.v_bpe = 1,
.v_sw = 4,
},
.polarity = {
.rise_vclk = 1,
.inv_hsync = 1,
.inv_vsync = 1,
.inv_vden = 0,
},
};
通过上图中的传参可以找到图中所示的结构体去填充fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;这一句中的3cfb_lcd。
5.7.11.2、pdata->cfg_gpio
继续上码中往下看代码
//初始化LCD用到的GPIO
static void ek070tn93_cfg_gpio(struct platform_device *pdev)
{
int i;
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF0(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF0(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF1(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF1(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF2(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF2(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 4; i++) {
s3c_gpio_cfgpin(S5PV210_GPF3(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF3(i), S3C_GPIO_PULL_NONE);
}
/* mDNIe SEL: why we shall write 0x2 ? */
writel(0x2, S5P_MDNIE_SEL);
/* drive strength to max */
writel(0xffffffff, S5PV210_GPF0_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);
}
5.7.11.3、pdata->clk_on
5.7.11.4、resource的处理
(1)platform_device中提供resource结构体数组
(2)probe中platform_get_resource取出resource并且按FLAG分头处理
取出的实际上也就是下图所示的LCd相关的物理地址
#if defined(CONFIG_S5P_DEV_FB)
static struct resource s3cfb_resource[] = {
[0] = {
.start = S5P_PA_LCD, //这里 的资源就是对应的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,
},
};
然后957行代码实际上就是做的物理地址到虚拟地址的动态映射。
5.7.12.framebuffer驱动分析3
5.7.12.1、一些硬件操作
(1)s3cfb_set_vsync_interrupt----------所以关于LCD寄存器的硬件操作就在这里
进入到这个函数中可以看到很多函数传递的参数都是struct s3cfb_global这个结构体。
它主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的。
(2)s3cfb_set_global_interrupt
5.7.12.2、s3cfb_init_global
还有的相关的s3cfb_global的填充。
5.7.12.3、向框架注册该fb设备
(1)s3cfb_alloc_framebuffer
(2)s3cfb_register_framebuffer--------这里看到出我们的x210显然是个高级设备(所谓高级就是把这多个虚拟buff在一个屏幕显示,这样就可以对静止的某个buff不扫描,多动的画面进行扫描),有五个虚拟窗口的buff,这个函数中就有通过一个for循环去注册。
我们可以在驱动层对这五个fb进行操作,谁是主谁是次我们都是可以进行设置的。
5.7.13.framebuffer驱动分析4
5.7.13.1、一些硬件操作
(1)s3cfb_set_clock------------------设置像素时钟
(2)s3cfb_set_window-------------设置打开哪一个window,其实就是哪一个fb设备,去使用
(3)s3cfb_display_on
5.7.13.2、驱动中处理中断
(1)platform_get_irq-----------获取中断
(2)request_irq------------因为中断的资源也是有限的,我们也是需要想内核去申请使用中断的
5.7.13.3、logo显示(1020-1027)
5.7.13.4、backlight点亮
5.7.14.应用层为何不能设置分辨率
5.7.14.1、问题描述
(1)第4节时试图在应用层设置分辨率失败了,原因何在?
(2)定位问题:肯定是驱动的事儿
(3)进一步驱动中定位:ioctl部分的事儿
5.7.14.2、fb的ioctl部分
(1)fb是典型的字符设备驱动
fbmem_init(void)
{
proc_create("fb", 0, NULL, &fb_proc_fops); //创建设备文件fb
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n", FB_MAJOR);
fb_class = class_create(THIS_MODULE, "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–》fb_ioctl—do_fb_ioctl–》 fb_set_var(1058行)–》info->fbops->fb_check_var(s3cfb.c中s3cfb_check_var)
(2)ioctl分为2部分,在驱动框架部分和驱动部分各有一半
(3)一路追踪找问题