Linux PWM framework(二)- 背光子系统

  • 了解backlight driver.

1.Backlight Framework
在这里插入图片描述

1.1.用户空间

  背光设备文件对应于/sys/class/backlight/目录下的文件。/sys/class/backlight是注册的背光设备类型,而在/sys/class/backlight/目录下的文件就是所注册的背光设备。系统完成背光设备类型的注册,代码如下:

drivers/video/backlight/backlight.c:
289 static struct attribute *bl_device_attrs[] = {                                                           
290     &dev_attr_bl_power.attr,
291     &dev_attr_brightness.attr,
292     &dev_attr_actual_brightness.attr,
293     &dev_attr_max_brightness.attr,
294     &dev_attr_type.attr,
295     NULL,
296 };
297 ATTRIBUTE_GROUPS(bl_device);

662 static int __init backlight_class_init(void)                                                             
663 {
664     backlight_class = class_create(THIS_MODULE, "backlight");  //注册背光设备类型; 
670 
671     backlight_class->dev_groups = bl_device_groups;   //指定背光设备类型的属性文件; 
672     backlight_class->pm = &backlight_class_dev_pm_ops;
673     INIT_LIST_HEAD(&backlight_dev_list);
674     mutex_init(&backlight_dev_list_mutex);
675     BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);
676 
677     return 0;
678 }

1.2.struct backlight_device

 89 struct backlight_device {
 90     /* Backlight properties */
 91     struct backlight_properties props;
 92 
 93     /* Serialise access to update_status method */
 94     struct mutex update_lock;
 95 
 96     /* This protects the 'ops' field. If 'ops' is NULL, the driver that
 97        registered this device has been unloaded, and if class_get_devdata()
 98        points to something in the body of that driver, it is also invalid. */
 99     struct mutex ops_lock;
100     const struct backlight_ops *ops;    //背光设备的相关操作函数 
101 
102     /* The framebuffer notifier block */
103     struct notifier_block fb_notif;
104 
105     /* list entry of all registered backlight devices */
106     struct list_head entry;
107 
108     struct device dev;
109 
110     /* Multiple framebuffers may share one backlight device */                                           
111     bool fb_bl_on[FB_MAX];
112 
113     int use_count;
114 };

  其中backlight_properties和backlight_ops结构体定义如下:

 67 /* This structure defines all the properties of a backlight */
 68 struct backlight_properties {
 70     int brightness;
 72     int max_brightness;
 75     int power;
 78     int fb_blank;
 80     enum backlight_type type;
 82     unsigned int state;
 87 };
 
 52 struct backlight_ops {                                                                                   
 53     unsigned int options;
 58     int (*update_status)(struct backlight_device *); //更新背光设备亮度等属性 
 61     int (*get_brightness)(struct backlight_device *); //获取背光设备亮度 
 64     int (*check_fb)(struct backlight_device *, struct fb_info *);
 65 };

1.3.相关APIs:

1.3.1.Register/unregister backlight device

在/sys/class/backlight/目录下注册和移除具体的背光设备:
struct backlight_device *backlight_device_register(const char *name,struct device *dev, void *devdata, struct backlight_ops *ops); 
void backlight_device_unregister(struct backlight_device *bd);

1.3.2.Get backlight device/Drop backlight reference

 struct backlight_device *devm_of_find_backlight(struct device *dev);
 static void devm_backlight_release(void *data);

1.3.3.Enable backlight/Disable backlight

static inline int backlight_enable(struct backlight_device *bd);
static inline int backlight_disable(struct backlight_device *bd)

2.PWM backlight

2.1.struct platform_pwm_backlight_data:

 10 struct platform_pwm_backlight_data {
 11     int pwm_id;
 12     unsigned int max_brightness;
 13     unsigned int dft_brightness;
 14     unsigned int lth_brightness;
 15     unsigned int pwm_period_ns;
 16     unsigned int *levels;
 17     unsigned int post_pwm_on_delay;
 18     unsigned int pwm_off_delay;
 19     /* TODO remove once all users are switched to gpiod_* API */
 20     int enable_gpio;
 21     int (*init)(struct device *dev);
 22     int (*notify)(struct device *dev, int brightness);
 23     void (*notify_after)(struct device *dev, int brightness);
 24     void (*exit)(struct device *dev);
 25     int (*check_fb)(struct device *dev, struct fb_info *info);
 26 };  

struct pwm_bl_data:

 28 struct pwm_bl_data {
 29     struct pwm_device   *pwm;
 30     struct device       *dev;
 31     unsigned int        period;
 32     unsigned int        lth_brightness;
 33     unsigned int        *levels;
 34     bool            enabled;
 35     struct regulator    *power_supply;
 36     struct gpio_desc    *enable_gpio;
 37     unsigned int        scale;
 38     bool            legacy;
 39     unsigned int        post_pwm_on_delay;
 40     unsigned int        pwm_off_delay;
 41     int         (*notify)(struct device *,
 42                       int brightness);
 43     void            (*notify_after)(struct device *,
 44                     int brightness);
 45     int         (*check_fb)(struct device *, struct fb_info *);
 46     void            (*exit)(struct device *);                                                            
 47 };

2.2.sysfs

  在/sys/class/backlight/目录下创建pwm backlight node,并注册pwm_backlight_ops。

drivers/video/backlight/pwm_bl.c:
585     bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
586                        &pwm_backlight_ops, &props);  

140 static const struct backlight_ops pwm_backlight_ops = {
141     .update_status  = pwm_backlight_update_status,                                                       
142     .check_fb   = pwm_backlight_check_fb,
143 };

2.3.驱动分析

drivers/video/backlight/pwm_bl.c
pwm_backlight_probe 
    pwm_backlight_parse_dt 	  //解析 dts 中的 brightness-levels、default-brightness-level
    //RK3288 还会在这里解析 enable-gpios ,但是 3399 没有,3399 是在 probe 里面用 devm_gpiod_get_optional      //获取 enable-gpio 状态的
    devm_gpiod_get_optional  //实际上就是封装了 gpio_request_one
    devm_gpio_request_one 	//申请背光使能 gpio
    devm_pwm_get ->     /drivers/pwm/core.c //获得一个pwm
        pwm_get ->
            of_pwm_get ->
                of_parse_phandle_with_args    解析上面dts中的pwms属性.
                of_node_to_pwmchip
                pwm = pc->of_xlate    //最终生成struct pwm_device类型.    
    pwm_request    //申请pwm,防止其他驱动也会使用.
	pwm_set_period    //pb->pwm->period = data->pwm_period_ns
    pwm_get_period    //获取period.
    dev_set_name(&pdev->dev, "rk28_bl");    //name不能改,用户空间会被用到:/sys/class/backlight/rk28_bl
    backlight_device_register    -> /drivers/video/baklight/backlight.c   //注册标准背光设备
        device_register
        backlight_register_fb ->
            fb_register_client    //callback 是 fb_notifier_callback 
			fb_register_client   // 注册内核通知链
    backlight_update_status ->        //用默认值更新.
        bd->ops->update_status ->
            pwm_backlight_update_status ->
                compute_duty_cycle    //计算占空比
                pwm_config    //配置pwm 
                pwm_backlight_power_on    //enable背光
    platform_set_drvdata //可以将 pdev 保存成平台总线设备的私有数据,以后再要使用它时只需调用 platform_get_drvdata

2.3.1.计算占空比

static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
{
    /*一般情况下这个值都为0*/
    unsigned int lth = pb->lth_brightness;
	/*占空比*/
    int duty_cycle;
    /*pb->levels这个表格就是从dts节点brightness-levels中获取的,
    假设进来的参数brightness是254,那么得到的duty_cycle就是1,
    如果没有这个表格,那么就直接是进来的亮度值.*/
    if (pb->levels)
        duty_cycle = pb->levels[brightness];
    else
        duty_cycle = brightness;
		
    /*假设这里lth是0,那么公式就是duty_cycle * pb->period / pb->scale
	pb->period也就是dts节点 pwms 的第三个参数周期值为 25000
    pb->scale为pb->levels数组中的最大值
	所以这个公式就是按照将Android的纯数值转换成事件周期值对应的占空比.*/
    return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
}

2.3.2.调节占空比方法

  • pwm_backlight_probe 函数,解析dts,调用backlight_update_status来改变背光;
  • sysfs - brightness_store(drivers/video/backlight/backlight.c),设置brightness,然后调用backlight_update_status来改变背光;
  • backlight_register_fb(new_bd); ->fb_notifier_callback 调用backlight_update_status来改变背光; (drivers/video/backlight/backlight.c)
static int fb_notifier_callback(struct notifier_block *self,  
                unsigned long event, void *data)  
{  
...  
    /*只处理亮屏和灭屏事件.*/  
    /* If we aren't interested in this event, skip it immediately ... */  
    if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)  
        return 0;  
...  
    if (bd->ops)  
        if (!bd->ops->check_fb ||  
            bd->ops->check_fb(bd, evdata->info)) {  
            bd->props.fb_blank = *(int *)evdata->data;  
            //亮屏情况  
            if (bd->props.fb_blank == FB_BLANK_UNBLANK)  
                bd->props.state &= ~BL_CORE_FBBLANK;  
            //灭屏时  
            else  
                bd->props.state |= BL_CORE_FBBLANK;  
            backlight_update_status(bd);  
        }  
...  
}

2.4.Dtsi settings

backlight: backlight {
		status = "disabled";
		compatible = "pwm-backlight";
		pwms = <&pwm0 0 25000 0>;	
		pwm-names = <backlight>;
	
		/*开机初始化默认等级,Android起来之后会改变它.*/
		default-brightness-level = <50>;	
		enable-gpios = <&gpio 71 GPIO_ACTIVE_HIGH>;
		
		/*背光可调等级,比如这里是255级,实际反应到占空比就是当前值和数组中最大值的比值,
    	例如当前是200,那么最终duty cycle就是200/255.*/
		brightness-levels = <
			  0   1   2   3   4   5   6   7
			  8   9  10  11  12  13  14  15
			 16  17  18  19  20  21  22  23
			 24  25  26  27  28  29  30  31
			 32  33  34  35  36  37  38  39
			 40  41  42  43  44  45  46  47
			 48  49  50  51  52  53  54  55
			 56  57  58  59  60  61  62  63
			 64  65  66  67  68  69  70  71
			 72  73  74  75  76  77  78  79
			 80  81  82  83  84  85  86  87
			 88  89  90  91  92  93  94  95
			 96  97  98  99 100 101 102 103
			104 105 106 107 108 109 110 111
			112 113 114 115 116 117 118 119
			120 121 122 123 124 125 126 127
			128 129 130 131 132 133 134 135
			136 137 138 139 140 141 142 143
			144 145 146 147 148 149 150 151
			152 153 154 155 156 157 158 159
			160 161 162 163 164 165 166 167
			168 169 170 171 172 173 174 175
			176 177 178 179 180 181 182 183
			184 185 186 187 188 189 190 191
			192 193 194 195 196 197 198 199
			200 201 202 203 204 205 206 207
			208 209 210 211 212 213 214 215
			216 217 218 219 220 221 222 223
			224 225 226 227 228 229 230 231
			232 233 234 235 236 237 238 239
			240 241 242 243 244 245 246 247
			248 249 250 251 252 253 254 255>;		
	};

说明:

  • pwms = <&pwm0 0 25000 0>;

    • 第一个参数 表示此背光接在 pwm0 上;
    • 第二个参数 表示 index 为 0,pwm0 下只有 1个 pwm,所以填 0
    • 第三个参数 表示周期为 25000ns,即频率 为 40k
    • 第四个参数 表示极性,0 正极性,1 负极性
      • 正极性 0 表示 背光为正极 0~255 ,占空比从 0~100% 变化
      • 负极性 1 表示 背光为负极 255~0 ,占空比从 100~0% 变化
  • default-brightness-level = <50>;
    表示默认的背光,它存在于开机时候背光初始化到安卓。设置下来新的背光这段时间,default-brightness-level = < 50 > 表示为第 50 个元素的背光亮度。

  • enable-gpios = <&gpio 71 GPIO_ACTIVE_HIGH>;
    enable-gpios;表示背光使能脚,这个根据电路原理图配置即可;有的硬件没有这个背光使能脚,那么将这个配置删除,背光驱动通过配置 brightness-levels 数组的第 0 个元素将显示调黑。

2.4.1.enable-gpios 代码解析

devm_gpiod_get_optional(&pdev->dev, "enable",GPIOD_ASIS);
-> of_find_gpio(dev, con_id, idx, &lookupflags);  
   ->of_get_named_gpiod_flags
      ->of_find_gpiochip_by_xlate
         ->chip->of_xlate  //gpio 驱动optional 实现,如果驱动没有实现,则调用系统实现的of_gpio_simple_xlate
      ->of_xlate_and_get_gpiod_flags
         ->gpiochip_get_desc //获取gpio desc

3.Debug

  • cd /sys/class/backlight
  • cd /sys/device/platform/backlight

Note:调节brightness 改变占空比。

3.1.shell脚本调节背光:

#!/system/bin/sh or #!/bin/sh
i=0
while [ $i -le 255 ]
do
  echo $i
  echo $i > /sys/class/backlight/backlight/brightness
  i=$((i+1))
  usleep 100000
done

refer to

  • https://www.dazhuanlan.com/2019/10/25/5db29684e6460/
  • https://blog.csdn.net/kris_fei/article/details/52485635
  • https://developer.ridgerun.com/wiki/index.php?title=Linux_PWM_Pulse_Width_Modulator
发布了161 篇原创文章 · 获赞 15 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_41028621/article/details/103542751
PWM