函数加载流程:
dsi_panel_init(struct msm_panel_info *pinfo,struct panel_struct *pstruct)---->panel初始化,获取屏的基本信息,从屏对应的头文件中;
msm_display_init();---->亮屏的开始
pdata->power_func(1, &(panel->panel_info));---->给屏上电,panel.power_func = mdss_dsi_panel_power;
pdata->dfps_func(&(panel->panel_info));---->panel.dfps_func = mdss_dsi_mipi_dfps_config;
pdata->pll_clk_func(1, &(panel->panel_info));---->使能时钟;panel.pll_clk_func = mdss_dsi_panel_clock;
msm_fb_alloc(&(panel->fb));----->申请FB缓冲区
msm_display_config();------->LCD的基本配置;如;mipi的配置,DSI控制器的初始化等;
msm_display_on();------>向LCD屏的寄存器中写入ON_command命令参数,并检测mipi的数据通道是否是通路;
ret = pdata->bl_func(1);---->背光使能;初始化背光;
上层调用ioctrl()函数向底层FB节点发送亮灭屏事件命令,底层调用fb_ioctrl()函数去调用fb_blank()以通知链的方式去通知TP和加载一系列事件函数,根据事件命令去处理对应的操作,而这个事件处理函数是在DSI驱动中的probe()函数中注册的;
先执行LCD上电事件命令,再执行亮屏事件命令,经过函数调用,最终会调用到在DSI驱动中解析屏的on_command命令参数写入到LCD屏对应的寄存器中,亮屏后,在打开背光;
执行灭屏操作和亮屏操作其实差不多,也是把灭屏的OFF_command指令写到LCD寄存器中,但在执行命令之前,会把背光设置为0,关闭背光;在执行完灭屏事件后,再去执行给LCD下电的操作;
亮屏操作流程:
fb_ioctl()------->framebuff节点对应的函数操作,位置:fbdev/core/fbmem.c ,创建一个FB节点给上层去操作;
info = file_fb_info(file);---->获取mdss_fb_probe()里面注册的一些函数;
do_fb_ioctl();
fb_blank(struct fb_info *info, int blank);---->参数blank就是下面函数中的blank_mode;这个是亮屏的起始函数;
fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);---->TP通知链,通知TP做一些相应的动作;
mdss_fb_blank(int blank_mode, struct fb_info *info);------>启动事件子系统;---->fb_blank = mdss_fb_blank();在fb_probe中注册的;
mdss_fb_blank_sub(int blank_mode, struct fb_info *info,int op_enable);----->switch函数中判断的根据blank_mode;
mdss_fb_blank_unblank(mfd);----->亮屏操作;
mfd->mdp.on_fnc();--->mdp5_interface->on_fnc = mdss_mdp_overlay_on;
mdss_mdp_overlay_on();------>mdp5_interface->on_fnc = mdss_mdp_overlay_on;
mdss_mdp_overlay_kickoff();
mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,struct mdss_mdp_commit_cb *commit_cb);
ctl->ops.display_fnc;----->ctl->ops.display_fnc = mdss_mdp_video_display; mdss_mdp_video_display()回调函数注册在mdss_mdp_video_start()中;///////////
mdss_mdp_video_display();------->亮屏的主要函数;
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_LINK_READY, NULL,CTL_INTF_EVENT_FLAG_DEFAULT);---->上电操作;
mdss_dsi_on(struct mdss_panel_data *pdata);
mdss_dsi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_ON);---->上电操作
mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_UNBLANK, NULL,CTL_INTF_EVENT_FLAG_DEFAULT);----->发送亮屏(MDSS_EVENT_UNBLANK)和灭屏(MDSS_EVENT_BLANK)事件来点亮和熄灭屏;
rc = pdata->event_handler(pdata, event, arg);------->接受事件去处理;ctrl_pdata->panel_data.event_handler = mdss_dsi_event_handler;
mdss_dsi_event_handler(struct mdss_panel_data *pdata,int event, void *arg);-----》事件处理函数;
mdss_dsi_unblank(struct mdss_panel_data *pdata);----->点亮屏和使能TE引脚中断等功能;
ctrl_pdata->on(pdata);--->ctrl_pdata->on = mdss_dsi_panel_on;
mdss_dsi_panel_on(struct mdss_panel_data *pdata);---->发送on命令和屏幕相关参数命令;
mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT);---->发送on_cmds,CE_cmds,CABC_cmds等命令;
mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_PANEL_ON, NULL);---->也是点亮屏,只是这个另一模式HS模式,这个模式主要在dtsi文件中去配;
mdss_fb_set_backlight(mfd, mfd->unset_bl_level);----->打开背光;
灭屏函数流程:
在这里插入fb_ioctl()------->framebuff节点对应的函数操作,位置:fbdev/core/fbmem.c ,创建一个FB节点给上层去操作;
info = file_fb_info(file);---->获取mdss_fb_probe()里面注册的一些函数;
do_fb_ioctl();
fb_blank(struct fb_info *info, int blank);---->参数blank就是下面函数中的blank_mode;这个是亮屏的起始函数;
fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);---->TP通知链,通知TP;
mdss_fb_blank(int blank_mode, struct fb_info *info);------>启动事件子系统;---->fb_blank = mdss_fb_blank();在fb_probe中注册的;
mdss_fb_blank_sub(int blank_mode, struct fb_info *info,int op_enable);----->switch函数中判断的根据blank_mode;
mdss_fb_blank_blank();
mdss_panel_is_power_off(req_power_state);----->有一个电源状态检测;
mdss_fb_stop_disp_thread(mfd);--->关闭那个dispaly处理线程;
mdss_fb_set_backlight(mfd, 0);---->设置背光亮度为0;关闭背光;
mdss_mdp_overlay_off(mfd);---->mdp5_interface->off_fnc = mdss_mdp_overlay_off;
mdss_mdp_ctl_stop(mdp5_data->ctl, mfd->panel_power_state);--->灭屏
mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state);-------->ctl->ops.stop_fnc = mdss_mdp_video_stop;
mdss_mdp_video_intfs_stop();
mdss_mdp_video_ctx_stop();
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, NULL,CTL_INTF_EVENT_FLAG_DEFAULT);
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_OFF, NULL,CTL_INTF_EVENT_FLAG_DEFAULT);
mdss_dsi_blank();----->灭屏事件处理函数
ctrl_pdata->off(pdata);---->灭屏指令的发送;
mdss_dsi_panel_off(struct mdss_panel_data *pdata);
mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT);
mdss_dsi_off(pdata, power_state);---->下电操作;
mdss_dsi_panel_power_ctrl(pdata, power_state);----下电;代码片
背光概念:背光是控制屏幕亮度,
背光控制有3种方式: 1.LED灯控制背光,通过控制LED的电流大小,来控制最大亮度,通过控制级数来控制背光的变化;(pmic提供引脚)
2.PWM的方式控制背光,从电路中引出某一引脚来控制三极管电路,从而控制电路的电流大小;(单独的电路芯片)
3.命令集的方式控制背光,通过以命令的方式向寄存器中写入控制的值来控制电路;(它的供电的脚是从LCD芯片引出来的)
wled方式背光驱动路径:kernel/msm-3.18/drivers/leds
背光级别的映射关系:(这个很重要,跟上层设置滑动亮度进度条,亮度值大小有关系,)
#define MDSS_BRIGHT_TO_BL(out, v, bl_max, max_bright) do {\
out = (2 * (v) * (bl_max) + max_bright);\
do_div(out, 2 * max_bright);\
} while (0)
mdss_fb_probe():
backlight_led.brightness = mfd->panel_info->brightness_max;
backlight_led.max_brightness = mfd->panel_info->brightness_max;
led_classdev_register(&pdev->dev, &backlight_led);---->注册背光目录和节点供上层使用;(重要点,上层会操作该目录的节点去控制背光亮度)
device_create_with_groups();----->创建背光组控制;一系列的节点文件;
上层对节点写亮度值,底层亮度设置的操作流程:
mdss_fb_set_bl_brightness();---->mdss_fb.c
mdss_fb_set_backlight();----->mdss_fb.c
mdss_fb_scale_bl(mfd, &temp);----->CABL开启后,会重新计算出一个背光值,目的是降低功耗;
pdata->set_backlight(pdata, temp);----->mdss_dsi_panel.c
mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, int level);----->这边已经可以达到4095级别;
led_trigger_event(struct led_trigger *trig, int brightness);
led_set_brightness(struct led_classdev *led_cdev, int brightness);
__led_set_brightness(struct led_classdev *led_cdev, int brightness);
led_cdev->brightness_set(led_cdev, brightness);----->函数指针的方式跳转到设置函数;
qpnp_wled_set(led_cdev, brightness);------->kernel/msm-3.18/drivers/leds/leds-qpnp-wled.c
schedule_work(&wled->work);----->调用工作队列,使用工作线程去执行这设置函数;
qpnp_wled_set_level(struct qpnp_wled *wled, int level);
qpnp_wled_write_reg();------>分别向寄存器中写入高8位和低8位数据;
qpnp_wled_sync_reg_toggle();---->同步寄存器
qpnp_wled_module_en();----->背光是否使能;
4.1.数据显示流程frambuff的注册
4.1.1 FB注册流程
4.1.2 图像数据显示流程
4.2.LCD屏上下电机时钟
SI时钟计算如下:
H-total = HorizontalActive + HorizontalFrontPorch + HorizontalBackPorch + HorizontalSyncPulse + HorizontalSyncSkew(data + 前肩 +后肩 + 行同步(换行时钟 + 电子前移时钟))
V-total = VerticalActive + VerticalFrontPorch + VerticalBackPorch + VerticalSyncPulse + VerticalSyncSkew
Total pixel = H-total * V-total * 60(Hz通常都是这个,当然可以变).
Bitclk = Total pixel * bpp(byte) *8/lane number(有几路mipi data lane).
Byteclk = bitclk/8
Dsipclk(Dsi pixel clock) = (Byteclk * lane number)/bpp(byte) = Total pixel * 8
Byteclk = pclk * pixel depth / lane number
另一种写法:(一秒传输数据的所需要的时钟,这跟帧率有关系)
PLL_CLOCK:Mipiclock = [ (width+hsync+hfp+hbp) x (height+vsync+vfp+vbp) ] x(bus_width) x fps/ (lane_num)/2;
MIPI时钟是每一条数据通道的时钟,因为在传输数据的时候,是多数据通道同时在传输数据,并且只用一个时钟脚;
4.3.ESD防静电
高通的esd功能也是通过dtsi来配置的。如:
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A];//read reg
qcom,mdss-dsi-panel-status-command-mode = "dsi_lp_mpde";
qcom,mdss-dsi-panel-status-check-mode = "reg_read";
qcom,mdss-dsi-panel-status-read-length = <1>;
qcom,mdss-dsi-panel-status-valid-params = <1>;
qcom,mdss-dsi-panel-status-value = <0x9C>;//right value
qcom,mdss-dsi-panel-max-error-count = <3>;
需要注意的是,这些属性对应的参数除qcom,mdss-dsi-panel-max-error-count都没有默认值,如果配置不完整,可能会导致整个esd功能失效。配置完成后,确保屏幕无异常。
可以修改qcom,mdss-dsi-panel-status-value为其他值,观察是否有esd resume情况发生。判断esd机制是否生效。
总结:上层会在定时去检查屏幕是否有问题,而ESD这个功能的检测就去检测是否有问题,ESD功能的检测有两种方式,一种是TE引脚的方式(硬件),一种是读取寄存器值的方式(软件);
到时候系统去定时检测屏状态时,会根据dtsi里配置的ESD方式去调用相应的函数来检测;根据 "qcom,mdss-dsi-panel-status-check-mode"去判断哪一种模式;
1.读寄存器方式check dsi 状态的流程:
__init mdss_dsi_status_init(void);--->驱动加载函数;
check_dsi_ctrl_status();---->INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status);工作队列的方式;
mdss_check_dsi_ctrl_status();----->pdsi_status->mfd->mdp.check_dsi_status(work, ESD_interval);工作队列的方式;
mdss_dsi_reg_status_check();---->会把这个函数放在延迟工作队列中去执行;
mdss_dsi_gen_read_status();------->check_read_status(ctrl_pdata);---->ctrl->check_read_status = mdss_dsi_gen_read_status;
mdss_dsi_cmp_panel_reg_v2();----->读取对应的寄存器值,并与在之前的dtsi文件设置的值,进行比较;
2.TE引脚中断方式去check dsi 状态的流程;
__init mdss_dsi_status_init(void);--->q驱动加载函数;
check_dsi_ctrl_status();---->INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status);工作队列的方式;
mdss_check_dsi_ctrl_status();----->pdsi_status->mfd->mdp.check_dsi_status(work, ESD_interval);工作队列的方式;
mdss_dsi_TE_NT35596_check();------->硬件中断的方式去检测DSI状态;
4.4.屏幕效果及色温设置方式
4.5.LCD屏dtsi文件解析
4.6 LCD常见的问题
花屏问题:
1.LCD初始化时序信号不对;
2.花屏还有一种可能是Baseband给LCD送的数据跟LCD工作模式不对。比如你对LCD的工作模式设为RGB565,但你给它送的数据是RGB444或RGB666;
3.host对LCD读写过快;
开机LCD亮的瞬间有花屏问题:
解释:一般都是由于LCD在初始化完成后刷新第一副图像未完全准备好的时候背光已经亮了。解决的办法就是在UBOOT时候背光亮之前的延时相应的加长一点。
屏幕闪烁问题:1.背光闪烁;2.屏幕闪烁;
关闭panel打开背光,看是否闪,如果闪可能panel的背光设计有问题或者是残次品,或者背关芯片供电不稳。(背光闪烁)
如果背光不闪,panel闪的话大部分原因跟频率有关系,把频率调整到何时的值看是否还有闪的现象,调整panel自身的刷新率看所否还有有闪的现象;(屏幕闪烁)
在休眠情况下,重新唤醒屏,使用一段时间出现烧屏问题:
原因:在灭屏情况下,不断唤醒上电操作和睡眠操作导致IC芯片;PM频繁调用TP suspend/resume,当LCD 处于sleep in状态,频繁进出LPWG会导致IC状态异常,击穿VCOM。
解决方案:1.只在FFBM模式下PM才能调用TP resume/suspend(唤醒/休眠); 2.IC厂商修改TP固件,保证出现这种现象也不会造成IC芯片烧坏;
打开CABC开关,偶现花屏现象:
原因:屏幕的相关参数问题,在config的时候没有配置上;
解决方案:修改相关屏的初始化参数,