=======pwm频率修改

当前ALPS branch上,disp_pwm driver采用的是turnkey code,不同芯片型号,或不同branch,disp_pwm频率设定可能会有差异,因此客户有时会遇到如下问题:

(1)如何修改lk和kernel下disp_pwm频率?

(2)如何修改disp_pwm clock source?

(3)为什么修改dts无效?

首先 介绍下pwm

[SOLUTION]

对于以上几种问题,归根结底就是:disp_pwm频率如何设定的问题。下面就概括一下,当前遇到的设定disp_pwm频率(fre)的方法。

1、Disp_PWM频率计算方法

fre=source clock/(divider+1)/(period+1)

source clock:时钟源,26MHz/104MHz/125MHz/62.5MHz/15.625/……,与IC design有关。若没有特别说明,一般是26MHz

divider:对source clock除频

period:disp_pwm周期,默认是1023

因此,想达到特定的fre,只需要适当的修改source、divider参数即可(period一般不建议修改)

2、disp_pwm频率代码位置

lk: ddp_pwm.c/disp_pwm_init()

kernel: ddp_pwm.c/disp_pwm_config_init()

3、dts配置方式方式

若lk & kernel都从dts读取disp_pwm参数,则只需针对性修改dts即可

led6:led@6 {

compatible = "mediatek,lcd-backlight";

led_mode = <5>;

data = <1>;

pwm_config = <0 0 0 0 0>;

};

pwm_config参数:clock source、divider、low_duration、high_duration、pmic_pad。与dts配置中pwm_config = <0 0 0 0 0>参数一一对应。只需关注前面2个参数即可

如上设定:clock source=26MHz,divider=0

fre=26MHz/1/1024=25.29KHz

3、不使用dts

divider: PWM_DEFAULT_DIV_VALUE   ///设定成你需要的值

CLK_CFG_1 bit[2:0]:0/1/2/……      ///选择不同的source

所以遇到频率无法修改成功的问题,先看是使用dts配置,还是disp_pwm driver init时hardcode写死,再相对应的修改设定。

5、kernel下source clock切换问题

【MT8788】ALPS04829940

说明:MT8788(MT8183,Sylvia)芯片上,disp_pwm source有如下5种:

0x100000B0

CLK_CFG_7

bit[2:0]

0

clk26m

26MHz

1

univpll_d3_d4

104MHz

2

osc_d2

125MHz

3

osc_d4

62.5MHz

4

osc_d16

15.625MHz

kernel下使用dts设定,ddp_pwm.c/disp_pwm_config_init(),会调用disp_pwm_set_pwmmux()函数对如上的5种clock source进行重新mapping,使用mapping后的clock ID进行配置。8788上我司目前使用的CLK_CFG_7

例如,想设定source=26MHz,则dts中应该这样填写:pwm_config = <4 0 0 0 0>;

寄存器定义

mapping后的clock ID

clock source

预期clk(mhz)

bit[2:0]=0

4

clk26m

26

bit[2:0]=1

3

univpll_d3_d4

104

bit[2:0]=2

2

osc_d2

125

bit[2:0]=3

1

osc_d4

62.5

bit[2:0]=4

0

osc_d16

15.625

=======pwm背光亮度调节

mtk方案并没有使用原生.level = EARLY_SUSPEND_LEVEL_DISABLE_FB去设置亮度,而是通过直接写属性节点brightness的方式,hal层通过读写此节点从而控制亮度

想要知道原生亮度控制方式可以参考此此链接:https://www.cnblogs.com/reality-soul/p/4757728.html

1 hal层控制

节点路径:sys/devices/platform/leds-mt65xx/leds/lcd-backlight/brightness

上层调用流程课参考http://www.52rd.com/Blog/Detail_RD.Blog_apu981_69251.html,此博客已做了解释

2 kernel如何响应亮度

首先看看驱动层brightness设备节点如何创建的

kernel-4.4/drivers/leds/led-class.c模块中会执行static DEVICE_ATTR_RW(brightness);这里要说下这个DEVICE_ATTR_RW宏

#define DEVICE_ATTR_RW(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RW(_name)

struct device_attribute dev_attr_(wakealarm) = __ATTR_RW(wakealarm);

#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO),    _name##_show, _name##_store)    
             
struct device_attribute dev_attr_(wakealarm) =  __ATTR(wakealarm, (S_IWUSR | S_IRUGO),wakealarm_show, wakealarm_store)

#define __ATTR(_name, _mode, _show, _store) {                
    .attr = {.name = __stringify(_name),                
         .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        
    .show    = _show,                        
    .store    = _store,                        
}

struct device_attribute dev_attr_(wakealarm) = {                
    .attr = {.name = __stringify(wakealarm),                
         .mode = VERIFY_OCTAL_PERMISSIONS((S_IWUSR | S_IRUGO)) },        
    .show    = wakealarm_show,                        
    .store    = wakealarm_store,                
}

最终这个宏 static DEVICE_ATTR_RW(wakealarm);  生成了

struct device_attribute dev_attr_(wakealarm) = {                
    .attr = {.name = __stringify(wakealarm),                
         .mode = VERIFY_OCTAL_PERMISSIONS((S_IWUSR | S_IRUGO)) },        
    .show    = wakealarm_show,                        
    .store    = wakealarm_store,                
}

由此可以知道DEVICE_ATTR_RW(brightness)也会去指定一个store函数即brightness_store,在写brightness时会调用brightness_store,一搜果然存在

static ssize_t brightness_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
{       struct led_classdev *led_cdev = dev_get_drvdata(dev);unsigned long state;ssize_t ret;mutex_lock(&led_cdev->led_access);if (led_sysfs_is_disabled(led_cdev)) {ret = -EBUSY;goto unlock;}ret = kstrtoul(buf, 10, &state);/*上层写数据时会保存到buf里*/if (ret)goto unlock;if (state == LED_OFF)led_trigger_remove(led_cdev);led_set_brightness(led_cdev, state);/*当上层写节点时会调用led_set_brightness,我们的brightness也会传下去*/ret = size;
unlock: mutex_unlock(&led_cdev->led_access);return ret;
}

接下来led_set_brightness(leds/led-core.c) 会调用led_set_brightness_async(led_cdev, brightness)这里面还涉及到soft-blink可以不管,我们主要分析亮如何传进去的,以及驱动如何把0-255的亮度转化成pwm的占空比。接下来看下led_set_brightness_async干了啥

//led_set_brightness_async定义在leds.h里
static inline void led_set_brightness_async(struct led_classdev *led_cdev,enum led_brightness value)
{value = min(value, led_cdev->max_brightness);led_cdev->brightness = value;if (!(led_cdev->flags & LED_SUSPENDED))led_cdev->brightness_set(led_cdev, value);//接着brightness_set,这个        //brightness_set在哪儿定义
}

想要知道brightness_set如何来的就要看kernel-4.4/driver/misc/mediatek/leds/mtk_leds_drv.c中的probe函数

static int mt65xx_leds_probe(struct platform_device *pdev){int i;int ret;/* rc; */struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list();if (cust_led_list == NULL) {LEDS_DRV_INFO("%s: get dts fail.\n", __func__);return -1;}................//省略中间代码g_leds_data[i]->cust.mode = cust_led_list[i].mode;g_leds_data[i]->cust.data = cust_led_list[i].data;g_leds_data[i]->cust.name = cust_led_list[i].name;g_leds_data[i]->cdev.name = cust_led_list[i].name;g_leds_data[i]->cust.config_data = cust_led_list[i].config_data;        g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;//brightness_set实际上        //是调用的    //mt65xx_led_set,不清楚的需要去了解下Linux设备与驱动g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);}

mt65xx_led_set接下来会调用mt_mt65xx_led_set 进而调用mt_mt65xx_led_set_cust(misc/mediatek/leds/mt6771/mtk_leds.c)

//这个函数里重点讲下上层0-255级数如何跟驱动pwm级数(0-1024)发生联系的
void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)
{struct mt65xx_led_data *led_data =container_of(led_cdev, struct mt65xx_led_data, cdev);/* unsigned long flags; *//* spin_lock_irqsave(&leds_lock, flags); */if (disp_aal_is_support() == true) {if (led_data->level != level) {led_data->level = level;if (strcmp(led_data->cust.name, "lcd-backlight") != 0) {LEDS_DEBUG("Set NLED directly %d at time %lu\n",led_data->level, jiffies);schedule_work(&led_data->work);} else {if (level != 0&& level * CONFIG_LIGHTNESS_MAPPING_VALUE < 255) {level = 1;} else {level =(level * CONFIG_LIGHTNESS_MAPPING_VALUE) /255;}backlight_debug_log(led_data->level, level);disp_pq_notify_backlight_changed((((1 <<MT_LED_INTERNAL_LEVEL_BIT_CNT)- 1) * level +127) / 255);//mt_mt65xx_led_set_cust第二个参数就是即将传给驱动的真正值,1《MT_LED_INTERNAL_LEVEL_BIT_CNT是固定的1023,这个计算公式将上层brightness跟pwm(0-1023)对应起来了,至于为什么时驱动中pwm级数为0-1023驱动有涉及,查找一下就知道了 disp_aal_notify_backlight_changed((((1 <<MT_LED_INTERNAL_LEVEL_BIT_CNT)- 1) * level +127) / 255);}}} else {.............}/* spin_unlock_irqrestore(&leds_lock, flags); */}
/* if(0!=aee_kernel_Powerkey_is_press()) */
/* aee_kernel_wdt_kick_Powkey_api("mt_mt65xx_led_set",WDT_SETBY_Backlight); */
}
int mt_mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level)
{#ifdef CONFIG_MTK_PWMstruct nled_setting led_tmp_setting = { 0, 0, 0 };int tmp_level = level;unsigned int BacklightLevelSupport =Cust_GetBacklightLevelSupport_byPWM();#endifstatic bool button_flag;switch (cust->mode) {//这些mode实在dts中定义的,8788背光使用的是     //MT65XX_LED_MODE_CUST_BLS_PWMcase MT65XX_LED_MODE_GPIO:LEDS_DEBUG("brightness_set_cust:go GPIO mode!!!!!\n");return ((cust_set_brightness) (cust->data)) (level);case MT65XX_LED_MODE_PMIC:...................// 省略中间代码case MT65XX_LED_MODE_CUST_BLS_PWM:if (strcmp(cust->name, "lcd-backlight") == 0)bl_brightness_hal = level;#ifdef MET_USER_EVENT_SUPPORTif (enable_met_backlight_tag())output_met_backlight_tag(level);#endifreturn ((cust_set_brightness) (cust->data)) (level);//此函数才是这一些列调用 //中最关键的......................
}

((cust_set_brightness) (cust->data)) (level)这个就是一个函数 指针,使用typedef 定义了,那这个函数到底指向那里了?它指向vendor/.../cust_leds.c定义的cust_mt65xx_led结构体中,如下:

static struct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {{"red",               MT65XX_LED_MODE_PMIC, MT65XX_LED_PMIC_NLED_ISINK3,{255}},{"green",             MT65XX_LED_MODE_PMIC, MT65XX_LED_PMIC_NLED_ISINK1,{25}},{"blue",              MT65XX_LED_MODE_PMIC, MT65XX_LED_PMIC_NLED_ISINK2,{25}},{"jogball-backlight", MT65XX_LED_MODE_NONE, -1,{0,0,0,0,0}},{"keyboard-backlight",MT65XX_LED_MODE_NONE, -1,{0,0,0,0,0}},{"button-backlight",  MT65XX_LED_MODE_NONE, -1,{0,0,0,0,0}},{"lcd-backlight",         MT65XX_LED_MODE_CUST_BLS_PWM, (int)disp_bls_set_backlight,{1,0,0,0,0}},
};//MT65XX_LED_MODE_CUST_BLS_PWM模式下设置会调用disp_bls_set_backlight函数然后继续调用disp_pwm_set_backlight->disp_pwm_set_backlight_cmdq(/misc/mediatek/video/common/pwm10/ddp_pwm.c)
int disp_pwm_set_backlight_cmdq(enum disp_pwm_id_t id, int level_1024, void *cmdq)
{
#ifndef CONFIG_FPGA_EARLY_PORTING/* PWM is excluded from FPGA bitfile */unsigned long reg_base;int old_pwm;int index;int abs_diff;int max_level_1024;if ((DISP_PWM_ALL & id) == 0) {PWM_ERR("[ERROR] disp_pwm_set_backlight_cmdq: invalid PWM ID = 0x%x", id);return -EFAULT;}index = index_of_pwm(id);//id为0选用pwm0输出   1 pwm1输出/* we have to change backlight after config init or max backlight changed */old_pwm = atomic_xchg(&g_pwm_backlight[index], level_1024);if (old_pwm != level_1024 || atomic_cmpxchg(&g_pwm_is_change_state[index], 1, 0) == 1) {abs_diff = level_1024 - old_pwm;if (abs_diff < 0)abs_diff = -abs_diff;if (old_pwm == 0 || level_1024 == 0 || abs_diff > 64) {/* To be printed in UART log */disp_pwm_log(level_1024, MSG_LOG);if (old_pwm != level_1024) {/* Print information if backlight is changed */PWM_NOTICE("disp_pwm_set_backlight_cmdq(id = 0x%x, level_1024 = %d), old = %d",id, level_1024, old_pwm);}} else {disp_pwm_log(level_1024, MSG_LOG);}max_level_1024 = disp_pwm_get_max_backlight(id);if (level_1024 > max_level_1024)level_1024 = max_level_1024;else if (level_1024 < 0)level_1024 = 0;level_1024 = disp_pwm_level_remap(id, level_1024);PWM_NOTICE("disp_pwm_set_backlight_cmdq max_level_1024 : %d\n",max_level_1024);reg_base = pwm_get_reg_base(id);//读取pwm0的基地址  8788上是1 1 00E000PWM_NOTICE("disp_pwm_set_backlight_cmdq(reg_base = 0x%x, DISP_PWM_CON_1_OFF = 0x%x), reg = 0x%x",reg_base, DISP_PWM_CON_1_OFF, reg_base + DISP_PWM_CON_1_OFF);if (level_1024 > 0) {printk("[lcm][kernel] (unsigned int)(INREG32(reg32)&~(mask))|(val)-->%d\n",(unsigned int)(INREG32(reg_base + DISP_PWM_CON_1_OFF)&~(0x1fff << 16))|(level_1024 << 16));DISP_REG_MASK(cmdq, reg_base + DISP_PWM_CON_1_OFF, level_1024 << 16, 0x1fff << 16);disp_pwm_set_enabled(cmdq, id, 1);} else {/* Avoid to set 0 */DISP_REG_MASK(cmdq, reg_base + DISP_PWM_CON_1_OFF, 1 << 16, 0x1fff << 16);//reg_base + DISP_PWM_CON_1_OFF值为1100E01C DISP_PWM_CON_1寄存器disp_pwm_set_enabled(cmdq, id, 0);      /* To save power */}DISP_REG_MASK(cmdq, reg_base + DISP_PWM_COMMIT_OFF, 1, ~0);DISP_REG_MASK(cmdq, reg_base + DISP_PWM_COMMIT_OFF, 0, ~0);}if (g_pwm_led_mode == MT65XX_LED_MODE_CUST_BLS_PWM &&atomic_read(&g_pwm_is_power_on[index]) == 0 && level_1024 > 0) {/* print backlight once after device resumed */disp_pwm_backlight_status(id, true);}
#endifreturn 0;
}//介绍下如何获取pwm寄存器基地址
#define pwm_get_reg_base(id) ((id == DISP_PWM0) ? DISPSYS_PWM0_BASE : DISPSYS_PWM1_BASE)
传下去的id为0,所以返回DISPSYS_PWM0_BASE,看下这个DISPSYS_PWM0_BASE#define DISPSYS_PWM0_BASE         ddp_get_module_va(DISP_MODULE_PWM0)
这个ddp_get_module_va实际上就是从ddp_module结构体中根据module_id获取地址, 贴出来部分ddp_module代码:
static ddp_module  ddp_modules[DISP_MODULE_NUM] = {....................{DISP_MODULE_PWM0,DISP_T_PWM,"pwm0",0,&ddp_driver_pwm,{"mediatek,disp_pwm0",0x1100e000,158,0,0,0}},{DISP_MODULE_PWM1,DISP_T_PWM,"pwm1",0,NULL,{reg_magic,}},{DISP_MODULE_CONFIG,DISP_T_UNKNOWN,"config",0,NULL,{"mediatek,mmsys_config",0x14000000,0,0,0,0}},.........................
}

pwm寄存器如下

DISP_REG_MASK(cmdq, reg_base + DISP_PWM_CON_1_OFF, level_1024 << 16, 0x1fff << 16);如上所说reg_base + DISP_PWM_CON_1_OFF是pwm1,看看这个寄存器说明

到这里就完全清晰了,DISP_REG_MASK将计算后的pwm级数写进寄存器   转换成占空比(PWM_HIGH_WIDTH/PWM_PERIOD)

mtk8788 pwm频率及背光亮度调节相关推荐

  1. 基于TWL6032 PWM控制液晶背光亮度

    0引言 目前市场上的液晶显示器,都是指LED背光液晶屏,实际上使用了侧面led二极管作为背光源,替代传统的CCFL荧光背光,即采用了LED背光源的TFT液晶屏幕.相比于传统的CCFL冷阴极背光源液晶显 ...

  2. 十二、使用PWM调整LCD背光亮度

    和手机一样,开发板中也带有调整背光亮度的功能. 调整背光亮度依赖于PWM,它通过调节脉冲宽度来控制背光亮度,此方式需要使用PWM驱动.本章将对其进行讲解. 一.用户空间调整背光亮度 一般应用程序可以通 ...

  3. Ubuntu Linux笔记本屏幕背光亮度调节 (转)

    网上有很多在Ubuntu Linux下调节笔记本屏幕亮度的方法,有的调的是亮度但不是背光亮度,有的调背光亮度的方法在我的电脑上不好使--找了半天发现这个方法,适用范围应该比较广(起码在我这里好用). ...

  4. PWM调光方法在LED亮度调节中的应用

    LED 是一种固态电光源, 是一种半导体照明器件,其电学特性具有很强的离散性.它具有体积小.机械强度大.功耗低.寿命长, 便于调节控制及无污染等特征,有极大发展前景的新型光源产品.LED 调光方法的实 ...

  5. linux pwm 调屏_PWM调光方法在LED亮度调节中的应用

    LED 是一种固态电光源, 是一种半导体照明器件,其电学特性具有很强的离散性.它具有体积小.机械强度大.功耗低.寿命长, 便于调节控制及无污染等特征,有极大发展前景的新型光源产品.LED 调光方法的实 ...

  6. ESP8266呼吸灯亮度调节并且实时显示PWM数值

    以下代码实现了ESP8266的网页控制板载LED灯的亮度调节,并且将GPIO_2的PWM数值显示在网页上. 网页显示代码来自: 太极创客http://www.taichi-maker.com/home ...

  7. android手动亮度调节,背光闪烁,自动背光调节

    AAL与CABC背光选择(两种方式控制背光):参考[FAQ05966]89平台支持BB端CABC(即AAL)或LCM端CABC方式控制背光,两种方式使用方法如下[BB端CABC(即AAL)]- 打开功 ...

  8. 笔记本屏幕 亮度 背光调节 工具 c++ 用来解决亮度调节功能键失效问题

    某次windows10 大更新,坑啊,调节亮度的快捷键失效了,多次卸载集显驱动和重新安装没有解决,无奈使用电源管理里面的亮度滑竿几个月后,更新了win10 1803, 本想能解决这个问题,然而...无 ...

  9. [高通MSM8953][Android10]user版本背光亮度无法调节

    文章目录 开发平台基本信息 问题描述 解决方法 开发平台基本信息 芯片: MSM8953 版本: Android 10 kernel: msm-4.9 问题描述 在开发时候遇到了我编译的固件无法调节背 ...

最新文章

  1. python随机抽样numpy_python numpy之np.random的随机数函数使用介绍
  2. 极光推送 java api_JPush极光推送Java服务器端API
  3. Python+OpenCV图像处理之模糊操作
  4. crawler4j源码学习(2):Ziroom租房网房源信息采集爬虫
  5. mysql版本升级对数据的影响_MySQL升级
  6. 《一个会写诗的程序员》 东海光剑
  7. 使用微信即时远程开门
  8. “潜力工作者”会不会成为明年24届秋招统计参数中的受害者?
  9. 短信验证php_php如何实现短信验证
  10. EasyExcel表头校验,表内容校验
  11. ASFF的TensorFlow2实现
  12. 程序员是这样炼成的(2)-选择成熟的柿子还是生柿子
  13. 栾锟数据科学与计算机学院,山东女子学院学生会第四届第一任干部名单
  14. 电脑卡顿,终于解决了多年的电脑卡顿问题
  15. 用手写板向计算机输入汉字是什么技术,使用手写板输入文字的简单介绍
  16. 学科语文方面的论文怎么选题?
  17. 旖旎风景——浪漫烟花(Python实现)
  18. 浅析人们对于企业即时通讯软件的几点误解
  19. Oracle使用SQL实现矩阵转置
  20. 5GC——UE周期性注册和移动更新注册流程

热门文章

  1. 我的时间都去哪里了?
  2. Android Studio——Spinner 修改字体颜色和字体大小
  3. 进程篇——了解Makefile文件
  4. 两化融合主要在哪些方面
  5. prev_permutation函数
  6. 身份证OCR实名认证接口
  7. 04_服务注册Eureka
  8. Fiddler抓手机app的包
  9. window版加密磁盘
  10. Brat 标注工具 配置文件 详细说明