基于Framebuffer的Android LCD驱动详解

1、资料快车

1)正点原子(framebuffer):
https://doc.embedfire.com/linux/imx6/driver/zh/latest/linux_driver/framebuffer_sub_system.html
2)Framebuff结构体 :
https://www.cnblogs.com/lifexy/p/7604011.html
3)正点原子嵌入式linux驱动开发——Linux LCD驱动
https://zhuanlan.zhihu.com/p/663352463
4)Linux驱动开发之LCD显示驱动调试-TCON
https://blog.csdn.net/weixin_45244289/article/details/119924258
5) Linux驱动开发: FrameBuffe(LCD)驱动开发(FrameBuffer帧原理、MMAP驱动、OLED驱动)
https://cloud.tencent.com/developer/article/1933138

5)IMX6ULL-LCD-韦东山

2、preface

1、LCD驱动程序属于字符设备

2、为什么用户的FB需要向内核申请内存? 
因为FB最终是给内核使用的,因此需要内核分配好,再mmap给用户使用

3、timming是指哪些?在代码层面,就是描述一帧图片的各个规格(分辨率、时钟频率);
>>总的来说就是告诉处理器,以怎样的速度传输(使用时钟频率来表示),在显示屏什么位置进行显示(使用分辨率等术语来表示,屏参/设备树中给出各个点的坐标 width/height/边框)

3、Panel-LCD硬件原理

1)preface

1、Panel硬件所需要的信号和接口纷繁复杂,这也是硬件发展的更新迭代的结果,种类和细节很多,每个主题需要额外的资料深入;

2、理解Panel的众多参数和结构,是分析驱动程序的基础,务必背下来;

3、屏幕种类:

https://blog.csdn.net/azj2019/article/details/107845843

https://zhuanlan.zhihu.com/p/465894506

1)低端产品使用LED;高端产品手机电视常用LCD;高端产品曲面屏,柔性场景使用OLED;

2)这里讨论最常见的LCD, TFT-LCD;
在这里插入图片描述

接下来从微观到宏观的方式来说明一帧画面的显示过程

2)屏幕显示的原理

1、一帧画面的显示组成
在这里插入图片描述

1)对于1920×1080分辨率的TFT LCD来说, 共需要1920×1080×3个这样的点组合而成;

2)一帧电子画面本质上就是通过不同的颜色 构成表达;

2、一个像素pixel的显示原理
在这里插入图片描述

一个像素的颜色由三原色(RGB)混合而成
在这里插入图片描述

3、灰阶(色深)

1)单靠RGB三种颜色组合出来的颜色是有限的,那如何是好?

2)通过变化三原色亮度,转化为器件的工作原理就是一个发红色/蓝色/绿色的器件,控制其透光程度 来控制亮暗程度,就可以产生不同“层次”的红色/蓝色/绿色,那样组合出来的颜色就十分丰富了,术语称为灰阶,使用色深 来描述亮暗程度范围;
在这里插入图片描述

一种颜色的色深位数越高,色彩越丰富,常见有8bit、10bit,图示为一个亚像素使用10bit来表示

4、一个像素点的数据如何表示?

颜色模型相当于用不同的格式来描述一个颜色。常见的有RGB、YUV、HSV、HSI等。最常用的为RGB,我们从RGB入手。

1)数据单位

bpp(bits per pixel),每个像素用多少数据位来表示

2)三种常见的RGB888/RGB565/RGB555像素表示法

可以用24位数据来表示红绿蓝,也可以用16位(还有其他),比如:

24bpp:RGB888实际上会用到32位,其中8位未使用,其余24位中分别用8位表示红®、绿(G)、蓝(B)

16bpp:有RGB565,RGB555

RGB565:用5位表示红、6位表示绿、5位表示蓝
RGB555:16位数据中用5位表示红、5位表示绿、5位表示蓝,浪费一位
在这里插入图片描述

主控 根据屏幕设计选择合适的像素格式数据;

3)TFT-LCD电路驱动逻辑框图

在这里插入图片描述

1、电路线路主要分为三类,数据线路、控制线路、电源线路(Vcom、Gamma);软件重点关注数据线路、控制线路;

2、屏端部件介绍:

1)Timing Controller 根据TTL/LVDS等信号 自动产生时序控制信号(STV/CPV等),并转化Source driver所支持的协议数据,比如常见的miniLVDs(常见于标清、高清屏)、P2P(常见于4K屏),该器件可以分布在屏端、主板端或独立一个TCON小板;

2)Source driver IC 负责接收数据信号,不同屏厂设计不同,决定接收处理哪种协议的信号,如果不支持SOC送出的数据格式,则需要额外增加转换器件,比如TCON;组合虽然很多,但在某一类产品上设计会相对固定,先掌握原理,用到时再去深入了解其协议内容;

3、数据接口总多,RGB-TTL、LVDS、VB1、miniLVDS、P2P等等,传送数据格式是不同的;

1)在硬件设计初期根据产品特性选定;

2)根据屏幕尺寸、连接距离、传输数据量、抗干扰能力、成本、片上资源等维度来选定;

4)TFT-LCD, Display timming control

在这里插入图片描述

1、成功在显示器显示一个画面,需要各种信号的配合;

2、总来的来说,干一个事情,在什么位置,什么时间,显示什么数据;

3、控制信号是固定的(在产品设计之初需要按照屏规格书配置好);

4、在运行阶段 数据信号和传输速度 可变;

5、LCD的技术参数:

  • 单扫、双扫:从上到下,从左到右;
  • 24位显示:16M(24BPP): 16位显示:16K:565、 5551(1:Alpha) ;
  • VCLK:每来一个时钟,移动一个像素;显示速度是可以控制的;

5)主控与LCD屏幕的连接

在这里插入图片描述

1)上图为RGB接口 ,主控给出的信号,其实不是直接就能驱动屏幕,LCD屏内还集成了时序控制器,将主控给的信号转化为TCON(Timming control)信号和各种电压,最终驱动显示屏显示;

2)我们应该理解panel的各个组成部件(内存、LCD控制器、Tcon控制器等等)的作用,因为不同平台硬件设计这些部件的位置不是固定的,不同产品设计不同,比如LCD控制器有可能直接集成在LCD面板上(比如8080并口接口);

6)LCD控制器的数据来源

在这里插入图片描述

1)LCD控制器会周而复始地从内存中取出一个个像素的颜色值,通过信号线发送给屏端,同时也会产生DCLK、VSYNC、HSYNC、DE信号指示LCD显示屏同步工作;

2)Framebuffer显存分配驱动负责还是应用负责?

因为LCD最终要从内核空间取数据,因此内存分配由驱动负责;应用层会去调用mmap (驱动中使用vm_iomap_memory方法将显存暴露给用户空间使用),涉及内存管理,知识点有点深,这里不去深究;

3)Framebuffer显存如何保证读写同步?

驱动或LCD控制器负责,应用层不负责!

4)Framebuffer显存可以有多个?

PC机上大型游戏就需要强大的显存,可以有多个Framebuffer(需要更多的显存,硬件要支持),驱动负责分配,然后通知应用;

7)引脚介绍(RGB-TTL-LCD)

在这里插入图片描述

1、LED背光一般给的是高压,由专门的电源芯片负责驱动!主控将BL_ON和PWM给到电源芯片

2、两类信号线:数据线、控制信号线、各类电压

1)panel相对其它模块跟多一些,屏驱接口属于高速信号,多用串行差分技术;

2)软件负责数据信号、控制信号的正确性;

3)硬件负责各类电压正确性;

8)接口方案

1、显示屏的接口可谓是ARM模块之最,一个模块竟有十多种!

2、不同产品所用的硬件设计千差万别,这也是嵌入式的特性所在,本质原理不变,以不变应万变!

3、我们从低端到高端的维度进行梳理;

1、接口总览

在这里插入图片描述

1)ADAS:Advanced Driving Assistance System

2)MIPI:Mobile Industry Processor Interface

3)MIPI-DBI (Display Bus Interface)

4)MIPI-DPI (Display Pixel Interface)

5)MIPI-DSI (Display Serial Interface)

6)GMSL:Gigabit Multimedia Serial Link - 多屏应用

7)FPD-Link :Flat Panel Display Link - 车载摄像头

2、8080接口

集成LCD控制器的屏幕称为LCM,MCU使用8080接口与此类屏幕通信,对于LCM的接口8080非常简单,就和内存差不多。

需要读数据,所以有一个读信号RD;

需要写数据,所以有一个写信号WR;

需要分辨这个模组和其他东西,所以有一个片选信号CS;

需要传输数据,需要数据线DB0-DB15,

需要分辨传输的地址,所以需要地址线,但是地址线和数据线共用,通过RS信号来分辨DB0-DB15上传输的是地址还是数据。

在这里插入图片描述

应用场景:由于MCU没有LCD控制器,IO数量也有限,因此MCU常用的8080接口LCD模组,这是妥协的选择;

3、RGB-TTL接口

在这里插入图片描述

在这里插入图片描述

1)TTL:Transistor Transistor Logic的缩写,译为晶体管-晶体管逻辑,TTL电平信号由TTL器件产生;

2)采用TTL接口电路的大多是小尺寸液晶彩电或低分辨率的液晶彩电,旧产品产物,已逐渐被淘汰;

4、LVDS

1)介绍

由于TTL的局限性,迫切需要新的技术,LVDS应运而生,LVDS(Low Voltage Differential Signaling)是一种低摆幅的差分信号技术,它使得信号能在差分PCB 线对或平衡电缆上以几百Mbps(Mbit/s)的速率传输,一般大约77.5M。该接口使用1.2V偏置提供400mV摆幅的信号,低压幅和低电流驱动输出实现了低噪声和低功耗。
LVDS信号包括图像的RGB 基色信号、行同步、场同步信号及时钟信号

2)8Bit LVDS_MAP 连接框图
在这里插入图片描述

3)LVDS的几种接口连线设计
在这里插入图片描述

1、根据应用场景的传输数据量可以增删差分数据线

1)比如标清屏使用6Bit/8Bit LVDS_MAP,高清屏使用8Bit/10Bit LVDS_MAP;

2、LVDS是将RGB数据打包,串行地发送,这就涉及RGB打包的格式,目前有VESA 或 JEIDA两种格式

不同的显示屏支持VESA 或 JEIDA格式,主控需要设置于屏一样的数据格式;

3、一对差分线也称为1 lane;

4、8Bit LVDS_MAP座子设计
在这里插入图片描述

4)LVDS常见接口
在这里插入图片描述

在这里插入图片描述

5)车载应用场景下两种LVDS接口

深入探索LVDS、GMSL与FPD-Link的数据传输革命

https://blog.csdn.net/weixin_43199439/article/details/141642870

https://blog.csdn.net/u010783226/article/details/120492306

5、MiniLvds

1)mini-LVDS(mini Low Voltage Differential Signaling) 接口与LVDS接口相似,数据信号也是由差分信号线对进行传输;

2)SOC芯片的LVDS和mini-LVDS信号引脚是可以复用的,但电气特性不完全一样,共模电压和差分摆幅更小,一次传输的数据小于LVDS;

3)主要用于驱动Tconless屏,TCON器件集成在SOC中,直接输出TCON信号给屏,即TCON到源驱动器(source板)的传输,一般时钟频率小于330MHZ;

4)mini-LVDS 接口技术是在高分辨率、高色深和高帧速的显示应用中,RSDS接口受限于带宽(数据传输速率170MHZ)的限制而发展起来的;

5)MiniLvds信号接口定义
在这里插入图片描述

5)MiniLvds座子
在这里插入图片描述

6、VB1

1)随着近些年来电视画面向着高分辨率,高色彩深度,高刷新率的发展,特别是超高清晰度电视、3D电视、240Hz 运动补偿技术的推动,传输速度的高速化以及传输线之间信号的时滞问题愈发显著。大量的LVDS 数据通道增加了厂商的制造成本,同时占用的空间也影响着整机向进一步轻薄化发展。此外,因为LVDS 需要专门的时钟通道,更高的数据带宽可能导致时钟偏移给系统带来不稳定,也会产生严重的电磁干扰,要利用现有技术来提升性能及降低成本已经很难,LVDS 的局限性越发明显,成本压力和技术限制迫使电视制造商寻求替代的解决方案,V-by-One技术应运而生。

对于我们目前较常见的全高清晰度信号1920 x 1080@120Hz 10-bit达到10Gbps 以上数据量。在实际应用中,LVDS 需要24 个数据/时钟通道,而V-by-One仅需要4个数据通道。目前LVDS电缆和信号传输芯片,在整机成本中占很大的比重。而新接口能极有效降低线材成本、简化芯片设计以及节省系统占用空间。此外,由于传输速度可变,原先采用LDVS 技术的设计不需要经过很大的改动便可无缝地过渡到V-by-One上来。

2)接口框图
在这里插入图片描述

座子
在这里插入图片描述

3)信号线

8对差分信号传输图像信号,主板端芯片输出端约有1.1V直流偏置电压,但传输给屏时会经隔离电容进行隔直。
HTPDN热拔插识别脚,主板端芯片IO口设置为开漏状态,由于上拉电阻作用,在未连接屏时电压在3.3V,但连接屏后会被屏拉致低电平,用于主板检测是否有连接屏,一般SOC引脚连接 悬空不用。
LOCKN用于指示CDR PLL 是否处于锁定状态,未连接屏时被上拉到3.3V,在连接屏后,屏会对输入信号进行时钟和数据还原,而当还原出来的时钟信号锁定后,屏会将LOCKN拉低,一般SOC引脚连接 悬空不用。
Data format 0、Data format 1部分屏用于选择图像分区,如1分区、2分区、4分区、8分区。
IIC:SDA、SCLIIC接口,可用于设置屏的工作状态,部分屏不接IIC会导致花屏或黑屏。
Bit_sel用于设置屏处于10bit工作模式还是8bit工作模式,具体状态可参考屏规格书
L-DIM Enable内部调光时能信号,可参考屏规格书,如高电平是外部PWM调光,低电平是内部自动调光。
7、MIPI

MIPI表示Mobile Industry Processor Interface,即移动产业处理器接口。是MIPI联盟发起的为移动应用处理器制定的开放标准和一个规范。主要是手机内部的接口(摄像头、显示屏接口、射频/基带接口)等标准化,从而减少手机内部接口的复杂程度及增加设计的灵活性。

对于LCD,MIPI接口可以分为3类:

  • MIPI-DBI (Display Bus Interface)

    • 既然是Bus(总线),就是既能发送数据,也能发送命令,常用的8080接口就属于DBI接口。

    • Type B (i-80 system), 8-/9-/16-/18-/24-bit bus

    • Type C (Serial data transfer interface, 3/4-line SPI)

  • MIPI-DPI (Display Pixel Interface)

    • Pixel(像素),强调的是操作单个像素,在MPU上的LCD控制器就是这种接口

    • Supports 24 bit/pixel (R: 8-bit, G: 8-bit, B: 8-bit)

    • Supports 18 bit/pixel (R: 6-bit, G: 6-bit, B: 6-bit)

    • Supports 16 bit/pixel (R: 5-bit, G: 6-bit, B: 5-bit)

  • MIPI-DSI (Display Serial Interface)

    • Serial,相比于DBI、DPI需要使用很多接口线,DSI需要的接口线大为减少
    • Supports one data lane/maximum speed 500Mbps
    • Supports DSI version 1.01
    • Supports D-PHY version 1.00
      在这里插入图片描述

4、APP访问LCD驱动Driver过程

1)总体流程如下

在这里插入图片描述

1.open("/dev/fb0", ...)   主设备号: 29, 次设备号: 0
2.ioctl(fd, FBIOGET_VSCREENINFO, &fb_info->var); //获得可变信息(含有分辨率等)
3.ioctl(fd, FBIOGET_FSCREENINFO, &fb_info->fix); //获得固定信息(含有显存信息)
4.void *ptr = mmap(0,
			fb_info->var.yres_virtual * fb_info->fix.line_length,
			PROT_WRITE | PROT_READ,
			MAP_SHARED, fd, 0);  //获得Framebuffer映射到用户空间的地址
5.往ptr地址写数据,LCD会周而复始读取并显示;

其中:通过mmap(系统调用,fbdev子系统实现)映射内核内存到用户内存,用户可以往此内存空间进行读写;

2)Framebuffer显示原理小结

1、framebuffer机制模仿显卡的功能,将显卡硬件结构抽象为一系列的数据结构,可以通过framebuffer的读写直接对显存进行操作。用户可以将framebuffer看成是显存的一个映像,将其映射到进程空间后,就可以直接进行读写操作,写操作会直接反映在屏幕上(LCD会周而复始地将该区域的数据送显)。

2、显示数据的流向:App -> Framebuffer(内存区域) -> display control -> LCD显示屏;

3、多个APP同时向 Framebuff传输数据,如何处理?

同一时刻,显示屏只能显示一帧数据,多个APP的数据需要合成为一帧,因此引入图像层级合成概念,软件算法可以实现?硬件或软件均可以实现,硬件实现会更快速(app1->fb1、app2->fb2、app3->fb3…)
在这里插入图片描述

4、静止画面和动态画面显示有区别?

无论是哪一种,对于panel底层来说,没有任何区别;

静止画面 -> framebuffer不刷新,反之动态画面需要按照一定的速度进行刷新;

5、总之底层负责拿数据显示,上层需要构造数据

1)UI界面操作:滑动等效果由用户层调用库绘制,再传递给FB;

2)视频:将视频帧按照一定的速度传递给FB;

5、fbdev驱动源码分析

1)panel驱动架构

不同项目使用的panel架构也不同,常见三种

1、基于fbdev
android/common/common14-5.15/common/drivers/video/fbdev

2、基于media子系统
/android/common/common14-5.15/common/common_drivers/drivers/media/vout/lcd

3、基于DRM
/android/common/common14-5.15/common/common_drivers/drivers/drm

实际项目上如何选用?优劣如何?
1)FB是基础,DRM/media只是对FB架构的进一步封装,DRM/media 可以互相搭配使用;
2)先了解fbdev,再过渡到DRM/media子系统;

Media子系统是一个用于处理多媒体设备的框架,他提供的一组API和驱动程序,用于管理和控制视频、音频和其他多媒体设备;

2)panel - boot

1、Boot部分负责什么?
从kernel移植过来的驱动,做一些必要的初始化,以便在boot阶段能播放开机LOGO;

2、Tcon.bin/PQ.bin等的烧录,后续kernel还会设置?
不会重复烧录,在boot阶段完成即可

3)panel - kernel

1、panel驱动属于 字符类设备
在Linux中,“Framebuffer”或者“fb”都指的是framebuffer设备(与其说是设备,更不如说是一种机制),Linux内核将所有和显示有关的软件和硬件集成到一起,虚拟出来了一个fb设备,在LCD能够正常驱动以后就会有一个/dev/fbn(n从0开始的任意数)设备文件,这个fb有些时候还被称作“帧缓存”。

2、fbdev例子:
s3c2410 LCD frame buffer device
/android/vendor/amlogic/common/kernel/common_5.4/drivers/video/fbdev/s3c2410fb.c

NXP官方编写的Linux下的LCD驱动mxsfb.c
内核4.19在fbdev下,内核5.4移植到DRM架构

3、背光控制
/android/vendor/amlogic/common/kernel/common_5.4/drivers/video/backlight

4、引脚控制使用GPIO子系统和pinctrl子系统

5、fb设备节点
console:/dev/graphics # ls
fb0  fb1  fb2
console:/sys/class/graphics # ls
fb0  fb1  fb2

在TV领域中,只有一个显示屏,多个fb,表明使用三个Framebuffer

7、LCD驱动就是一堆参数的设置!
1)先从驱动中读取fb_info;
2)获取一帧数据 fb×的大小,并映射mmap出来;
3)操作这次内存,填充数据,送到屏幕端;

同一块内存,如何处理消费者-生产者同步的?使用VSYNC机制

4)数据结构

1.fb_info--非常多的名词
/android/vendor/amlogic/common/kernel/common_4.9/include/linux/fb.h
struct fb_info { 
	atomic_t count; //引用计数,跟踪有多少个用户(进程)打开了此设备
	int node;  // 设备编号,对应 /dev/fbX 中的 X

	struct fb_var_screeninfo var; //variable 可变屏幕参数(可由应用程序更改):分辨率、色深、刷新率等
	struct fb_fix_screeninfo fix; //fixable 固定屏幕参数(由驱动设定,应用程序只读):帧缓冲内存的物理地址、长度等。
	struct fb_monspecs monspecs;	//显示器规格,支持的模式、频率范围
	struct fb_pixmap pixmap;	// 图像硬件映射器。用于加速绘图操作。
	struct fb_pixmap sprite;	// 光标硬件映射器。用于硬件光标支持。
	struct fb_cmap cmap;		// 当前的调色板(颜色映射表),对 8/16 位色深模式尤为重要。
	struct fb_videomode *mode;	// 当前正在使用的视频模式

	struct fb_ops *fbops;
	struct device *device;		/* This is the parent */
	struct device *dev;		/* This is this fb device */
	int class_flag;                    /* private sysfs flags */
	union {
		char __iomem *screen_base;	//映射到内核空间的帧缓冲内存虚拟地址
		char *screen_buffer;
	};
	unsigned long screen_size;	//射的帧缓冲内存大小(字节)
	void *pseudo_palette;		//伪调色板,一个驱动内部使用的,用于模拟16色模式的数组
};

1) fb_var_screeninfo 、 fb_fix_screeninfo 和fb_ops是驱动与用户空间应用程序(如ffmpeg)交互最重要的三个结构体。
2) node, count, flags, dev, device 这些结构在register_framebuffer() 注册后由内核框架填充和管理。


2.fb_var_screeninfo
/android/vendor/amlogic/common/kernel/common_4.9/include/linux/fb.h
struct fb_var_screeninfo {
    1.screen info
    __u32 xres;    /* visible resolution		*/
    __u32 yres;
    __u32 xres_virtual;  /* virtual resolution */ --内核使用此变量表示分配多少个framebuffer内存
    __u32 yres_virtual;
    __u32 xoffset;
    __u32 yoffset;
    __u32 bits_per_pixel;
    2.Timing
    __u32 pixclock;
    __u32 left_margin;	
}

3.display_timing
/android/vendor/amlogic/common/kernel/common_5.4/include/video/display_timing.h
struct display_timing {
   	struct timing_entry pixelclock;

	struct timing_entry hactive;		/* hor. active video */
	struct timing_entry hfront_porch;	/* hor. front porch */
	struct timing_entry hback_porch;	/* hor. back porch */
	struct timing_entry hsync_len;		/* hor. sync len */

	struct timing_entry vactive;		/* ver. active video */
	struct timing_entry vfront_porch;	/* ver. front porch */
	struct timing_entry vback_porch;	/* ver. back porch */
	struct timing_entry vsync_len;		/* ver. sync len */

	enum display_flags flags;		/* display flags */  -- pin polarity 引脚极性
}

4.fb_videomode
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/fb.h
struct fb_videomode {
	const char *name;	/* optional */
	u32 refresh;		/* optional */
	u32 xres;
	u32 yres;
	u32 pixclock;
	u32 left_margin;
	u32 right_margin;
	u32 upper_margin;
	u32 lower_margin;
	u32 hsync_len;
	u32 vsync_len;
	u32 sync;
	u32 vmode;
	u32 flag;
};

5)fbdev子系统

fbdev子系统的作用,fbmem提供统一的驱动框架和接口,开发者仅需编写s3c2440fb.c即可

1.子系统源码目录
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/of_display_timing.c
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/display_timing.c

2.注册fb设备
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbmem.c
register_framebuffer(fb_info);
--do_register_framebuffer(fb_info);
----registered_fb[i] = fb_info; //将fb_info放到全局数组里

3.file_operations
static const struct file_operations fb_fops = {
	.owner =	THIS_MODULE,
	.read =		fb_read,
	.write =	fb_write,
	.unlocked_ioctl = fb_ioctl,  //ioctl实现
	.mmap =		fb_mmap,  //mmap映射内核内存到用户空间,供应用程序使用
	.open =		fb_open,
};

4.fb_mmap(复杂的内存管理知识域)
用户调用mmap
fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
--fb_mmap()
----return vm_iomap_memory(vma, start, len); //将映射好的内存信息返回给用户

5.fb_open
info = get_fb_info(fbidx);
file->private_data = info;

6.fb_ioctl
--do_fb_ioctl(info, cmd, arg);
----case FBIOGET_VSCREENINFO:  //获取可变的屏幕信息
----case FBIOGET_FSCREENINFO:  //获取固定的屏幕信息
----case FBIOPAN_DISPLAY:

7.从设备树中获取display_timing
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/of_display_timing.c
of_get_display_timings(struct device_node *np)
--of_parse_display_timing(entry, dt);
----parse_timing_property(np, "hactive", &dt->hactive);

6)fb设备源码分析

LCD驱动程序的核心:
1)分配fb_info;
2)设置fb_info;
3)注册fb_info;
4)硬件相关的设置(1、引脚设置;2、时钟设置;LCD控制器设置;)
1、IMX6ULL fbdev驱动程序

1)LCD控制介绍

IMX6ULL的LCD控制器名称为eLCDIF(Enhanced LCD Interface,增强型LCD接口),主要特性如下:
* 支持MPU模式:有些显示屏自带显存,只需要把命令、数据发送给显示屏即可;即8080接口
* VSYNC模式:跟MPU模式类似,多了VSYNC信号。针对高速数据传输(行场信号)
* 支持DOTCLK模式:RGB接口,就是前面讲的TFT-RGB接口
* 支持ITU-R BT.656接口,可以把4:2:2 YcbCr格式的数据转换为模拟电视信号
* 8/16/18/24/32 bit 的bpp数据都支持,取决于IO的复用设置及寄存器配置
* MPU模式,VSYNC模式,DOTCLK模式,都可以配置时序参数。

2)LCD控制器的数据传输与处理
在这里插入图片描述

上图是IMX6ULL的LCD控制器框图。
我们在内存中划出一块内存,称之为显存(framebuffer),软件把数据写入显存。
设置好LCD控制器之后,它会通过AXI总线协议从显存把RGB数据读入FIFO,最后到达LCD接口(LCD Interface)。

3)寄存器设置

1、查看任何芯片的LCD控制器寄存器时,记住几个要点:
1)哪些LCD的信息需要告诉LCD控制器?分辨率、行列时序、像素时钟等;
2)怎么把显存地址、像素格式告诉LCD控制器。

2、LCDIF_CTRL寄存器
[17]: DOTCLK_MODE, 使用DOTCLK模式时-RGB-TTL,设置为1;
[15:14]: INPUT_DATA_SWIZZLE, 显存中像素颜色的数据转给LCD控制器时,字节位置是否交换
[13:12]: CSC_DATA_SWIZZLE,显存中的数据被传入LCD控制器内部并被转换为24BPP后,在它被转给LCD接口之前,字节位置是否交换:
[11:10]: LCD_DATABUS_WIDTH, LCD数据总线宽度,就是对外输出的LCD数据的位宽,8位/16位/18位/24位
[9:8]: WORD_LENGTH, 输入的数据格式,即显存中每个像素占多少位, 8位/16位/18位/24位
[3]: DATA_FORMAT_16_BIT, 0:数据格式为ARGB555;  1:数据格式为RGB565
[2]: DATA_FORMAT_18_BIT,  0:低18位用来表示RGB666,高14位无效  1:高18位用来表示RGB666,低14位无效
[1]: DATA_FORMAT_24_BIT,  0:所有的24位数据都有效,格式为RGB888  1:转给LCD控制器的数据是24位的,但只用到其中的18位,    每个字节用来表示一个原色,每字节中高2位无效
[0]: run, 使能LCD控制器,开始传输数据

3、LCDIF_TRANSFER_COUNT寄存器
[31:16]:V_COUNT,一帧中,有多少行有效数据
[15:0]:H_COUNT,一行中,有多少个像素

4、电气极性寄存器
LCDIF_VDCTRL0/LCDIF_VDCTRL1/LCDIF_VDCTRL2

5. LCDIF_CUR_BUF寄存器
[31:0]:ADDR,LCD控制器正在传输的当前帧在显存中的地址

6.LCDIF_NEXT_BUF寄存器
[31:0]:ADDR,下一帧在显存中的地址

4)驱动程序

IMX6ULL fbdev驱动程序涉及3部分:

1)GPIO设置(通过pinctrl子系统设置引脚,驱动程序不需要写代码,配置设备树即可)
  * LCD引脚
  * 背光引脚
2)时钟设置
  * 确定LCD控制器的时钟
  * 根据LCD的DCLK计算相关时钟
3)LCD控制器本身的设置
  * 比如设置Framebuffer的地址
  * 设置Framebuffer中数据格式、LCD数据格式
  * 设置时序

3.imx6ull probe操作
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/mxsfb.c
mxsfb_probe()
1.分配fb结构体
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbsysfs.c
--framebuffer_alloc()
--platform_get_resource(pdev, IORESOURCE_MEM, 0); 
--devm_ioremap_resource(&pdev->dev, res); //映射为虚拟地址供内核使用
2.填充fb结构体、分配fb内存
--mxsfb_init_fbinfo(host, mode)
----mxsfb_init_fbinfo_dt(host, vmode); //从设备树中提取timing
------of_parse_phandle(np, "display", 0);
------of_property_read_u32(display_np, "bits-per-pixel",
				   &var->bits_per_pixel);
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbmon.c
------fb_videomode_from_videomode(&vm, vmode); //构造fb_videomode
----dma_alloc_wc(dev, PAGE_ALIGN(fb_size), &fb_phys, GFP_KERNEL); /* Memory allocation for framebuffer */
3.构造fb_var_screeninfo
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/modedb.c
--fb_videomode_to_var(&fb_info->var, mode)
--mxsfb_check_var(&fb_info->var, fb_info);
4.注册fb设备
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbmem.c
--register_framebuffer(fb_info);
----do_register_framebuffer();
------fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
5.设置时钟
--mxsfb_enable_controller(fb_info);
----regulator_enable(host->reg_lcd);
----clk_set_rate()  //设置时钟
----mxsfb_enable_axi_clk()


/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/mxsfb.c
struct mxsfb_info {
	struct fb_info fb_info;
	struct platform_device *pdev;
	struct clk *clk;
	struct clk *clk_axi;
	struct clk *clk_disp_axi;
	void __iomem *base;	/* registers */
	unsigned allocated_size;
	int enabled;
	unsigned ld_intf_width;
	unsigned dotclk_delay;
	const struct mxsfb_devdata *devdata;
	u32 sync;
	struct regulator *reg_lcd;
};

/android/vendor/amlogic/common/kernel/common_4.9/drivers/regulator/internal.h
struct regulator {
	struct device *dev;
	struct list_head list;
	unsigned int always_on:1;
	unsigned int bypass:1;
	unsigned int device_link:1;
	int uA_load;
	unsigned int enable_count;
	unsigned int deferred_disables;
	struct regulator_voltage voltage[REGULATOR_STATES_NUM];
	const char *supply_name;
	struct device_attribute dev_attr;
	struct regulator_dev *rdev;
	struct dentry *debugfs;
};

static struct fb_ops mxsfb_ops = {
	.owner = THIS_MODULE,
	.fb_check_var = mxsfb_check_var,
	.fb_set_par = mxsfb_set_par,
	.fb_setcolreg = mxsfb_setcolreg,
	.fb_blank = mxsfb_blank,
	.fb_pan_display = mxsfb_pan_display, //切换framebuffer
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

2、s3c2410 fbdev驱动程序
1.s3c2410fb probe操作
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/s3c2410fb.c
s3c2410fb_probe()
--s3c24xxfb_probe()
1.分配fb结构体
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbsysfs.c
----framebuffer_alloc()
2.分配fb内存
----platform_get_resource(pdev, IORESOURCE_MEM, 0);
----info->mem = request_mem_region();
----info->io = ioremap();//映射为虚拟地址供内核使用
----s3c2410fb_map_video_memory(fbinfo); //根据mach_info->num_displays 分配fb内存(支持多个)
------info->screen_base = dma_alloc_wc(); //从DMA内存分配
3.硬件寄存器初始化
----s3c2410fb_init_registers(fbinfo);
----s3c2410fb_check_var(&fbinfo->var, fbinfo); //构造fb_var_screeninfo结构体
4.注册fb设备
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbmem.c
----register_framebuffer(fbinfo);
----device_create_file(&pdev->dev, &dev_attr_debug); //创建调试节点

6、多Framebuffer架构

1、现代驱动都使用多个Framebuffer?是的

2、多Framebuffer需要硬件什么支持?

1)足够的显存空间; 有的使用普通内存、有的使用DMA内存;

2)并不是越多越好,根据平台的处理速度选择分配多少个Framebuffer;

3、应用程序怎么操作?

填充好数据,通知驱动switch,那样不会很耗费资源?现代SOC速度,可以忽略不计!

4、驱动和应用程序要配合着使用,流程如下
在这里插入图片描述

1)驱动先分配多个buffer,表示硬件上支持,等APP查询时会获悉驱动支持多个buffer,再决定使用单buffer还是多buffer;

2)切换完成后,进入下一轮循环;

3)掌握这个基础模型,即使后面使用更复杂的子系统/中间件,封装得再严密,都离不开这个基础核心操作;

1.驱动分配多个framebuffer
/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/mxsfb.c
fb_info->fix.smem_len = SZ_32M;
fbi->screen_base = dma_alloc_writecombine(fbi->device,
				fbi->fix.smem_len,
				(dma_addr_t *)&fbi->fix.smem_start,
				GFP_DMA | GFP_KERNEL);

fb_info->fix.smem_len  // 含有总buffer大小 
fb_info->var           // 含有单个buffer信息

2.APP:读取buffer信息
ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);

// 计算是否支持多buffer,有多少个buffer
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
nBuffers = fix.smem_len / screen_size;

3.APP:使能多buffer
var.yres_virtual = nBuffers * var.yres;
ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);

4.APP:获取buffer并写buffer
1)通过系统调用mmap来获取可操作的framebuffer
fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
/* get buffer */
pNextBuffer =  fb_base + nNextBuffer * screen_size;
/* set buffer */
lcd_draw_screen(pNextBuffer, colors[i]);

5.APP:开始切换buffer
/* switch buffer */
var.yoffset = nNextBuffer * var.yres;
ioctl(fd_fb, FBIOPAN_DISPLAY, &var);

/android/vendor/amlogic/common/kernel/common_4.9/drivers/video/fbdev/core/fbmem.c
fb_ioctl()
--do_fb_ioctl
----fb_pan_display(info, &var);
------info->fbops->fb_pan_display(var, info) // 调用硬件相关的函数  
--------writel(fb_info->fix.smem_start + offset,
			host->base + host->devdata->next_buf); /* update on next VSYNC */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值