添加LK显示屏代码
修改/bootable/bootloader/lk/target/msm8952/oem_panel.c
添加屏幕厂家提供的驱动头文件,例:
将头文件添加到bootable/bootloader/lk/dev/gcdb/display/include/目录下
#include “include/panel_kd_ota7290b_1200p_video.h”
static struct panel_resolution nt35521_video_panel_res = {
//800, 1280, 44, 55, 11, 0, 14, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
// w, h, hf, hb, hp, 0, vf, vb, vp, 0, 0, 0, 0, 0, 0, 0, 0, 0
800, 1280, 16, 48, 40, 0, 5, 3, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
在数组中添加新的屏幕
在函数init_panel_data()中添加与刚才数组对应的panel_id
在init_panel_data()中会根据选择的panel_id填充一些系统的结构体,填充结构体的值在添加的显示屏驱动的头文件中。
在/bootable/bootloader/lk/platform/msm_shared/mipi_dsi.c中mdss_dsi_read_panel_signature()会根据init_panel_data()中填充的结构体的panel_signature选择读取不同显示屏的信息
mipi_ota7290b_manufacture_ida(mipi)为读取方法。
根据显示屏需要的工作电压修改/bootable/bootloader/lk/target/msm8952/regulator.c
在kernel中添加显示屏代码
修改设备树显示屏电压/kernel/msm-3.18/arch/arm64/boot/dts/readboy/msm8937-mdss-panels.dtsi
修改/kernel/msm-3.18/arch/arm64/boot/dts/readboy/msm8937-regulator.dtsi设备文件显示屏电压
在/kernel/msm-3.18/arch/arm64/boot/dts/readboy/msm8937-pmi8937-qrd-sku2.dtsi中#include显示屏厂家提供dsi-panel-kd-ota7290b-1200p-video.dtsi文件,也有部分代码是把#include “dsi-panel-kd-ota7290b-1200p-video.dtsi“放在kernel/arch/arm64/boot/dts/qcom/msm8976-mdss-panels.dtsi中,并将厂家提供的TP设备树文件的配置添加到i2c@78b7000 {} 节点中,一般情况下都需要根据平台修改厂家提供设备树文件。
配置为双MIPI模式
/kernel/arch/arm64/boot/dts/qcom/msm8976-qrd-skun.dtsi
显示屏的背光配置也在这个文件里面
&dsi_hx084jr002_vid {qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm"; //采用PWM方式qcom,mdss-dsi-bl-pmic-pwm-frequency = <40>; //PWM period in microsecondsqcom,mdss-dsi-bl-pmic-bank-select = <0>; Light PulseGenerator channel for backlightqcom,mdss-dsi-pwm-gpio = <&pm8950_mpps 4 0>;
};&mdss_dsi {hw-config = "split_dsi"; //设置为双MIPI模式
};&mdss_dsi0 { //配置第一MIPI通道pinctrl-names = "mdss_default", "mdss_sleep";pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;qcom,dsi-pref-prim-pan = <&dsi_hx084jr002_vid>;qcom,platform-enable-gpio = <&msm_gpio 16 0>;qcom,platform-reset-gpio = <&msm_gpio 25 0>;
};
&mdss_dsi1 { //配置第二MIPI通道pinctrl-names = "mdss_default", "mdss_sleep";pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;qcom,dsi-pref-prim-pan = <&dsi_hx084jr002_vid>;qcom,platform-enable-gpio = <&msm_gpio 16 0>;qcom,platform-reset-gpio = <&msm_gpio 25 0>;};
kernel上电电压配置
/kernel/arch/arm64/boot/dts/g600a/msm8976-mdss-panels.dtsi
dsi_panel_pwr_supply: dsi_panel_pwr_supply {#address-cells = <1>;#size-cells = <0>;...qcom,panel-supply-entry@4 {reg = <4>;qcom,supply-name = "vdd_ana"; //添加vdd_ana,上电时拉高qcom,supply-min-voltage = <2800000>;qcom,supply-max-voltage = <2800000>;qcom,supply-enable-load = <100000>;qcom,supply-disable-load = <100>;};};
为vdd_ana选择ldo电源,/kernel/arch/arm64/boot/dts/g600a/msm8976-mdss.dtsi
mdss_dsi: qcom,mdss_dsi@0 {
...mdss_dsi0: qcom,mdss_dsi_ctrl0@1a94000 {...vdd-supply = <&pm8950_l17>;vddio-supply = <&pm8950_l6>;lab-supply = <&lab_regulator>;ibb-supply = <&ibb_regulator>;vdd_ana-supply = <&pm8950_l10>; //选择ldo10作为vdd_ana的电源};
};
msm8976-mdss.dtsi中配置的电压会通过\kernel\drivers\video\msm\mdss\mdss_dsi.c调用,把所有配置的电压拉高
static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata)
{
...ret = msm_dss_enable_vreg( //拉高各路电压ctrl_pdata->panel_power_data.vreg_config,ctrl_pdata->panel_power_data.num_vreg, 1);if (ret) {pr_err("%s: failed to enable vregs for %s\n",__func__, __mdss_dsi_pm_name(DSI_PANEL_PM));return ret;}
...ret = mdss_dsi_panel_reset(pdata, 1); //启动reset时序...
}static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
{
...
ret = ctrl_pdata->on(pdata); //开启MIPI实现函数为ctrl_pdata->on = mdss_dsi_panel_on,在\kernel\drivers\video\msm\mdss\mdss_dsi_panel.c
...
}
\kernel\drivers\video\msm\mdss\mdss_io_util.c
int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable)
{
...for (i = 0; i < num_vreg; i++) { //该循环打开所有ldo电压...rc = regulator_enable(in_vreg[i].vreg);...}
...
}
配置显示屏需要的正负压VSP,VSN
修改little kernel,bootable\bootloader\lk\target\msm8952\target_display.c
static void wled_init(struct msm_panel_info *pinfo)
{
...config.display_type = display_type;config.lab_init_volt = 4600000; /* fixed, see pmi register */config.ibb_init_volt = 1400000; /* fixed, see pmi register */config.lab_ibb_swire_control = swire_control;config.wled_avdd_control = wled_avdd_control;if(!swire_control) {if (labibb && labibb->force_config) {config.lab_min_volt = labibb->lab_min_volt;config.lab_max_volt = labibb->lab_max_volt;config.ibb_min_volt = labibb->ibb_min_volt;config.ibb_max_volt = labibb->ibb_max_volt;config.pwr_up_delay = labibb->pwr_up_delay;config.pwr_down_delay = labibb->pwr_down_delay;config.ibb_discharge_en = labibb->ibb_discharge_en;} else {/* default */config.pwr_up_delay = 3;config.pwr_down_delay = 3;config.ibb_discharge_en = 1;if (display_type) { /* amoled */config.lab_min_volt = 4600000;config.lab_max_volt = 4600000;config.ibb_min_volt = 4000000;config.ibb_max_volt = 4000000;} else { /* lcd */config.lab_min_volt = 5800000;config.lab_max_volt = 5800000;config.ibb_min_volt = 5800000;config.ibb_max_volt = 5800000;}}}
...
}
修改kernel,\kernel\arch\arm64\boot\dts\qcom\msm-pmi8950.dtsi
qcom,pmi8950@3 {
...
...
};
lab和ibb之间的延时设置
ap_msm8976\kernel\drivers\regulator\qpnp-labibb-regulator.c
/** This API is used to set the power up delay for IBB regulator* regulator: the reglator device* val: the delay in us for power up of IBB regulator*/
int qpnp_ibb_set_pwrup_dly(struct regulator *regulator, u32 val)
{struct qpnp_labibb *labibb;int rc = 0;u8 reg;labibb = regulator_get_drvdata(regulator);for (reg = 0; reg < ARRAY_SIZE(ibb_pwrup_dly_plan); reg++)//从ibb_pwrup_dly_plan[reg]中选择合适的延时时间if (val == ibb_pwrup_dly_plan[reg])break;if (reg == ARRAY_SIZE(ibb_pwrup_dly_plan))reg = ARRAY_SIZE(ibb_pwrup_dly_plan) - 1;mutex_lock(&(labibb->ibb_vreg.ibb_mutex));if (labibb->ibb_vreg.pwrup_dly == ibb_pwrup_dly_plan[reg]) {rc = 0;goto _exit;}//通过配置寄存器设置延时时间rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base,REG_IBB_PWRUP_PWRDN_CTL_1,IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK <<IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT,reg << IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT,1);if (rc) {pr_err("qpnp_ibb_sec_masked_write register %x failed rc = %d\n",REG_IBB_PWRUP_PWRDN_CTL_1, rc);goto _exit;}//把设置的ibb上电延时赋给一个全局变量,读取ibb开启关闭状态会用到labibb->ibb_vreg.pwrup_dly = ibb_pwrup_dly_plan[reg];_exit:mutex_unlock(&(labibb->ibb_vreg.ibb_mutex));return rc;
}
kernel上下电代码
\msm8916_G35S\kernel\drivers\video\msm\mdss\mdss_dsi.c
mdss_dsi_on -> mdss_dsi_panel_power_on -> msm_dss_enable_vreg, mdss_dsi_panel_reset
mdss_dsi_on调用mdss_dsi_panel_power_on之后会初始化mipi,lp11会在mdss_dsi_on中设置
控制mipi发送显示屏初始化代码
mdss_dsi_event_handler -> mdss_dsi_unblank -> ctrl_pdata->on(pdata)
ctrl_pdata->on实现函数在mdss_dsi_panel.c
ctrl_pdata->on = mdss_dsi_panel_on;
LK上电控制代码
\bootable\bootloader\lk\dev\gcdb\display\gcdb_display.c,中调用\bootable\bootloader\lk\target\msm8952\target_display.c实现上电,背光控制的功能函数,这些功能函数的指针会注册到一个结构体,在\bootable\bootloader\lk\platform\msm_shared\display.c中获取结构体调用这些函数
display.c -> gcdb_display.c -> target_display.c
wled背光调节
需要用pmic调节背光把dtsi中改成:
qcom,mdss-dsi-bl-pm ic-control-type=”bl_ctrl_w led”;
qcom, mdss-dsi-bl-min-level=<1>;
qcom,mdss-dsi-bl-max-level=<4095>;
msm-pmi8950.dtsi(不同pmic文件可能有变化) 中有关于调节亮度电流的一些配置:
对应pmi8952一共有4路,没路最大30mA配置方法如下:
开启3路,每路最大30毫安的配置:
qcom,led-strings-list=[000102] ;
qcom,fs-curr-ua=<30000>;
开启4路,每路最大20毫安的配置:
qcom,led-strings-list=[00010203] ;
qcom,fs-curr-ua=<20000>;
例如有个屏最大需要80mA,那么可以开启3路,每路30mA
qcom,led-strings-list=[00 01 02] ;
qcom,fs-curr-ua=<30000>;
qcom,mdss-dsi-bl-max-level=<3640>; 3640=4095*80/90 3路最大是90mA
代码:\kerne\msm-3.18\drivers\leds\leds-qpnp-wled.c
背光亮度设置
static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
debug:/sys/class/leds/wled
文档:80_NT 391_2X_PM I 8952_Hardware_Register_Description.pdf
寄存器:0x0001D977
第一张图和第二张图要结合在一起看,一共是8+4=12位数来表示,就是最大值是4095
当FSC(应该是dts i的qcom,fs-curr-ua配置)
30mA时:对于低8位30/4095=0.00732…即LSB=7.3uA
对于高3位7.3uA*256 (左移8位) =1.875mA
切换界面屏闪 ro.qualcomm.cabl 根据颜色调节亮度
切换界面屏闪
device/qcom/msm8916_64/system.prop中
#System property for cabl
#ro.qualcomm.cabl=2
ro.qualcomm.cabl=0
内容适应背光技术(Content Adaptive Brightness Control - CABC)
cabl :Content Adaptive BackLight
CABC就是通过增加内容灰阶标准同时降低背光亮度来达到省功耗的目的。
CABC主要有四种状态:(1)Off Mode,CABC功能全部关闭;(2)UI Image Mode,优化显示UI图片时的功耗,尽可能的保证图片质量的同时可省10%的功耗;(3)Still Image Mode,优化显示静态图片时的功耗,该模式下图片质量损耗在可接受的范围内,同时可省30%的功耗;(4)Moving Image Mode,优化显示动态图片时的功耗,该模式下会最大限度的降低功耗,但是会带来图片质量的损耗,可省30%+的功耗。
显示屏旋转180度
device/qcom/msm8916_64/system.prop
#enable game colocation feature
sched.colocate.enable=1persist.radio.lw_enabled=truero.sf.lcd_density=240
+ro.panel.mountflip=3
在lk中旋转180度
static struct panel_config tv108qdm_nh0_video_panel_data = {"qcom,mdss_dsi_tv108qdm_nh0_video", "dsi:0:", "qcom,mdss-dsi-panel",10, 0, "DISPLAY_1", 3, 0, 60, 0, 0, 1, 0, 0, 0, 0, 0, 11, 0, 0, "qcom,mdss_dsi_tv108qdm_nh0_video"
};
panel_orientation设置3,bit(1)表示左右翻转,bit(2)为上下翻转
如果是双mipi屏还有配置panel_operating_mode,加上SPLIT_DISPLAY_FLAG,否则只配置panel_orientation为0x3会出现每一路mipi图像各自旋转180度造成画面分裂,配置SPLIT_DISPLAY_FLAG的效果就是双mipi整体旋转180度
双mipi控制器配置
//一般点单mipi的屏默认使用的是A路mipi,如下配置是选择B路mipi点屏
static struct panel_config b101qan01_dualdsi_video_panel_data = {"qcom,mdss_dsi_boe_incell_ft8201_video", "dsi:1:", "qcom,mdss-dsi-panel",//32这个参数会决定在cmdline中传递split_dsi,dual_dsi,single_dsi其中一个10, 0, "DISPLAY_2", 0, 0, 60, 0, 0, 0, 1, 0, 0, 0, 0, 32, 0, 0, "qcom,mdss_dsi_boe_incell_ft8201_sec_video"
};
调试异常问题
1、硬件正常,LK和kernel的配置文件正确情况下,双mipi屏幕只显示一半
static int init_panel_data(struct panel_struct *panelstruct,struct msm_panel_info *pinfo,struct mdss_dsi_phy_ctrl *phy_db)
{
...
case TV105WUM_AS0_VIDEO_PANEL:dprintf(CRITICAL, "hjt TV105WUM_AS0_VIDEO_PANEL.\n");panelstruct->paneldata = &tv105wum_as0_video_panel_data;panelstruct->paneldata->panel_operating_mode = DST_SPLIT_FLAG | //可能缺少配置这一项SPLIT_DISPLAY_FLAG | DUAL_DSI_FLAG;
...
2、显示屏在LK中的头文件配置可能会影响开机动画
3、显示屏kernel可以显示,开机LK阶段不显示
/*---------------------------------------------------------------------------*/
/* Panel configuration */
/*---------------------------------------------------------------------------*/static struct panel_config tv105wum_as0_video_panel_data = {"qcom,mdss_dsi_tv105wum_as0_video", "dsi:0:", "qcom,mdss-dsi-panel",10, 0, "DISPLAY_1", 0, 0, 60, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, NULL
}; //“1”表示panel_broadcast_mode,似乎是/* Enable command mode before sending the commands. */
根据panel_broadcast_mode,mipi读函数参数mipi->broadcast不同
mdss_dsi_cmds_tx(mipi,&ili9881_manufacture_id_page1, 1,mipi->broadcast);
内核对应的dtsi配置
//配置dsi slave controller为触发模式,master controller下发指令后slave controller再发送
qcom,cmd-sync-wait-broadcast;
使用该配置会导致下发灭屏指令off command时出现超时错误打印
[ 45.700740] mdss_dsi_cmds2buf_tx: failed to call cmd_dma_tx for cmd = 0x28
[ 45.700742] mdss_dsi_cmds_tx: failed to call
需要配置off command为高速模式,具体原因未知
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
4、双mipi配置msm8976-qrd-skun.dtsi问题
&mdss_dsi0 {pinctrl-names = "mdss_default", "mdss_sleep";pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;qcom,dsi-pref-prim-pan = <&dsi_hx084jr002_vid>; //不修改该参数出现kernel无法正确读取对应lcd的dtsi配置qcom,platform-enable-gpio = <&msm_gpio 109 0>;qcom,platform-reset-gpio = <&msm_gpio 25 0>;
};&mdss_dsi1 {pinctrl-names = "mdss_default", "mdss_sleep";pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;qcom,dsi-pref-prim-pan = <&dsi_hx084jr002_vid>; //不修改该参数出现kernel无法正确读取对应lcd的dtsi配置qcom,platform-enable-gpio = <&msm_gpio 109 0>;qcom,platform-reset-gpio = <&msm_gpio 25 0>;
};
5、kernel下mipi数据通道波形问题
qcom,mdss-dsi-hbp-power-mode;
6、调节亮度条背光闪烁,或者亮度变化不平滑
可能与pwm频率有关
qcom,mdss-dsi-bl-pmic-pwm-frequency = <1000>;
假设亮度每提高一级占空比变化0.3%,pwm周期为40μs,也就是说亮度提高一级在pwm的一个周期中保持高电平的时间就会延长0.12μs。但是在sdm835平台,经过计算得到pwm定时器精度约为0.64μs,所以亮度要提高0.64/0.12≈5个级别才能使pwm占空比产生变化,从而造成亮度调节不平滑的感觉,所以降低pwm频率使得0.12μs这个值大于pwm定时器精度约为0.64μs,可以改善该问题。
7、显示屏能显示但偶尔画面抖动
增大HorizontalFrontPorch值会有所改善
8、ESD防静电
高通的esd功能也是通过dtsi来配置的。如:
qcom,esd-check-enabled;qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A];//read regqcom,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 valueqcom,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状态;
9、进入fastboot模式开机后第一次灭屏会闪一下花屏,第二次灭屏和重启开机第一次灭屏都没出现
在dtsi中加上以下参数可以修复,原因未知
qcom,mdss-dsi-tx-eot-append;
10、开机闪黑色条纹,休眠唤醒后无现象
mipi dsi clk 存在两种工作模式,一种是连续时钟模式,传输过程不会切换 LP 状态;另一种是非连续时钟信号模式,每传输完一帧图像数据,帧 blanking 时将会切换为 LP 状态。rm69380的amoled屏在xbl使用了第一种clk模式,内核用了第二种clk模式,所以开机花屏,休眠唤醒恢复。
qcom,mdss-dsi-force-clock-lane-hs //连续时钟模式,传输过程不会切换 LP 状态