帧缓冲(framebuffer)是 Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关系物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备,主设备号为29,对应于/dev/fbn。lcd驱动就是属于帧缓冲设备驱动
LCD分类:
LCD按材料分有:STN(超扭曲阵列),TFT(薄膜晶体管),LTPS(低温晶体多晶硅), OLED(有机发光二极管)等。LCD的接口种类有:TTL RGB(数字RGB颜色接口),i80(又称MCU接口),LVDS(低压差分信号),RSDS(低摆幅差分信号),EDP(嵌入式显示接口),MIPI DSI(MIPI标准显示接口)等
LK:
aboot_init()target_display_init()gcdb_display_init()oem_panel_select():选择使用的屏dsi_panel_init():初始化 panel 参数msm_display_init() pdata->power_func 开启电源pdata->pll_clk_func 使能时钟msm_fb_alloc为帧缓冲器(frame buffer)分配内存。display_image_on_screen()从splash分区获取lk logo图片,如果splash分区没有满足要求的数据,就显示默认的logo。msm_display_config() dsi发送初始化命令msm_display_on()使能DSI VIDEO,用初始化命令来初始化panelpdata->bl_func(1) 开启背光
kernel:
一般的执行顺序是:MDP probe → DSI probe → FB probe。 MDP驱动:是高通内部的显示控制芯片。主要对使用的硬件资源进行初始化:时钟,中断,iommu,总线,DMA等。同时注册一些接口供fb设备使用。DSI驱动:传输协议层设备。解析模组厂商提供的panel的dtsi文件,从哪个文件中能够获取到panel的mode,分辨率,还有Driver IC的初始化command;FB驱动:显卡的驱动设备。实现Linux Framebuffer的注册和为上层用户提供访问控制接口。FB驱动在 mdss_fb.c 中完成MDP驱动层:module_init(mdp3_driver_init)----> platform_driver_register(&mdp3_driver) 然后根据 dsi 设备树文件里的mdss_mdp: qcom,mdss_mdp@1a00000 {compatible = "qcom,mdss_mdp3", 跟 mdp3_driver 的mdp3_dt_match[] = { { .compatible = "qcom,mdss_mdp3",}}匹配mdp3_probe()。匹配成功,则执行 probe函数。mdp3_res->mdss_util = mdss_get_util_intf() 以及接下来关于mdp3_res->mdss_util 执行全局变量 mdss_util 的初始化,fb注册时会用到。mdss_fb_register_mdp_instance(&mdp3_interface):将mdp3_interface赋值给全局变量mdp_instance,用fb注册时初始化mdp等操作mdp3_res->mdss_util->mdp_probe_done = true:标志mdp已经执行probe,dsi probe会检测
DSI驱动层:
module_init(msm_dsi_v2_driver_init) ---->platform_driver_register(&msm_dsi_v2_driver),用哪个dsi驱动,是根据dsi设备树文件里的mdss_dsi0: compatible = "qcom,msm-dsi-v2"。然后再跟msm_dsi_v2_driver结构体里的driver.name = "msm_dsi_v2"进行匹配驱动。在kernel/drivers/video/msm/mdss/dsi_host_v2.cmsm_dsi_probe()。如果匹配成功,则执行msm_dsi_v2_driver 里的.probe = msm_dsi_probe ,msm_dsi_probe函数。(LCD 初始化信息 : 遍历设备树,寻找fb设备节点。初始化lcd屏幕,注册LCD屏幕。)platform_set_drvdata(pdev, ctrl_pdata)。将ctrl_pdata 的数据传递给 pdev 。platform_get_resource()。从设备中获取内存、中断资源mdss_get_util_intf():获得全局变量mdp_instance,在mdp probe时注册of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 将设备树数据填充给platform_devicedsi_get_panel_cfg()。 dsi 里的 MDSS_DSI0 在 MDSS_MDP 数组里面选定具体那个LCD 配置。再根据 MDSS_MDP 选定的 lcd 配置里 获取具体名字 qcom,mdss-dsi-panel-name = “xxxx” 的配置项。dsi_find_panel_of_node()。 通过上面获取的panel name找到dsi panel节点。mdss_dsi_panel_init()。 初始化节点里 lcd 的 panel 参数。dsi_ctrl_config_init()。获得调节电源的数据,解析gpio、phy设置msm_dsi_io_init()。时钟和regulator的初始化dsi_register_interface()。注册 dsi 接口msm_dsi_ctrl_init()。初始化控制器msm_dsi_irq_init()。初始化中断dsi_panel_device_register_v2()。注册panel_device设备节点,确认是视频还是命令模式,并且注册dsi_event_handler 来与 mdp层、fb层进行通信。
FB驱动的注册:
module_init(mdss_fb_init)---------->> 调用platform_driver_register(&mdss_fb_driver)。平台设备是 fb0 所以用的是 dsi 中 mdss_mdb: mdi_fb0 :compatible = "qcom,mdss_fb" 跟mdss_fb_driver 结构体里面的 driver.name = "mdss_fb" 进行匹配驱动。在kernel/drivers/video/msm/mdss/mdss_fb.cmdss_fb_probe()。如果匹配成功,则执行 mdss_fb_driver 里的 .probe =mdss_fb_probe 函数。dev_get_platdata(&pdev->dev) 。 获取 mdss_panel_data 结构信息。mdss_fb_register() :fb_info 结构体进行填充,完成fb的注册,从mfd中取出屏信息放到fbi中,进行fb注册,并包含fb给用户空间的的操作函数register_framebuffer(struct fb_info *fb_info):注册 fb设备(在函数同文件中,即:kernel\drivers\video\fbmem.c中module_init(fbmem_init) ----> fbmem_init ---->register_chrdev()创建了一个字符设备,证明 lcd 驱动 就是一个字符设备驱动),这个就是核心层的接口,应用层可以对fb0节点的控制来操作framebuffer缓冲区struct fb_info *fb_info。注册framebuffer时需要用到fb_info结构体,fb_info结构体又包含了 fb_ops 、fb_var_screeninfo和fb_fix_screeninfo结构体fb_ops结构体中的fb_read、fb_write用于应用层对framebuffer的读写操作,fb_mmap用于应用进程和framebuffer之间的内存映射,fb_ioctl用于应用层对framebuffer进行的一些控制操作,fb_check_var和fb_set_par分别用于获取和设置framebuffer的显示参数fb_var_screeninfo记录用户可修改的显示控制参数,包括屏幕分辨率和每个像素点的比特数。fb_fix_screeninfo中记录用户不能修改的显示控制器的参数,如屏幕缓冲区的物理地址、长度。mfd->mdp.init_fnc(mfd):调用MDP的接口:init_fnc = mdp3_init mdp3_init mdp3_ctrl_init注册控制函数led_classdev_register(&pdev->dev, &backlight_led) 。注册背光灯设备