IMX6ULL LCD驱动(一)

本小节来分析Linux内核中的LCD框架,只分析基于RGB接口的LCD框架,如果是涉及GPU的那就比较复杂了,

有过应用层对LCD操作的应该知道,上层应用是通过一些Ioctl函数操作/dev/fbxxx,这些函数对内核同统一出来的一套LCD的操作函数,还有,对于不同的单板,其LCD控制器的操作肯定是不同的

所以,从这两方面来看,内核LCD框架肯定有两部分工作要完成

  1. 初始化具体单板的LCD控制器
  2. 注册统一的操作函数为上层应用使用

驱动大多都是这样,应用不关心下面具体的单板,具体的单板只需要实现自己的操作函数,然后注册给内核,通过应用来调用,都是这个套路

在开始之前先来提一个概念,一个RGB接口的LCD对应到/dev/下的一个fb,这个fb就包含了这个屏幕所有的信息,一个fb的数据是由一个fb_info结构体来描述,所以,一个LCD就是一个fb_info结构体,

struct fb_info {
	atomic_t count;
	int node;
	int flags;
	struct mutex lock;		/* Lock for open/release/ioctl funcs */
	struct mutex mm_lock;		/* Lock for fb_mmap and smem_* fields */
	struct fb_var_screeninfo var;	/* Current var */
	struct fb_fix_screeninfo fix;	/* Current fix */
	struct fb_monspecs monspecs;	/* Current Monitor specs */
	struct work_struct queue;	/* Framebuffer event queue */
	struct fb_pixmap pixmap;	/* Image hardware mapper */
	struct fb_pixmap sprite;	/* Cursor hardware mapper */
	struct fb_cmap cmap;		/* Current cmap */
	struct list_head modelist;      /* mode list */
	struct fb_videomode *mode;	/* current mode */

    ......
	struct fb_ops *fbops;
}

fb_info结构体中有一些固定的参数,一些可变的参数,可变参数就包括LCD的那些时间参数,最重要的就是fb_ops操作函数,就是为应用提供的操作函数.

现在开始分析驱动,不从所谓的核心层开始分析,直接从具体的设备分析,平台为飞思卡尔的RGB LCD驱动,  drivers\video\fbdev\mxsfb.c

注册平台设备驱动,如果要添加LCD的话需要注意匹配列表,

IMX6ULL LCD驱动(一)_第1张图片

分析probe函数

static int mxsfb_probe(struct platform_device *pdev)
{
	struct resource *res;
	struct mxsfb_info *host;
	struct fb_info *fb_info;
	struct pinctrl *pinctrl;

	gpio = of_get_named_gpio(pdev->dev.of_node, "enable-gpio", 0);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

	host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);


	fb_info = framebuffer_alloc(0, &pdev->dev);

	host->fb_info = fb_info;
	fb_info->par = host;

	ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0, dev_name(&pdev->dev), host);


	host->base = devm_ioremap_resource(&pdev->dev, res);

	platform_set_drvdata(pdev, host);


	INIT_LIST_HEAD(&fb_info->modelist);

	ret = mxsfb_init_fbinfo(host);
	
	ret = mxsfb_dispdrv_init(pdev, fb_info);

	ret = register_framebuffer(fb_info);

	mxsfb_overlay_init(host);

	return 0;
}

probe函数很长,我尽量留下了一些必要的信息,

  1. 函数一开始,定义了一个struct mxsfb_info结构体类型的变量,这个结构体是厂商自己定义的,点进去就会发现,其内部包含了一个struct fb_info,重点就是对它进行设置和注册,其余的一些是辅助变量,根据具体情况而定
  2. 申请"enable-gpio",有的屏幕需要由一个IO来控制整个屏幕的上电和下电,视具体情况而定
  3. 通过platform_get_resource函数获取平台驱动的IORESOURCE_MEM类型的变量,在设备树中就是reg的值,reg就是某一款单板的LCD控制器的基地址,这里就是去获取基地址的值
  4. 分配一些变量的空间,host和fb_info,然后两者关联在一起
  5. 中断相关操作
  6. 很重要,映射LCD控制寄存器的基地址,放到host->base中,有了基地址,根据偏移值就可以访问到LCD控制器的寄存器地址
  7. 一些时钟的设置,这里省略掉了
  8. 调用mxsfb_init_fbinfo函数做一些初始化工作

这个函数很重要,

IMX6ULL LCD驱动(一)_第2张图片

一开始是设置fb_info的一些变量,fix是固定的意思,设置一个固定参数的值,关键是设置操作函数fb_info->fbops = &mxsfb_ops;

IMX6ULL LCD驱动(一)_第3张图片

然后调用mxsfb_init_fbinfo_dt函数进一步设置,这个函数是获取设备树中的信息来设置屏幕参数的可变信息,也就是时序

函数很长,不列出函数体,作总结即可

  1. host->id = of_alias_get_id(np, "lcdif"); 读取设备树的lcdif节点,这个节点就对应的是LCD接口的节点
  2. of_property_read_u32(display_np, "bus-width", &width);  获取总线宽度,就是多少根数据线传输LCD数据
  3. of_property_read_u32(display_np, "bits-per-pixel",&var->bits_per_pixel);  每一个像素点占多少个bit
  4. of_find_node_by_name(display_np,"display-timings"); 获取时序参数了,在设备树的display-timings中指定
  5. 接下来是一个循环,如果有多个display-timings的话就把所有的参数都读出来放到vm中,然后从vm中把参数复制到变量fb_vm中, 再把fb_vm添加到fb_info的modelist中fb_add_videomode(&fb_vm, &fb_info->modelist);

到这里就执行完了,所以mxsfb_init_fbinfo_dt函数是循环读取设备树的时序参数,如果有多个的话,就把他们都添加到fb_info的modelist链表中

接着分析mxsfb_init_fbinfo函数,设置完modelist之后会把第一个作为默认的显示参数,赋值为var,

IMX6ULL LCD驱动(一)_第4张图片

然后调用mxsfb_check_var函数检查参数,这个函数中做分辨率和总线宽度的检查,

IMX6ULL LCD驱动(一)_第5张图片

紧接着计算行分辨率的字节数和缓存大小,然后调用mxsfb_map_videomem函数分配LCD的显存空间

IMX6ULL LCD驱动(一)_第6张图片

最后一个函数mxsfb_restore_mode,点进去可以发现,这里就是根据前面读出来的屏幕参数去设置LCD控制寄存器了,

IMX6ULL LCD驱动(一)_第7张图片

最后,缓存清零

这个调用分支就看完了,是去读取参数设置具体单板的LCD控制寄存器的,这一动作执行完毕之后,那接下来,就无疑是去注册前面辛苦设置的fb_info结构体了

回到前面接着分析probe函数

IMX6ULL LCD驱动(一)_第8张图片

最后调用register_framebuffer函数注册fb_info结构体

总结一下

  1. 申请fb_info结构体
  2. 获取屏幕参数以设置LCD控制寄存器
  3. 注册fb_info结构体

本小节内容也相对较多了,注册fb_info结构体的分析放到下一小节分析.

 

 

 

你可能感兴趣的:(IMX6)