深入理解linux内核之深入理解PWM子系统

系列 内容
深入理解linux内核 深入理解PWM子系统

文章目录

    • 用户空间:
    • 背光状态:
    • 私有数据的获取:
    • prob
    • 设备树的解析:
    • 根据亮度值来计算占空比

深入理解linux内核之深入理解PWM子系统_第1张图片

用户空间:

深入理解linux内核之深入理解PWM子系统_第2张图片
shell脚本调节背光:

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

backlight_device

目录:kernel/include/linux/backlight.h
深入理解linux内核之深入理解PWM子系统_第3张图片

背光状态:

深入理解linux内核之深入理解PWM子系统_第4张图片

私有数据的获取:

static inline void * bl_get_data(struct backlight_device *bl_dev)
{
		/*********返回driver的私有数据********/
        return dev_get_drvdata(&bl_dev->dev);
}

prob

其对应的私有数据是在prob函数中赋值:

/*获取私有数据 return dev->platform_data; */
struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
struct platform_pwm_backlight_data defdata;
struct pwm_bl_data *pb;

/***设备树的解析***/
ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
data = &defdata;

pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
if (data->levels) {
	 for (i = 0; i <= data->max_brightness; i++)
	     if (data->levels[i] > pb->scale)
		     pb->scale = data->levels[i];
		 pb->levels = data->levels;
 } else
	 pb->scale = data->max_brightness;
	 
pb->notify = data->notify;
pb->notify_after = data->notify_after;
pb->check_fb = data->check_fb;
pb->exit = data->exit;
pb->dev = &pdev->dev;
pb->enabled = false;

//devm_gpiod_get_optional只是对gpiod_get_index
//gpiod_get_index()本质上和gpio_request()是一个功能,是申请gpio的,只是它是从device tree去查找, 
pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",GPIOD_ASIS);
                                                 
if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {
		 // 申请这个GPIO
		ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio,GPIOF_OUT_INIT_HIGH,"enable");		
	    //根据gpio序号获取相应的描述gpio的结构体desc
		pb->enable_gpio = gpio_to_desc(data->enable_gpio);
}

if (pb->enable_gpio) {
	if (node && node->phandle &&
		gpiod_get_direction(pb->enable_gpio) == GPIOF_DIR_OUT && gpiod_get_value(pb->enable_gpio) == 0)
    	initial_blank = FB_BLANK_POWERDOWN;
else
    gpiod_direction_output(pb->enable_gpio, 1);

//通过regulator_get就可以获得regulator设备。
pb->power_supply = devm_regulator_get(&pdev->dev, "power");

//core.c//获得一个pwm
pb->pwm = devm_pwm_get(&pdev->dev, NULL);
        pwm_get ->
            of_pwm_get ->
                of_parse_phandle_with_args    解析上面dts中的pwms属性.
                of_node_to_pwmchip
                pwm = pc->of_xlate    //最终生成struct pwm_device类型.   
                
//linux/drivers/pwm/core.c
//adjust the current PWM config to the PWM arguments
pwm_adjust_config(pb->pwm);

pwm_get_args(pb->pwm, &pargs);
pb->period = pargs.period;

pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);

memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;

props.max_brightness = data->max_brightness;

//这个注册函数接口在/sys/class/backlight/下又创建了一个设备接口, 名字是通过dev_name(&pdev->dev)实参传递的.
bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb, &pwm_backlight_ops, &props);

bl->props.brightness = data->dft_brightness;
bl->props.power = initial_blank;
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
                             		                                     

设备树的解析:

static int pwm_backlight_parse_dt(struct device *dev,
                                  struct platform_pwm_backlight_data *data)
{
	 struct device_node *node = dev->of_node;
	 struct property *prop;
	 /* determine the number of brightness levels */
     prop = of_find_property(node, "brightness-levels", &length);

	 data->max_brightness = length / sizeof(u32);
	 
	 /* read brightness levels from DT property */

	 if (data->max_brightness > 0) {
		size_t size = sizeof(*data->levels) * data->max_brightness;
		data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
		ret = of_property_read_u32_array(node, "brightness-levels",data->levels,data->max_brightness);

	 ret = of_property_read_u32(node, "default-brightness-level",
                                &value);
     data->dft_brightness = value;
     data->max_brightness--;

}
                                

我们重点看pwm_backlight_update_statuscompute_duty_cycle

当向brightness中写入合法的亮度索引后,就会调用到pwm_backlight_update_status,其中的变量brightness就是写入的所用值,如果不为0的话(为0的话,会调用pwm_backlight_power_off关闭背光),就会调用compute_duty_cycle,这个函数将索引转换为duty_cycle(在normal模式下表示的是高电平的持续时间,用来控制占空比),然后调用pwm_config配置tcmpb寄存器,实现占空比的改变。

static int pwm_backlight_update_status(struct backlight_device *bl)
{
        struct pwm_bl_data *pb = bl_get_data(bl);
        int brightness = bl->props.brightness;
        int duty_cycle;
        
		/********其中FB_BLANK_UNBLANK是亮屏操作;********/
        if (bl->props.power != FB_BLANK_UNBLANK ||
            bl->props.fb_blank != FB_BLANK_UNBLANK ||
            bl->props.state & BL_CORE_FBBLANK)
                brightness = 0;

        if (pb->notify)
                brightness = pb->notify(pb->dev, brightness);

        if (brightness > 0) {
		        //计算占空比
                duty_cycle = compute_duty_cycle(pb, brightness);
                pwm_config(pb->pwm, duty_cycle, pb->period);
                pwm_backlight_power_on(pb, brightness);
        } else{
		       //props.brightness为0则关闭显示屏
				pwm_backlight_power_off(pb);
		}

        if (pb->notify_after)
                pb->notify_after(pb->dev, brightness);

        return 0;
}

根据亮度值来计算占空比

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中获取的*/
        if (pb->levels)
                duty_cycle = pb->levels[brightness];
        else
                duty_cycle = brightness;
                
		/*duty_cycle * pb->period / pb->scale*/
		/*pb->scale为pb->levels数组中的最大值*/
		/*pb->period也就是dts节点pwms的第三个参数周期值为25000*/
		
        return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
}

函数只是pwm_apply_state()的封装
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
linuxpwm接口位于
参数:
duty_nspwm最终输出脉冲周期中占空比时间
period_nspwm最终输出脉冲的周期

static inline int pwm_config(struct pwm_device *pwm, int duty_ns,int period_ns)
{
        struct pwm_state state;
        if (!pwm)
                return -EINVAL;
        if (duty_ns < 0 || period_ns < 0)
                return -EINVAL;
        pwm_get_state(pwm, &state);
        if (state.duty_cycle == duty_ns && state.period == period_ns)
                return 0;
        state.duty_cycle = duty_ns;
        state.period = period_ns;
        return pwm_apply_state(pwm, &state);
}

你可能感兴趣的:(子类__Display,子类__kernel,深入理解linux内核)