LCD的framebuffer驱动详解

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)一路追踪找问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值