高通LCD的pwm背光驱动
發(fā)生異常的現(xiàn)象:
msm8953 lcd在快速亮滅的情況下背光概率性休眠不滅;測量高通pwm,發(fā)現(xiàn)正常的時候pwm的管腳LCM_BL_PWM為低電平,失敗的時候為高電平;
根據(jù)原理圖:
mpp是什么?
mpp是基于電源pmic的管腳,也叫做多功能管腳;MPP的全稱是Multi Purpose Pin;可以做電源、gpio、ADC、PWM、SINK等功能。
背光的控制方式:
我們使用的就是第一種方式;
通過soc->pmi8950(內(nèi)部pwm)->mpp3的方式去控制。
lcd背光控制調(diào)用流程:
首先,我們用的是mipi接口,所以lcd顯示驅(qū)動是在mdss_dsi.c中,pwm驅(qū)動控制是在pwm-qpnp.c文件中(kernel\msm-3.18\drivers\pwm);
在mdss_dsi.c文件中,具體在哪里調(diào)用到背光函數(shù)呢?
根據(jù)打印log,可以知道背光控制函數(shù)mdss_dsi_panel_bl_ctrl;
mdss_dsi_panel_bl_ctrl這個函數(shù)是在mdss_dsi_panel.c文件中;
調(diào)用順序如下:
mdss_dsi_ctrl_probe -- >
mdss_dsi_config_panel -- >
mdss_dsi_panel_init -- >
ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl;
根據(jù)mdss_dsi_panel_bl_ctrl函數(shù):
static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,u32 bl_level) {....../** Some backlight controllers specify a minimum duty cycle* for the backlight brightness. If the brightness is less* than it, the controller can malfunction.*/if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0))bl_level = pdata->panel_info.bl_min;switch (ctrl_pdata->bklt_ctrl) {case BL_WLED:led_trigger_event(bl_led_trigger, bl_level);break;case BL_PWM:mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level);break;case BL_DCS_CMD:if (!mdss_dsi_sync_wait_enable(ctrl_pdata)) {mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);break;}/** DCS commands to update backlight are usually sent at* the same time to both the controllers. However, if* sync_wait is enabled, we need to ensure that the* dcs commands are first sent to the non-trigger* controller so that when the commands are triggered,* both controllers receive it at the same time.*/sctrl = mdss_dsi_get_other_ctrl(ctrl_pdata);if (mdss_dsi_sync_wait_trigger(ctrl_pdata)) {if (sctrl)mdss_dsi_panel_bklt_dcs(sctrl, bl_level);mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);} else {mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);if (sctrl)mdss_dsi_panel_bklt_dcs(sctrl, bl_level);}break;default:pr_err("%s: Unknown bl_ctrl configuration\n",__func__);break;} }我們進(jìn)入mdss_dsi_panel_bklt_pwm函數(shù)來看看:
static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) {int ret;u32 duty;u32 period_ns;if (ctrl->pwm_bl == NULL) {pr_err("%s: no PWM\n", __func__);return;}if (level == 0) {if (ctrl->pwm_enabled) {ret = pwm_config_us(ctrl->pwm_bl, level,ctrl->pwm_period);if (ret)pr_err("%s: pwm_config_us() failed err=%d.\n",__func__, ret);pwm_disable(ctrl->pwm_bl);}ctrl->pwm_enabled = 0;return;}.... }進(jìn)入pwm_disable函數(shù),這里有調(diào)用了一個回調(diào)函數(shù):
/*** pwm_disable() - stop a PWM output toggling* @pwm: PWM device*/ void pwm_disable(struct pwm_device *pwm) {if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) {pwm->chip->ops->disable(pwm->chip, pwm);} }搜索之后,可以在qpnp_pwm_disable(pwm-qpnp.c文件中找到相應(yīng)的函數(shù)和函數(shù)集):
static struct pwm_ops qpnp_pwm_ops = {.enable = qpnp_pwm_enable,.disable = qpnp_pwm_disable,.config = qpnp_pwm_config,.free = qpnp_pwm_free,.owner = THIS_MODULE, };/*** qpnp_pwm_disable - stop a PWM output toggling* @pwm_chip: the PWM chip* @pwm: the PWM device*/ static void qpnp_pwm_disable(struct pwm_chip *pwm_chip,struct pwm_device *pwm) {struct qpnp_pwm_chip *chip = qpnp_pwm_from_pwm_chip(pwm_chip);unsigned long flags;int rc = 0;spin_lock_irqsave(&chip->lpg_lock, flags);if (QPNP_IS_PWM_CONFIG_SELECTED(chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED)rc = qpnp_lpg_configure_pwm_state(chip,QPNP_PWM_DISABLE);else if (!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED))rc = qpnp_lpg_configure_lut_state(chip,QPNP_LUT_DISABLE);if (!rc)chip->enabled = false;spin_unlock_irqrestore(&chip->lpg_lock, flags);if (rc)pr_err("Failed to disable PWM channel: %d\n",chip->channel_id); }來到qpnp_lpg_configure_pwm_state(chip, QPNP_PWM_DISABLE);這個函數(shù)中來:
static int qpnp_lpg_configure_pwm_state(struct qpnp_pwm_chip *chip,enum qpnp_pwm_state state) {struct qpnp_lpg_config *lpg_config = &chip->lpg_config;u8 value, mask;int rc;bool test_enable;if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {if (state == QPNP_PWM_ENABLE)value = QPNP_ENABLE_PWM_MODE_ONLY_SUB_TYPE;elsevalue = QPNP_DISABLE_PWM_MODE_ONLY_SUB_TYPE;mask = QPNP_PWM_MODE_ONLY_ENABLE_DISABLE_MASK_SUB_TYPE;} else {if (state == QPNP_PWM_ENABLE)value = qpnp_enable_pwm_mode(chip);elsevalue = QPNP_DISABLE_PWM_MODE(chip);mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK;if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)mask |= QPNP_EN_PWM_OUTPUT_MASK;}if (chip->in_test_mode) {test_enable = (state == QPNP_PWM_ENABLE) ? 1 : 0;rc = qpnp_dtest_config(chip, test_enable);if (rc)pr_err("Failed to configure TEST mode\n");}pr_debug("pwm_enable_control: %d\n", value);rc = qpnp_lpg_save_and_write(value, mask,&chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],SPMI_LPG_REG_ADDR(lpg_config->base_addr,QPNP_ENABLE_CONTROL), 1, chip);if (rc)goto out;/** Due to LPG hardware bug, in the PWM mode, having enabled PWM,* We have to write PWM values one more time.*/if (state == QPNP_PWM_ENABLE)return qpnp_lpg_save_pwm_value(chip); out:return rc; }qpnp_lpg_save_pwm_value會保存著上一次pwm的高低電平的值,
rc = qpnp_lpg_save_and_write(value, mask,&chip->qpnp_lpg_registers[QPNP_PWM_VALUE_LSB],SPMI_LPG_REG_ADDR(lpg_config->base_addr,QPNP_PWM_VALUE_LSB), 1, chip);保存了上一次亮屏的時候的電平值;所以只要把這段語句去掉,在快速閃滅屏的時候,滅屏就不會出現(xiàn)背光不滅的情況,這是因為寄存器沒有寫好前,就保存亮屏的高電平值;
LCD背光驅(qū)動
在qpnp_lpg_init進(jìn)入probe函數(shù)中,spmi驅(qū)動是什么呢?參考這篇文章:
SPMI理解
其實簡單理解spmi就是一個通訊協(xié)議;
pwm_chip->chip.ops = &qpnp_pwm_ops;注冊相應(yīng)的回調(diào)函數(shù);
patch地址
patch地址
轉(zhuǎn)載于:https://www.cnblogs.com/linhaostudy/p/9173117.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的高通LCD的pwm背光驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS表单验证,最详细步骤,代码
- 下一篇: 误打误撞的模板字符串