7.framebuffer驱动详解

1.framebuffer介绍

1.1、什么是framebuffer
(1)裸机中如何操作LCD
(2)OS下操作LCD的难点
(3)framebuffer帧缓冲(简称fb)是linux内核中虚拟出的一个设备
(4)framebuffer向应用层提供一个统一标准接口的显示设备
(5)从驱动来看,fb是一个典型的字符设备,而且创建了一个类/sys/class/graphics
1.2、framebuffer的使用
(1)设备文件 /dev/fb0
(2)获取设备信息 #include <linux/fb.h>
(3)mmap做映射
(4)填充framebuffer

FrameBuffer的显示缓冲区位于Linux的内核态地址空间中。

而在Linux中,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理,缓冲区地址的。

为此, Linux在文件操作file operations结构中提供了mmap()函数,可将文件的内容映射到用户空间。

对于帧缓冲设备,则可通过映射操作,将屏幕缓冲区(FrameBuffer)的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图。

FrameBuffer与应用程序的交互如图15.1所示。


2.framebuffer应用编程实践1

2.1、打开设备
2.2、获取设备信息
(1)不可变信息FSCREENINFO,使用ioctl的FBIOGET_FSCREENINFO名
(2)可变信息VSCREENINFO,使用ioctl的FBIOGET_VSCREENINFO名


3.framebuffer应用编程实践2

3.1、mmap做映射
做完了mmap后fb在当前进程中就已经就绪了,随时可以去读写LCD显示器了。
3.2、fb显示之刷背景


4.framebuffer应用编程实践3

4.1、设置分辨率
(1)实验失败,实验结果是只能修改虚拟分辨率,不能修改可视分辨率。原因要去驱动里找。
(2)正确的做法是在驱动中去修改参数,然后重新编译运行,才能解决。
4.2、写字、画线、图片显示等


5.framebuffer驱动框架总览

5.1、驱动框架部分
(1)drivers/video/fbmem.c。主要任务:1、创建graphics类、注册FB的字符设备驱动、提供register_framebuffer接口给具体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.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.3、如何分析
(1)经验
(2)分析menuconfig、Makefile、Kconfig等
(3)内核编译后检查编译结果中的.o文件


6.framebuffer驱动框架分析1

重要结构体:----------------------------------

1. fb_fops

用来实现帧缓冲设备的操作


 

 

 

2. fb_info:

包含了驱动实现的底层函数和设备状态的数据

 

 

3. fb_cmap

结构体记录了一个颜色板信息,也可以叫调色板信息。

用户空间程序可以使明ioctl()函数的FBIOGETCMAP和FBIOPUTCMAP命令读取和设置颜色表的值。struct fb cmap结构体的定义如下:

struct fb_cmap {
	__u32 start;			/* 颜色板的第一个元素的入口地址	*/
	__u32 len;			/* 元素的个数 */
	__u16 *red;			/* 表示红色分量的值	*/
	__u16 *green;
	__u16 *blue;
	__u16 *transp;			/* 透明度分量的值, can be NULL */
};

 

 

4. fb_var_screeninfo

fb_var_screeninfo结构体中存储了用户可以修改的显示控制器参数,例如屏幕分辨率、每个像素的比特数、透明度等。

  1. 该结构体中有几个重要成员需要注意。
  2. xres表示屏幕一行有多少个像素点。
  3. yres表示屏幕一列有多少个像素点。
  4. bits_per_pixel表示每个像素点占用多少个字节。

fb_var_screeninfo结构体的定义如下:

 

5.fb_fix_screeninfo

fb_fix_screeninfo结构体中,记录了用户不能修改的固定显示控制器参数。这些固定参数如缓冲区的物理地址、缓冲区的长度、显示色彩模式、内存映射的开始位置等。

  1. 需要注意的是,
  2. 第07行的visual表示屏幕使用的色彩模式,在Linux下,支持多种色彩模式。
  3. 第11行,表示显存中一行占用的内存字节数,具体占用多少字节由显示模式来决定。
  4. 第15行,保留了6个字节为以后扩充使用。

这个结构体的成员都需要在驱动程序初始化时设置,该结构体的定义代码如下:

 

 

 

参考:

https://blog.csdn.net/jmq_0000/article/details/7104824

 

 

 

 

 

 

 

 

 


7.framebuffer驱动框架分析2

7.1、register_framebuffer
(1)fb驱动框架开放给驱动编写着的注册接口
(2)fb_check_foreignness
(3)remove_conflicting_framebuffers
(4)device_create
(5)fb_init_device
7.2、fb在sysfs中的接口
(1)device_attrs
(2)dev_set_drvdata和dev_get_drvdata


8.framebuffer驱动框架分析3

8.1、fb的mode
(1)什么是mode
(2)fb_var_to_videomode
(3)fb_add_videomode
8.2、注册登记该fb设备
(1)registered_fb[i] = fb_info;
(2)结合fb_read等函数中对fb_info的使用
(3)关键点:数据如何封装、数据由谁准备由谁消费、数据如何传递
8.3、fb_notifier_call_chain


9.framebuffer驱动分析1

9.1、s3cfb.c
(1)注意目录结构的组织
(2)s3cfb_driver
9.2、s3c_device_fb
(1)mach-x210.c中,被使用
(2)devs.c中
(3)resource的定义和作用


10.framebuffer驱动分析1

10.1、probe函数分析

(1)struct s3c_platform_fb    这个结构体是fb的platform_data结构体,这个结构体变量就是platform设备的私有数据,这个数据在platform_device.device.platform_data中存储。在mach文件中去准备并填充这些数据,在probe函数中通过传参的platform_device指针取出来。

struct s3c_platform_fb {
	int		hw_ver;
	char		clk_name[16];
	int		nr_wins;
	int		nr_buffers[5];
	int		default_win;
	int		swap;
	phys_addr_t	pmem_start; /* starting physical address of memory region */
	size_t		pmem_size; /* size of memory region */
	void            *lcd;
	void		(*cfg_gpio)(struct platform_device *dev);
	int		(*backlight_on)(struct platform_device *dev);
	int		(*backlight_onoff)(struct platform_device *dev, int onoff);
	int		(*reset_lcd)(struct platform_device *dev);
	int		(*clk_on)(struct platform_device *pdev, struct clk **s3cfb_clk);
	int		(*clk_off)(struct platform_device *pdev, struct clk **clk);
};

 

 

(2)struct s3cfb_global        这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的。

struct s3cfb_global {
	/* general */
	void __iomem		*regs;
	struct mutex		lock;
	struct device		*dev;
	struct clk		*clock;
	struct regulator	*regulator;
	int			irq;
	struct fb_info		**fb;
	struct completion	fb_complete;

	/* fimd */
	int			enabled;
	int			dsi;
	int			interlace;
	enum s3cfb_output_t	output;
	enum s3cfb_rgb_mode_t	rgb_mode;
	struct s3cfb_lcd	*lcd;

#ifdef CONFIG_HAS_WAKELOCK
	struct early_suspend	early_suspend;
	struct wake_lock	idle_lock;
#endif

#ifdef CONFIG_CPU_FREQ
	struct notifier_block	freq_transition;
	struct notifier_block	freq_policy;
#endif

};

(3)struct resource

struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};


(4)regulator  电源监控


10.2、重要的platform_data

platform_data的传递过程
(1)to_fb_plat

static int __devinit s3cfb_probe(struct platform_device *pdev)
{
	struct s3c_platform_fb *pdata;
	struct s3cfb_global *fbdev;
	struct resource *res;
	int i, j, ret = 0;


	pdata = to_fb_plat(&pdev->dev);//取出挂接再device上的私有变量void		*platform_data;
	if (!pdata) {
		dev_err(fbdev->dev, "failed to get platform data\n");
		ret = -EINVAL;
		goto err_pdata;
	}

}


(2)s3cfb_set_platdata     \arch\arm\plat-s5p\devs.c

void __init s3cfb_set_platdata(struct s3c_platform_fb *pd)
{
	struct s3c_platform_fb *npd;
	int i;

	if (!pd)
		pd = &default_fb_data;

	npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);
	if (!npd)
		printk(KERN_ERR "%s: no memory for platform data\n", __func__);
	else {
		for (i = 0; i < npd->nr_wins; i++)
			npd->nr_buffers[i] = 1;

		npd->nr_buffers[npd->default_win] = CONFIG_FB_S3C_NR_BUFFERS;

		s3cfb_get_clk_name(npd->clk_name);
		npd->clk_on = s3cfb_clk_on;
		npd->clk_off = s3cfb_clk_off;

		/* starting physical address of memory region */
		npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, 1);
		/* size of memory region */
		npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMD, 1);

		s3c_device_fb.dev.platform_data = npd;  //设置了platform_data
	}
}


(3)smdkc110_machine_init      \arch\arm\mach-s5pv210\mach-x210.c

static void __init smdkc110_machine_init(void)
{
/*************  部分省略  **************/
#ifdef CONFIG_FB_S3C_LTE480WV
	s3cfb_set_platdata(&lte480wv_fb_data);
#endif  

#ifdef CONFIG_FB_S3C_EK070TN93  //我们使用的是这款lcd
	smdkv210_backlight_off();
	s3cfb_set_platdata(&ek070tn93_fb_data);//调用函数
#endif

}

(4)真正的Platform_data    \arch\arm\mach-s5pv210\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,
};
#endif


11.framebuffer驱动分析2

11.1、struct s3cfb_lcd

fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;



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,
};
#endif



static struct s3cfb_lcd ek070tn93 = {
	.width = S5PV210_LCD_WIDTH,  //1024
	.height = S5PV210_LCD_HEIGHT, //600
	.bpp = 32,
	.freq = 60,

	.timing = {
		.h_fp	= 160,
		.h_bp	= 140,
		.h_sw	= 20,
		.v_fp	= 12,
		.v_fpe	= 1,
		.v_bp	= 20,
		.v_bpe	= 1,
		.v_sw	= 3,
	},
	.polarity = {
		.rise_vclk = 0,
		.inv_hsync = 1,
		.inv_vsync = 1,
		.inv_vden = 0,
	},
};

 

11.2、pdata->cfg_gpio

if (pdata->cfg_gpio)
	pdata->cfg_gpio(pdev);

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));  //设置CON为0x2
		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);
}


11.3、pdata->clk_on

    if (pdata->clk_on)  //为空
        pdata->clk_on(pdev, &fbdev->clock);

11.4、resource的处理


(1)platform_device中提供resource结构体数组
(2)probe中platform_get_resource取出resource并且按FLAG分头处理

取出资源:

	fbdev->regulator = regulator_get(&pdev->dev, "pd");
	if (!fbdev->regulator) {
		dev_err(fbdev->dev, "failed to get regulator\n");
		ret = -EINVAL;
		goto err_regulator;
	}


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
	}
};

static struct resource s3cfb_resource[] = {
	[0] = {
		.start = S5P_PA_LCD,  //0xf800 0000
		.end   = S5P_PA_LCD + S5P_SZ_LCD - 1,  //0xf800 0000 - SZ_1M -1
		.flags = IORESOURCE_MEM,  //0000 0020
	},
	[1] = {
		.start = IRQ_LCD1,
		.end   = IRQ_LCD1,
		.flags = IORESOURCE_IRQ,
	},
	[2] = {
		.start = IRQ_LCD0,
		.end   = IRQ_LCD0,
		.flags = IORESOURCE_IRQ,
	},
};

使用资源:动态分配虚拟内存

res = request_mem_region(res->start,
		res->end - res->start + 1, pdev->name); //注册地址
if (!res) {
	dev_err(fbdev->dev, "failed to request io memory region\n");
	ret = -EINVAL;
	goto err_io;
}

fbdev->regs = ioremap(res->start, res->end - res->start + 1);  //得到虚拟地址
if (!fbdev->regs) {
	dev_err(fbdev->dev, "failed to remap io region\n");
	ret = -EINVAL;
	goto err_mem;
}


12.framebuffer驱动分析3

12.1、一些硬件操作
(1)s3cfb_set_vsync_interrupt
(2)s3cfb_set_global_interrupt
12.2、s3cfb_init_global
12.3、向框架注册该fb设备
(1)s3cfb_alloc_framebuffer
(2)s3cfb_register_framebuffer


13.framebuffer驱动分析4

13.1、一些硬件操作
(1)s3cfb_set_clock
(2)s3cfb_set_window
(3)s3cfb_display_on
13.2、驱动中处理中断
(1)platform_get_irq
(2)request_irq
13.3、logo显示
13.4、backlight点亮


14.应用层为何不能设置分辨率

14.1、问题描述
(1)第4节时试图在应用层设置分辨率失败了,原因何在?
(2)定位问题:肯定是驱动的事儿
(3)进一步驱动中定位:ioctl部分的事儿
14.2、fb的ioctl部分
(1)fb是典型的字符设备驱动
(2)ioctl分为2部分,在驱动框架部分和驱动部分各有一半
(3)一路追踪找问题


15.折腾内核的启动logo

15.1、让logo显示在屏幕中央
15.2、自定义内核启动logo

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值