精华内容
下载资源
问答
  • mtk8788 pwm频率背光亮度调节

    千次阅读 2019-11-14 18:15:58
    =======pwm频率修改 当前ALPS branch上,disp_pwmdriver采用的是turnkey code,不同芯片型号,或不同branch,disp_pwm频率设定可能会有差异,因此客户有时会遇到如下问题: (1)如何修改lk和kernel下disp_pwm频率...

    =======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_PWM
            struct nled_setting led_tmp_setting = { 0, 0, 0 };
            int tmp_level = level;
            unsigned int BacklightLevelSupport =
                Cust_GetBacklightLevelSupport_byPWM();
        #endif
            static bool button_flag;
    
            switch (cust->mode) {//这些mode实在dts中定义的,8788背光使用的是     
                                 //MT65XX_LED_MODE_CUST_BLS_PWM
                case 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_SUPPORT
                    if (enable_met_backlight_tag())
                            output_met_backlight_tag(level);
            #endif
                    return ((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 00E000
                    PWM_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);
            }
    #endif
            return 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)

    展开全文
  • MT6571 使用PWM配置背光频率计算

    千次阅读 2016-07-15 16:46:25
    MT6571 使用PWM配置背光频率计算 [DESCRIPTION] MT6571 使用PWM配置背光频率计算 [SOLUTION] 以GPIO52 pin为例: 5.11tp-2.png (14.14 KB, 下载次数: 0 ) 下载附件 保存到相册 2016-5-11 ...

    MT6571 使用PWM配置背光及频率计算

    [DESCRIPTION]

    MT6571 使用PWM配置背光及频率计算

    [SOLUTION]

    以GPIO52 pin为例:

    5.11tp-2.png (14.14 KB, 下载次数: 0 )

    下载附件  保存到相册

    2016-5-11 10:25 上传


    一、将GPIO52在DCT tool配置成PWM1,用于背光。
    static struct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
    {"red", MT65XX_LED_MODE_NONE, -1,{0}},
    {"green", MT65XX_LED_MODE_NONE, -1,{0}},
    {"blue", MT65XX_LED_MODE_NONE, -1,{0}},
    {"jogball-backlight", MT65XX_LED_MODE_NONE, -1,{0}},
    {"keyboard-backlight",MT65XX_LED_MODE_NONE, -1,{0}},
    {"button-backlight", MT65XX_LED_MODE_NONE, -1,{0,0,0,0,0}},
    {"lcd-backlight", MT65XX_LED_MODE_PWM, PWM1, {0,0,0,0,0}},
    };
    1. PWM config data结构体解析
    clock_source: clock source frequency, can be 0/1
    div: clock division, can be any value within 0~7 (i.e. 1/2^(div) = /1, /2,
    /4, /8, /16, /32, /64, /128)
    low_duration: only for BACKLIGHT_LEVEL_PWM_64_FIFO_MODE_SUPPORT
    High_duration: only for BACKLIGHT_LEVEL_PWM_64_FIFO_MODE_SUPPORT
    2. PWM频率计算:
    If BACKLIGHT_LEVEL_PWM_MODE_CONFIG = BACKLIGHT_LEVEL_PWM_256_SUPPORT,
    PWM freq. = clock source / 2^(div) / 256
    If BACKLIGHT_LEVEL_PWM_MODE_CONFIG =
    BACKLIGHT_LEVEL_PWM_64_FIFO_MODE_SUPPORT,
    PWM freq. = clock source / 2^(div) /
    [(High_duration+1)(Level')+(low_duration+1)(64 - Level')]
    = clock source / 2^(div) / [(High_duration+1)*64] (when low_duration =
    High_duration)
    时钟源:
    0: block clock/1625 = 26M/1625 = 16K (MT6571)
    Mode0 Mode1 Mode2 Mode3
    GPIO52 LPD8 PWM_BL PWM_A
    1: block clock = 26M (MT6571)
    Div: 0~7
    例如, in MT6571, PWM_config = {1,1,0,0,0}
    ==> PWM freq. = 26M/2^1/256 = 50.78 KHz ( when
    BACKLIGHT_LEVEL_PWM_256_SUPPORT )
    ==> PWM freq. = 26M/2^1/(0+1)*64 = 203.13 KHz ( when
    BACKLIGHT_LEVEL_PWM_64_FIFO_MODE_SUPPORT )
    波形图如下:

    5.11tp-3.png (366.24 KB, 下载次数: 0 )

    下载附件  保存到相册

    2016-5-11 10:25 上传


    二、将GPIO52在DCT Tool配置成PWM_BL.
    static struct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
    {"red", MT65XX_LED_MODE_NONE, -1,{0}},
    {"green", MT65XX_LED_MODE_NONE, -1,{0}},
    {"blue", MT65XX_LED_MODE_NONE, -1,{0}},
    {"jogball-backlight", MT65XX_LED_MODE_NONE, -1,{0}},
    {"keyboard-backlight",MT65XX_LED_MODE_NONE, -1,{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, {0,0,0,0,0}},
    };
    1. PWM config data结构体解析
    clock_source: clock source frequency, can be 0/1/2/3
    div: clock division, can be any value within 0~1023
    low_duration: non-use
    High_duration: non-use
    pmic_pad: non-use
    2. 频率计算
    PWM freq.= clock source / (div + 1) /1024
    Clock source:
    0: 26 MHz
    1: 104 MHz
    Div: 0~1023
    By default, clock_source = 0 and div = 0 => PWM freq. = 26 KHz
    波形图如下:

    5.11tp-4.png (362.82 KB, 下载次数: 0 )

    下载附件  保存到相册

    2016-5-11 10:25 上传


    更加具体的信息请参考DCC文档:
    MT6571_Backlight_Config_SOP

    展开全文
  • [LED]LCD背光设置为PWM模式的时候,config_data里面的参数如何使用 [DESCRIPTION] LCD背光设置为PWM模式的时候,config_data里面的参数如何使用 [SOLUTION] 配置cust_leds.c参数: static struct cust_mt...

    [LED]LCD背光设置为PWM模式的时候,config_data里面的参数如何使用

    [DESCRIPTION]

    LCD背光设置为PWM模式的时候,config_data里面的参数如何使用

    [SOLUTION]

    配置cust_leds.c参数:
    static struct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
    {"red", MT65XX_LED_MODE_NONE, -1,{0}},
    {"green", MT65XX_LED_MODE_NONE, -1,{0}},
    {"blue", MT65XX_LED_MODE_NONE, -1,{0}},
    {"jogball-backlight", MT65XX_LED_MODE_NONE, -1,{0}},
    {"keyboard-backlight",MT65XX_LED_MODE_NONE, -1,{0}},
    {"button-backlight", MT65XX_LED_MODE_PMIC, MT65XX_LED_PMIC_BUTTON,{0}},
    {"lcd-backlight", MT65XX_LED_MODE_PWM, PWM1,{0,0,0,0,0}},
    };
    LCD 背光
    MT6589
    PWMPWM frequency = src_clk/(div+1)/64/HDuration;
    其中src_clk表示时钟源的频率,div表示分频的参数,HDuration表示每个bit的高电平持续的时钟周期。
    在\alps\mediatek\kernel\drivers\leds\leds.c文件里面的backlight_set_pwm函数通过config_data的参数来决定上面公式里面的参数。
    1. config_data->clock_source = 0表示选择32k的src_clk,config_data->clock_source> 0表示选择52M的src_clk;
    2. config_data->div表示分频的参数;
    3. config_data->High_duration和config_data->Low_duration分别表示每个bit持续的高电平的时钟周期和低电平持续的时钟周期。
    {0,0,0,0,0}src_clk=32kdiv=0HDuration=4; LDuration=4,pmic_pad=0
    32K/(0+1)/64/4=125Hz
    {"lcd-backlight", MT65XX_LED_MODE_PWM, PWM5,{1,1,32,32,1}},
    {1,1,32,32,1}src_clk=52Mdiv=1HDuration=32; LDuration=32,pmic_pad=1
    52M/(1+1)/64/32=12.695KHz
    因为底层设置的背光强度的level最大值是64,所以一个完整的PWM周期的波形如下,需要64bit来表示(T表示时钟周期),每个bit都需要High_duration这个时钟周期表示(Low_duration和High_duration在backlight_set_pwm被设置成是一样的,所以公式里面统一使用High_duration表示)。
    另外PWM的占空比是由kernel层设置的背光强度的level来决定的,level越大,输出波形的占空比越大。

    5.11tp-1.png (50.37 KB, 下载次数: 0 )

    下载附件  保存到相册

    2016-5-11 10:20 上传


    MT6572
    MT6572MT6589src_clkconfig_data->clock_source = 032ksrc_clk,config_data->clock_source> 066Msrc_clk;

    展开全文
  • 十二、使用PWM调整LCD背光亮度

    千次阅读 2019-10-08 23:11:51
    调整背光亮度依赖于PWM,它通过调节脉冲宽度来控制背光亮度,此方式需要使用PWM驱动。本章将对其进行讲解。 一、用户空间调整背光亮度 一般应用程序可以通过/sys/class/目录下的节点间接调整各个外设的参数。...

     

    和手机一样,开发板中也带有调整背光亮度的功能。

    调整背光亮度依赖于PWM,它通过调节脉冲宽度来控制背光亮度,此方式需要使用PWM驱动。本章将对其进行讲解。

     

     

    一、用户空间调整背光亮度 

    一般应用程序可以通过/sys/class/目录下的节点间接调整各个外设的参数。如下图,可通过命令行来控制背光亮度。设备节点不同开发板的目录不一定相同,读者需自行测试。

    如果读者确定自己的开发板有PWM控制背光的功能,但是在LCD、背光和PWM等相关目录没有找到调整亮度操作,可能的原因有PWM没有被编译进内核。

     

    我们可以在配置中执行搜索操作,如:

    $ make menuconfig  进入内核配置页面

    按 / 键搜索,确认背光PWM是否被编译进内核。

    如下图,我的背光PWM被编译进内核中了。需要注意,不同内核可能选项位置不同。

     

     

    二、PWM子系统

    在Linux3.5版本中并没有引入PWM子系统,而是使用总线设备驱动模型。我在此以Linux-4.4为例谈论PWM子系统。

    PWM也分为设备属性和行为。属性使用struct pwm_device表示,行为使用struct pwm_ops表示,两结构体定义如下:

    struct pwm_device {
        const char *label;
        unsigned long flags;
        unsigned int hwpwm;
        unsigned int pwm;
        struct pwm_chip *chip;
        void *chip_data;
        struct mutex lock;
    
        unsigned int period;        /* PWM周期 */
        unsigned int duty_cycle;    /* 占空比 */
        enum pwm_polarity polarity; /* 极性反转 */
    };
    enum pwm_polarity {
        PWM_POLARITY_NORMAL,
        PWM_POLARITY_INVERSED,
    };
    
    struct pwm_ops {
        int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
        void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
        int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
                  int duty_ns, int period_ns);
        int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
                    enum pwm_polarity polarity);
        int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
        void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
    #ifdef CONFIG_DEBUG_FS
        void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
    #endif
        struct module *owner;
    };

    pwm_ops的上层函数有:

    /* 下列函数与pwm_ops位置一一对应 */
    struct pwm_device *pwm_request(int pwm, const char *label);
    void pwm_free(struct pwm_device *pwm);
    int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
    int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
    int pwm_enable(struct pwm_device *pwm);
    void pwm_disable(struct pwm_device *pwm);

     

    PWM虽然分层和LCD一致,但这两个结构体并不像LCD中可以互相访问。如LCD中fb_info定义有:

    struct fb_info {
        ...
        struct fb_ops *fbops;    /* 可通过fb_info访问fb操作函数 */
        ...
    };

    既然struct pwm_device和struct pwm_ops不能互相访问,那么一定会有其他结构体连接两者。它就是struct pwm_chip:

    struct pwm_chip {
        struct device *dev;
        struct list_head list;
        const struct pwm_ops *ops;  /* PWM操作函数 */
        int base;
        unsigned int npwm;          /* 这个chip中的PWM个数 */
    
        struct pwm_device *pwms;    /* PWM属性 */
    
        struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
                        const struct of_phandle_args *args);
        unsigned int of_pwm_n_cells;
        bool can_sleep;
    };

     

    如果我们需要注册或注销PWM,使用的结构体应该也是struct pwm_chip。也就是PWM以chip注册。

    注册函数pwmchip_add()实现如下:

     1 /* chip的属性 */
     2 static struct attribute *pwm_chip_attrs[] = {
     3     &dev_attr_export.attr,      /* 注册pwm,创建设备节点 */
     4     &dev_attr_unexport.attr,    /* 注销pwm,销毁设备节点 */
     5     &dev_attr_npwm.attr,        /* 当前chip中pwm个数 */
     6     NULL,
     7 };
     8 
     9 int pwmchip_add(struct pwm_chip *chip)
    10 {
    11     return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL);
    12 }
    13 EXPORT_SYMBOL_GPL(pwmchip_add);
    14 
    15 int pwmchip_add_with_polarity(struct pwm_chip *chip, enum pwm_polarity polarity)
    16 {
    17     struct pwm_device *pwm;
    18     unsigned int i;
    19     int ret;
    20 ...
    21     /* pwm子系统和input子系统类似,按顺序填充的,但pwm子系统没有设备号 */
    22     ret = alloc_pwms(chip->base, chip->npwm);
    23 ...
    24     /* 申请空间 */
    25     chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
    26 ...
    27     chip->base = ret;
    28     /* 设置pwm_device */
    29     for (i = 0; i < chip->npwm; i++) {
    30         pwm = &chip->pwms[i];
    31         pwm->chip = chip;
    32         pwm->pwm = chip->base + i;
    33         pwm->hwpwm = i;
    34         pwm->polarity = polarity;
    35 ...
    36     }
    37 ...
    38     /* 创建pwmchip%d设备 */
    39     pwmchip_sysfs_export(chip);
    40 ...
    41 }
    42 
    43 void pwmchip_sysfs_export(struct pwm_chip *chip)
    44 {
    45 ...
    46     parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, "pwmchip%d", chip->base);
    47 ...
    48 }

     

    注销函数pwmchip_remove()实现如下:

     1 int pwmchip_remove(struct pwm_chip *chip)
     2 {
     3     unsigned int i;
     4     int ret = 0;
     5     /* 此函数会卸载此chip下的所有PWM */
     6     pwmchip_sysfs_unexport_children(chip);
     7 ...
     8     free_pwms(chip);
     9     /* 注销设备 */
    10     pwmchip_sysfs_unexport(chip);
    11 ...
    12 }
    13 EXPORT_SYMBOL_GPL(pwmchip_remove);
    14 
    15 void pwmchip_sysfs_unexport(struct pwm_chip *chip)
    16 {
    17     struct device *parent;
    18 
    19     parent = class_find_device(&pwm_class, NULL, chip,
    20                    pwmchip_sysfs_match);
    21     if (parent) {
    22         /* for class_find_device() */
    23         put_device(parent);
    24         device_unregister(parent);
    25     }
    26 }

     

    在注册和注销pwmchip函数中,只是创建和删除和设备节点。但是在此之前,它并没有创建类,而是使用了类pwm_class。分析可知PWM子系统在初始化过程中一定创建了类pwm_class。

     1 static struct class pwm_class = {
     2     .name = "pwm",                    /* 类名为pwm */
     3     .owner = THIS_MODULE,
     4     .dev_groups = pwm_chip_groups,    /* 存放所有chip */
     5 };
     6 
     7 static int __init pwm_sysfs_init(void)
     8 {
     9     return class_register(&pwm_class);
    10 }

     

    现在,我们来整理一下整体框架。

     

     

    二、三星平台驱动分析

    1. platform_device

     1 static struct resource samsung_pwm_resource[] = {
     2     DEFINE_RES_MEM(SAMSUNG_PA_TIMER, SZ_4K),
     3 };
     4 
     5 struct platform_device samsung_device_pwm = {
     6     .name        = "samsung-pwm",
     7     .id        = -1,
     8     .num_resources    = ARRAY_SIZE(samsung_pwm_resource),
     9     .resource    = samsung_pwm_resource,
    10 };

     

    2. platform_driver

     1 static struct platform_driver pwm_samsung_driver = {
     2     .driver        = {
     3         .name    = "samsung-pwm",
     4         .pm    = &pwm_samsung_pm_ops,
     5         .of_match_table = of_match_ptr(samsung_pwm_matches),
     6     },
     7     .probe        = pwm_samsung_probe,
     8     .remove        = pwm_samsung_remove,
     9 };
    10 module_platform_driver(pwm_samsung_driver);

     

    我们来分析probe()函数,它设置并注册了pwmchip:

     1 struct samsung_pwm_chip {
     2     struct pwm_chip chip;    /* pwm_chip */
     3     struct samsung_pwm_variant variant;
     4     u8 inverter_mask;
     5 
     6     void __iomem *base;
     7     struct clk *base_clk;
     8     struct clk *tclk0;
     9     struct clk *tclk1;
    10 };
    11 
    12 static int pwm_samsung_probe(struct platform_device *pdev)
    13 {
    14     struct device *dev = &pdev->dev;
    15     struct samsung_pwm_chip *chip;
    16     struct resource *res;
    17     unsigned int chan;
    18     int ret;
    19 
    20     /* 分配内存 */
    21     chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
    22     /* 设置chip */
    23     chip->chip.dev = &pdev->dev;
    24     chip->chip.ops = &pwm_samsung_ops;
    25     chip->chip.base = -1;
    26     chip->chip.npwm = SAMSUNG_PWM_NUM;
    27     chip->inverter_mask = BIT(SAMSUNG_PWM_NUM) - 1;
    28 ...
    29         memcpy(&chip->variant, pdev->dev.platform_data, sizeof(chip->variant));
    30 
    31     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    32     chip->base = devm_ioremap_resource(&pdev->dev, res);
    33 ...
    34     chip->base_clk = devm_clk_get(&pdev->dev, "timers");
    35 ...
    36     ret = clk_prepare_enable(chip->base_clk);
    37 ...
    38     for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan)
    39         if (chip->variant.output_mask & BIT(chan))
    40             pwm_samsung_set_invert(chip, chan, true);
    41 
    42     /* Following clocks are optional. */
    43     chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0");
    44     chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1");
    45 
    46     platform_set_drvdata(pdev, chip);
    47     /* 注册pwm */
    48     ret = pwmchip_add(&chip->chip);
    49 ...
    50     return 0;
    51 }

     

    remove()应该注销pwmchip:

     1 static int pwm_samsung_remove(struct platform_device *pdev)
     2 {
     3     struct samsung_pwm_chip *chip = platform_get_drvdata(pdev);
     4     int ret;
     5 
     6     ret = pwmchip_remove(&chip->chip);
     7     if (ret < 0)
     8         return ret;
     9 
    10     clk_disable_unprepare(chip->base_clk);
    11 
    12     return 0;
    13 }

     

    关于三星平台的struct pwm_ops pwm_samsung_ops在此不做分析,这些函数只是对寄存器进行读写操作。

     

    最后总结一下:

    1. PWM是通过注册chip来注册一个芯片中所有的PWM的

    2. 对于具体的PWM,可以使用sysfs中的attributre属性来分辨

    3. 注册完成某个PWM后,它的周期、占空比、极性和使能可以在sysfs中更改

    4. 在sysfs中修改降低了驱动程序修改的次数

     

    pwmchip在/sys/class/目录下生成,我们可以执行以下命令查看和修改pwmchip0的占空比:

    # cd /sys/class/pwm/

    # ls

    # cd ./pwmchip0

    # ls

    # cd ./pwm0

    # ls

    # ls duty_cycle

    # echo 50 > duty_cycle

    # ls duty_cycle

     

     

    下一章  十三、GPIO子系统

     

    转载于:https://www.cnblogs.com/Lioker/p/10976719.html

    展开全文
  • MT6582 使用PWM配置背光频率计算     [SOLUTION]   以GPIO52 pin为例: Mode0 Mode1 Mode2 Mode3 GPIO52 LPD8 PWM_BL PWM_A  /mediatek...
  • 调试问题:开机亮屏会听到嗡嗡鸣响,初步猜测背光频率过高
  • LCD背光驱动IC

    千次阅读 2018-10-19 11:20:18
    对于40Pin标准RGBLCD,需要背光驱动电路,现有如下三种参考设计,这三种均是恒流驱动: 1.UM1661 (某宝价格1元左右) 输入:2~6V  输出电压:高达24V 内部开关频率:2MHZ 最大输出电流:1.6A EN脚可接入PWM...
  • LCD 背光

    千次阅读 2011-03-02 22:55:00
    LCD背光由驱动产生文件/sys/class/leds/lcdbacklight/brightness对LCD背光亮度调整,原理是改变PWM信号的占空比来设置亮度一个参数是PWM信号的周期,另一个参数是PWM信号的占空比backlight_ops结构体定义如下:...
  • 考虑到设备功耗的降低以及使用的便利性,本文在嵌入式Linux下,设计了一种使用S3C2440的定时器产生PWM (Pulse Width Modulation)信号,根据设备实际使用需要,和外界光线强度的变化用按键调节LCD背光亮度的解决...
  • LCD背光设备 原理1

    千次阅读 2014-02-17 13:42:26
    本次背光驱动开发对应的是IMX233下LCD背光灯设备,主要由三个部分组成:PWM、RT9284B15PJ6芯片 和 LCD背光灯。 PWM(Pulse-Width Modulator,脉冲宽度调制) 是 利用微处理器的数字输出来对模拟电路进行控制的一种非常...
  • MT6571 使用PWM配置背光频率计算     [SOLUTION]   以GPIO52 pin为例: Mode0 Mode1 Mode2 Mode3 GPIO52 LPD8 PWM_BL PWM_A   一、将...
  • 配置LCD背光和LED

    2017-09-07 16:44:21
    [LED]如何配置LCD背光和LED,调试方法 [DESCRIPTION] 如何配置LCD背光和LED,调试方法 [SOLUTION] LCD背光和LED配置文件 alps/custom/lk/cust_leds.c alps/custom//kernel/leds/mt65xx/...
  • 液晶LCD背光升压电路

    千次阅读 2019-11-12 14:06:39
    1、LCD背光升压电路选用IC:LP3382B6F 2、FB反馈电压为 0.25V;则需要修改R54 、R55的电阻来进行电流的设置 由于使用的LCD背光电流是40mA,则电阻R54、R55并联后的电阻阻值为:0.25V ÷ 0.04A = 6.25Ω 3、...
  • 该产品可用于驱动为包括PDA、手机和数码相机在内的各种手持设备的LCD提供背光的白光LED。 XC9116的输入电压范围为2.5V至6.0V,输出电压高达17.5V。因此,输入2.5V电压,该产品可串联驱动4个LED,输入3.2V电压,则可...
  • [LED]如何配置LCD背光和LED,调试方法

    千次阅读 2016-07-18 16:16:05
    [LED]如何配置LCD背光和LED,调试方法 [DESCRIPTION] 如何配置LCD背光和LED,调试方法 [SOLUTION] LCD背光和LED配置文件 alps/custom/lk/cust_leds.c alps/custom//kernel/leds/mt65xx/cust_leds.c ...
  • LCD背光控制芯片

    2012-09-29 15:30:00
    PWM信号可通过调整占空比来调节输出电压,可以使用PWM来控制LCD背光。 但CPU的pwm引脚驱动能力太弱,常外接一个背光芯片。rt9293就是这样的一个恒流升压转换器。 Iled=Vref/Rset Vfb=Duty * 300mV ...
  • 基于嵌入式Linux的LCD背光调节及驱动的实现
  • 如何配置LCD背光和LED,调试方法

    千次阅读 2014-08-09 12:46:34
    LCD背光和LED配置文件 alps/custom/lk/cust_leds.c alps/custom//kernel/leds/mt65xx/cust_leds.c drvgen.exe编辑修改codegen.dws  配置cust_leds.c参数: static struct cust_mt65xx_led cust_led_list[MT65...
  • [DESCRIPTION]如何配置LCD背光和LED,调试方法[SOLUTION]LCD背光和LED配置文件alps/custom/&lt;proj name&gt;lk/cust_leds.calps/custom/&lt;proj name&gt;/kernel/leds/mt65xx/cust_leds.cdrvgen....
  • [MT6582_LCD]使用PWM配置背光如何配置,及频率计算2016年07月08日 10:10:22阅读数:1175MT6582 使用PWM配置背光频率计算 [SOLUTION] 以GPIO52 pin为例:Mode0Mode1Mode2Mode3GPIO52LPD8PWM_BLPWM_A /mediatek/...
  • am335x LCD调节背光

    2019-05-10 20:45:36
    一、平台环境: am3358 SDK 4.00.00.06 带dts 二、难点记录: ...LCD 背光 ,进入系统后,无法修改lcd背光频率 执行 echo 0 > /sys/class/pwm/pwmchip0/export 报错: -sh:echo:writeerror:Device...
  • LCD背光和LED配置文件  alps/custom/lk/cust_leds.c  alps/custom//kernel/leds/mt65xx/cust_leds.c  drvgen.exe编辑修改codegen.dws   配置cust_leds.c参数: static struct cust_mt65xx_led ...
  • MTK android配置LCD背光和LED,调试方法

    千次阅读 2015-06-24 22:10:10
    [DESCRIPTION] ...如何配置LCD背光和LED,调试方法 [SOLUTION] LCD背光和LED配置文件 alps/custom/lk/cust_leds.c alps/custom//kernel/leds/mt65xx/cust_leds.c drvgen.exe编辑修改
  • 由于LCD自身是不能发光的,它需要一个强劲的光源来给它提供背光,以便清晰地显示信息。这样的光源是非常耗电的,通常液晶显示屏的功耗常常占到系统总功耗的60%以上。以群创的7寸屏为例,通常背光灯的功耗为2.5W,而...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 733
精华内容 293
关键字:

lcd背光pwm频率