发生异常的现象:

msm8953 lcd在快速亮灭的情况下背光概率性休眠不灭;测量高通pwm,发现正常的时候pwm的管脚LCM_BL_PWM为低电平,失败的时候为高电平;

根据原理图:

mpp是什么?
mpp是基于电源pmic的管脚,也叫做多功能管脚;MPP的全称是Multi Purpose Pin;可以做电源、gpio、ADC、PWM、SINK等功能。

背光的控制方式:

  1. LCD控制IC支持动态背光控制功能(CABC)通过解析图像的直方图动态改变输出PWM的占空比从而动态调节LCD的背光,在不改变图像显示效果的情况下降低功耗,PMIC根据CABC的占空比控制背光输出电压;
  2. 背光控制部分不经过PMIC而是通过一颗单独的带有boost转换功能的LED驱动器如LM3630A,该芯片通过PWM调节亮度。

我们使用的就是第一种方式;

通过soc->pmi8950(内部pwm)->mpp3的方式去控制。

lcd背光控制调用流程:

首先,我们用的是mipi接口,所以lcd显示驱动是在mdss_dsi.c中,pwm驱动控制是在pwm-qpnp.c文件中(kernel\msm-3.18\drivers\pwm);

mdss_dsi.c文件中,具体在哪里调用到背光函数呢?

根据打印log,可以知道背光控制函数mdss_dsi_panel_bl_ctrl

mdss_dsi_panel_bl_ctrl这个函数是在mdss_dsi_panel.c文件中;

调用顺序如下:
mdss_dsi_ctrl_probe -- >
  mdss_dsi_config_panel -- >
    mdss_dsi_panel_init -- >
     ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl;

根据mdss_dsi_panel_bl_ctrl函数:

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;}
}

我们进入mdss_dsi_panel_bklt_pwm函数来看看:

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;}....
}

进入pwm_disable函数,这里有调用了一个回调函数:

/*** 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_disablepwm-qpnp.c文件中找到相应的函数和函数集):

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);这个函数中来:

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);

保存了上一次亮屏的时候的电平值;所以只要把这段语句去掉,在快速闪灭屏的时候,灭屏就不会出现背光不灭的情况,这是因为寄存器没有写好前,就保存亮屏的高电平值;

LCD背光驱动

qpnp_lpg_init进入probe函数中,spmi驱动是什么呢?参考这篇文章:
SPMI理解
其实简单理解spmi就是一个通讯协议;

static int qpnp_pwm_probe(struct spmi_device *spmi)
{struct qpnp_pwm_chip    *pwm_chip;int         rc;pwm_chip = kzalloc(sizeof(*pwm_chip), GFP_KERNEL);if (pwm_chip == NULL) {pr_err("kzalloc() failed.\n");return -ENOMEM;}spin_lock_init(&pwm_chip->lpg_lock);pwm_chip->spmi_dev = spmi;dev_set_drvdata(&spmi->dev, pwm_chip);rc = qpnp_parse_dt_config(spmi, pwm_chip);if (rc) {pr_err("Failed parsing DT parameters, rc=%d\n", rc);goto failed_config;}pwm_chip->chip.dev = &spmi->dev;pwm_chip->chip.ops = &qpnp_pwm_ops;    pwm_chip->chip.base = -1;pwm_chip->chip.npwm = 1;rc = pwmchip_add(&pwm_chip->chip);if (rc < 0) {pr_err("pwmchip_add() failed: %d\n", rc);goto failed_insert;}if (pwm_chip->channel_owner)pwm_chip->chip.pwms[0].label = pwm_chip->channel_owner;pr_debug("PWM device sid:%d channel:%d probed successfully\n",spmi->sid, pwm_chip->channel_id);return 0;failed_insert:kfree(pwm_chip->lpg_config.lut_config.duty_pct_list);
failed_config:dev_set_drvdata(&spmi->dev, NULL);kfree(pwm_chip);return rc;
}

pwm_chip->chip.ops = &qpnp_pwm_ops;注册相应的回调函数;

patch地址

patch地址

转载于:https://www.cnblogs.com/linhaostudy/p/9173117.html

高通LCD的pwm背光驱动相关推荐

  1. afe 高通_高通ASOC中的machine驱动

    ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

  2. 高通LCD bring up流程

    高通LCD bring up流程 前言 kernel 添加LCD的dtsi 将屏的dtsi放到平台的dtsi中 电压配置 lk 添加屏的.h文件 Panel configuration Panel r ...

  3. Linux之PWM背光驱动

    Linux之PWM背光驱动 | DD'NotesLinux之PWM背光驱动概述 Platform: RK3588/RK356X OS: Linux Kernel: v5.10 通过PWM调光的背光一般 ...

  4. 高通开发系列 - ALSA声卡驱动中音频通路kcontrol控件

    By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! ...

  5. 高通-LCD驱动框架简述

    LCD驱动框架简述 目录 一 LCD硬件结构框架 二 不同的硬件结构LCD的驱动框架简述 三 RGB接口LCD驱动简述(以户外手机(Android5.1)为例) 3.1 硬件线路框架 3.2 软件驱动 ...

  6. 高通LCD背光控制软件简析

    背光控制驱动分析 1,Wled backlight 在driver/leds/目录下存放着系统背光以及LED灯的驱动程序,通常需要在驱动程序中构造好struct led_classdev结构体,用来描 ...

  7. 高通SDX12:USB3.0驱动初始化代码分析

    1. Code Overview 高通SDX12平台使用synopsys dwc3的USB3.0控制器IP.早期的初始化是分两部分进行的,一个在高通官方提供的驱动中初始化,位于kernel/msm-5 ...

  8. 高通平台 input类 sensor驱动分析 : 光感/距感 stk3x1x driver分析

    stk3x1x driver分析 1:注册驱动 定义 i2c_driver static struct i2c_driver stk_ps_driver = {.driver = {.name = D ...

  9. [Android驱动] 高通 Q PNP Flash LED驱动 及上层调用的流程

    直白的记录一下,翻译的文件来自kernel: kernel/documentation/devicetree/bindings/leds/leds-qpnp-flash.txt ----------- ...

最新文章

  1. 2021年大数据常用语言Scala(十六):基础语法学习 列表 List 重点掌握
  2. android 反编译_Android 反编译实战
  3. nginx php跳转url参数,NGINX 中把url中的内容当初参数处理
  4. solver.prototxt参数说明(一)
  5. C语言笔试常考知识点
  6. Standard C Library - 思维火花 - 博客频道 - CSDN.NET
  7. 使用iScroll时,input等不能输入内容的解决方法
  8. 《Kotlin项目实战开发》第5章 函数与函数式编程
  9. C++基础——bitset与string的相互转化
  10. 示波器1m和50欧姆示阻抗匹配_为什么示波器阻抗一般是1M欧或者50欧
  11. Json对象直接存取数据库
  12. 多个容器一起打包_Docker从入门到掉坑(三):容器太多,操作好麻烦
  13. java 用户行为记录_大型网站用户行为记录的一个实现
  14. MongoDB下载安装教程(Windows)
  15. 我不建议大家随便跳槽
  16. R语言使用min函数获得向量数据的最小值
  17. 斐波那契数列python编程_fibonacci数列-斐波那契数列-python编程
  18. DRM dumb,prime介绍
  19. Docute 创建文档网站(docute v3)
  20. FastDFS分布式文件系统(详细入门级介绍)

热门文章

  1. It's a start!
  2. 软件测试技术 homework2
  3. window 系统上传文件到linux 系统出现dos 格式换行符
  4. 多线程条件变量(pthread_cond_wait)用法
  5. BNUOJ 4358 左手定则 搜索
  6. ubuntu 下mysql的常用命令
  7. docker-ce安装
  8. C语言指针,申请、释放内存,线程
  9. SQL Server-数据类型(七)
  10. 利用阿里云OSS对文件进行存储,上传等操作