使用pwm_bl驱动和backlight class实现背光调整

上节中梳理了dts中lvds_backlight设备节点的解析注册过程,以及pwm_bl驱动注册过程,由平台总线对设备与驱动进行匹配,调用probe回调函数,最终实现设备的初始化。

本次梳理驱动的具体实现,从probe调用到用户空间实现对设备节点的操作,即调整背光亮度。

1. 设备树的重新修改

背光控制由两个IO口,一个作为GPIO,给背光芯片提供使能信号;一个作为PWM输出,给背光芯片提供不同占空比的PWM波形,实现亮度控制。之前设备树节点配置中,没有配置GPIO使能信号,不能通过控制使能而打开/关闭背光。因此重新在设备节点中增加以下三行,加入GPIO节点属性,实现使能信号的控制。

pinctrl_bl_power: bl_power{

fsl,pins = <

SC_P_LVDS0_I2C0_SCL_LSIO_GPIO1_IO06 0x00000021

>;

};

...

lvds_backlight0: lvds_backlight {

...

pinctrl-names = "default";

pinctrl-0 = ;

bl-power = ;

...

};

pinctrl-0 pinctrl的驱动解析,配置管脚属性,复用状态等

其中SC_P_LVDS0_I2C0_SCL_LSIO_GPIO1_IO06为宏,定义在include/dt-bindings/pinctrl/pads-imx8qm.h头文件中,具体如下:

...

#define SC_P_LVDS0_I2C0_SCL 52 /* LVDS0.I2C0.SCL, LVDS0.GPIO0.IO02, LSIO.GPIO1.IO06 */ //端子号

...

#define SC_P_LVDS0_I2C0_SCL_LSIO_GPIO1_IO06 SC_P_LVDS0_I2C0_SCL 3 //复用功能

...

0x00000021为pad/mux 寄存器设置,需要根据电路设计情况进行设置。参考手册,摘取手册部分寄存器配置如下:

// bit filed

6-5

Pull Down Pull Up

Pull Down Pull Up

00b - prohibited

01b - pull-up

10b - pull-down

11b - pull disabled

4-1

reserved

reserved

0

PDRV

Drive

0b - high drive strength

1b - low drive strength

根据寄存器手册,0x21设置状态为:pull-up,low drive strength。

根据pinctrl-0节点属性,初始化时,内核将该复用端子初始化成GPIO。

bl-power节点属性由gpio驱动解析,标识出gpio组与组中的id,可以使用gpio模块的接口通过此节点获取gpio,从而实现对gpio的输入输出设置与输出状态设置。

2. pwm_bl的驱动实现

2.1 probe实现

pwm_bl.c文件是对lvds_backlight驱动的实现,当注册驱动后,匹配到设备,调用probe函数,函数内容如下:

static int pwm_backlight_probe(struct platform_device *pdev)

{

struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);

struct platform_pwm_backlight_data defdata;

struct backlight_properties props;

struct backlight_device *bl;

struct device_node *node = pdev->dev.of_node;

struct pwm_bl_data *pb;

struct pwm_args pargs;

int ret;

pr_debug("enter probe +%s\n","1");

if (!data) {

ret = pwm_backlight_parse_dt(&pdev->dev, &defdata); // 解析dts节点中的数据,包括默认背光,背光等级等

if (ret < 0) {

dev_err(&pdev->dev, "failed to find platform data\n");

return ret;

}

data = &defdata;

pr_debug("parser dtb data \n");

}

pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL); // 申请一块内存用于保存pwm_bl_data数据

data->enable_gpio = of_get_named_gpio(pdev->dev.of_node, "bl-power", 0); // 使用of函数得到gpio的id,提供给gpio的api使用

/*

* Compatibility fallback for drivers still using the integer GPIO

* platform data. Must go away soon.

*/

if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {

ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio, // 申请gpio

GPIOF_OUT_INIT_HIGH, "bl-power");

if (ret < 0) {

dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",

data->enable_gpio, ret);

goto err_alloc;

}

pb->enable_gpio = gpio_to_desc(data->enable_gpio); // 将gpio number转成其描述符,gpiolib的api使用

}

/*

* If the GPIO is not known to be already configured as output, that

* is, if gpiod_get_direction returns either 1 or -EINVAL, change the

* direction to output and set the GPIO as active.

* Do not force the GPIO to active when it was already output as it

* could cause backlight flickering or we would enable the backlight too

* early. Leave the decision of the initial backlight state for later.

*/

if (pb->enable_gpio &&

gpiod_get_direction(pb->enable_gpio) != 0) // 设置gpio为输出

gpiod_direction_output(pb->enable_gpio, 0); // 设置gpio输出电平为低

pb->power_supply = devm_regulator_get(&pdev->dev, "power"); // 获取power资源

if (IS_ERR(pb->power_supply)) {

ret = PTR_ERR(pb->power_supply);

goto err_alloc;

}

pb->pwm = devm_pwm_get(&pdev->dev, NULL); // 获取pwm资源,由于devm_*类接口是后来增加的获取资源的接口形式,如果获取失败,则使用传统接口获取

if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {

dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");

pb->legacy = true;

pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");

}

memset(&props, 0, sizeof(struct backlight_properties));

props.type = BACKLIGHT_RAW;

props.max_brightness = data->max_brightness;

pr_debug("register device, name is: %s\n", dev_name(&pdev->dev));

// 使用backlight class接口注册设备

bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), pdev->dev.parent, pb,

&pwm_backlight_ops, &props);

// 更新占背光信息

bl->props.brightness = data->dft_brightness;

bl->props.power = pwm_backlight_initial_power_state(pb);

backlight_update_status(bl);

}

devm_backlight_device_register是backlight class提供的接口函数,将设备注册到backlight class上,属性文件向文件系统注册,读写操作的实现均在backlight中实现,最终的gpio和pwm控制通过在pwm_backlight_ops中设置的回调实现。

2.2 为backlight class注册设置回调

上节提到的设置回调函数的结构体变量定义如下,其中有update_status成员变量,通过后续的backlight分析可知,在更新背光亮度和开关状态时,会调用此函数:

static const struct backlight_ops pwm_backlight_ops = {

.update_status= pwm_backlight_update_status,

.check_fb= pwm_backlight_check_fb,

};

函数参数是一个backlight_device的结构指针。函数实现如下:

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;

if (bl->props.power != 1 ||//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);

pr_debug("invoke notify function\n");

}

pr_debug("update backlight brightenss %d\n", 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); // 拉高bl_power, 打开pwm power

} else{

pwm_backlight_power_off(pb); // 拉低bl_power gpio,关闭pwm power

}

if (pb->notify_after)

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

return 0;

}

从设备结构指针中获取pwm的数据结构,根据power及其他属性状态,对背光亮度和开关状态进行设定。

3 backlight class

3.1 backlight class属性导出

backlight将bl_power,brightness,type等属性导出至用户空间,根据导出属性的读写权限,为属性编写配置*_show() *_store()函数,以brightness为例:

// 读导出的属性文件时的回调函数

static ssize_t brightness_show(struct device *dev,

struct device_attribute *attr, char *buf)

{

struct backlight_device *bd = to_backlight_device(dev);

return sprintf(buf, "%d\n", bd->props.brightness);

}

// 写导出的属性文件时的回调函数

static ssize_t brightness_store(struct device *dev,

struct device_attribute *attr, const char *buf, size_t count)

{

int rc;

struct backlight_device *bd = to_backlight_device(dev);

unsigned long brightness;

rc = kstrtoul(buf, 0, &brightness);

if (rc)

return rc;

rc = backlight_device_set_brightness(bd, brightness);

return rc ? rc : count;

}

static DEVICE_ATTR_RW(brightness); // 宏,导出属性

其中static DEVICE_ATTR_RW(brightness)是宏,展开后如下:

static struct device_attribute dev_attr_brightness =

{

.attr = {

.name = "brightness", // 属性名

.mode = VERIFY_OCTAL_PERMISSIONS((S_IWUSR | (S_IRUSR|S_IRGRP|S_IROTH))) // 属性文件的读写属性

},

.show = brightness_show, // 读回调函数

.store = brightness_store, // 写回调函数

};

引用brightness属性的属性列表

// 引用brightness属性结构体变量的属性列表

static struct attribute *bl_device_attrs[] = {

&dev_attr_bl_power.attr,

&dev_attr_brightness.attr,

&dev_attr_actual_brightness.attr,

&dev_attr_max_brightness.attr,

&dev_attr_type.attr,

NULL,

};

// 宏

ATTRIBUTE_GROUPS(bl_device);

对上述宏展开如下:

static const struct attribute_group bl_device_group = {

.attrs = bl_device_attrs,

};

static const struct attribute_group *bl_device_groups[] = {

&bl_device_group,

((void *)0), // 空指针,标志设备的属性组配置完成

};

其中bl_device_groups作为backlight class的默认属性配置在初始化时赋值给class的结构体变量:

static int __init backlight_class_init(void)

{

backlight_class = class_create(THIS_MODULE, "backlight");

if (IS_ERR(backlight_class)) {

pr_warn("Unable to create backlight class; errno = %ld\n",

PTR_ERR(backlight_class));

return PTR_ERR(backlight_class);

}

backlight_class->dev_groups = bl_device_groups;

backlight_class->pm = &backlight_class_dev_pm_ops;

INIT_LIST_HEAD(&backlight_dev_list);

mutex_init(&backlight_dev_list_mutex);

BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);

return 0;

}

至此,当backlight class初始化完成后,将注册的backlight device的属性导出到用户空间,并设置读写属性函数,导出的属性与其路径如下:

root:~# ls /sys/devices/platform/backlight/lvds_backlight/ -lh

total 0

-r--r--r-- 1 root root 4.0K Jan 1 00:00 actual_brightness

-rw-r--r-- 1 root root 4.0K Jan 1 00:00 bl_power

-rw-r--r-- 1 root root 4.0K Jan 1 00:00 brightness

lrwxrwxrwx 1 root root 0 Jan 1 00:00 device -> ../../../platform

-r--r--r-- 1 root root 4.0K Jan 1 00:00 max_brightness

drwxr-xr-x 2 root root 0 Jan 1 00:00 power

lrwxrwxrwx 1 root root 0 Jan 1 00:00 subsystem -> ../../../../class/backlight

-r--r--r-- 1 root root 4.0K Jan 1 00:00 type

-rw-r--r-- 1 root root 4.0K Jan 1 00:00 uevent

3.2 设置背光状态与调整背光

通过对bl_power和brightness文件的修改与读取,便能够控制背光的开关,设置背光亮度与获取当前背光的状态和亮度。当执行写操作时,如:

echo 80 > /sys/devices/platform/backlight/lvds_backlight/brightness

则调用brightness_store函数,更新pwm占空比,此时函数调用栈关系为:brightness_store --> backlight_device_set_brightness --> backlight_update_status --> 回调update_status,最后回调函数即为在bl_power中注册的回调函数,最终调用pwm的接口,实现对pwm占空比的调整,从而实现对背光亮度的控制。

linux pwm 调屏_linux驱动---bl_pwm驱动与backlight class实现背光调整相关推荐

  1. linux pwm 调屏_Linux驱动学习之:PWM驱动

    PWM(Pulse Width Modulation)--脉宽调制,它是利用微控制器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用于测量.通信.功率控制与变换等许多领域. s3c2440 ...

  2. linux pwm 调屏_基于嵌入式Linux的LCD背光调节及驱动的实现

    0 引言 在手持式设备中,液晶显示屏的使用越来越广泛.由于LCD自身是不能发光的,它需要一个强劲的光源来给它提供背光,以便清晰地显示信息.这样的光源是非常耗电的,通常液晶显示屏的功耗常常占到系统总功耗 ...

  3. linux pwm 调屏_嵌入式Linux系统基于PWM机制的液晶屏背光Backlight功能配置

    在研发嵌入式产品时,往往会用到LCD(液晶屏)来显示图形界面,而液晶屏的显示亮度则需要背光系统去调节. 调光方法: 1.数字调光,又称波宽控制调光(Pulse Width Modulation,简称P ...

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

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

  5. 【应用】AP5153 线性降压恒流 PWM调光LED照明驱动 高压低压

    概述 AP5153 是一种 PWM 调光的.低压 差的 LED 线性降压恒流驱动器. AP5153 仅需要外接一个电阻和一个 NMOS 管就可以构成一个完整的 LED 恒 流驱动电路,调节该外接电阻就 ...

  6. STM32+FreeRTOS模拟手机PWM调光、屏幕解锁、定时休眠小项目

    本人初学freertos,手里只有一块STM32MP157裸板,屏幕都没有,显示都是通过串口模拟的,自己想了一个实验项目,可以用来熟悉freertos的基本内容.谢谢 目录 概述: 主要程序: 实验效 ...

  7. linux PWM驱动屏幕亮度及pwm子系统框架(Linux驱动开发篇)

    1.对象 imx6ull单片机,控制其下面的pwm3的外设.关于对象的详细介绍看裸机pwm控制屏幕亮度 在dtsi中的位置 /soc/aips1/pwm3 pwm3: pwm@02088000 { c ...

  8. Mni2440 linux PWM 驱动代码修改支持 频率,占空比修改--XiaoLin.Peng

    作者:XiaoLin.Peng 欢迎交流 196568501 #include <linux/module.h> #include <linux/kernel.h> #incl ...

  9. 【51单片机】直流电机的驱动和PWM调速

    51单片机驱动直流电机与 PWM 调速是通过使用 51 单片机来控制直流电机的转速和方向.51 单片机通过控制电机的电流来实现驱动,并通过生成 PWM 信号来调节电机的转速.使用 PWM 调速可以使得 ...

  10. OC7141 PWM 调光的线性降压 LED恒流驱动IC

    OC7141概述 OC7141 是一种带 PWM 调光功能的线  性降压 LED 恒流驱动器,仅需外接一个电阻和一个 NMOS 管就可以构成一个完整的LED 恒流驱动电路,调节该外接电阻就可以  调节 ...

最新文章

  1. UVA 10269 Adventure of Super Mario
  2. Java8的集合:HashMap的实现原理
  3. Python生成随机五位数——模仿手机验证码
  4. windows mysql 主从_mysql读写分离实战二-windows 上mysql主从数据库搭建及问题总结
  5. acwing算法题--多重背包问题二
  6. 【面试题】hashCode() 和 equals() 之间的关系
  7. 编译原理三大经典书籍(龙书 虎书 鲸书)
  8. 01_Weblogic课程之概念篇:代理服务器,web服务器,应用程序服务器,JNDI概念,JTA概念,Java消息服务,Java验证和授权(JAAS),Java管理扩展,Web客户机,客户机应用程序
  9. ZigBee技术的应用和优势
  10. LeetCode MySQL 1174. 即时食物配送 II
  11. matlab 一维 平滑,一维加噪信号的平滑处理(3)
  12. ThinkPHP6项目基操(3.控制器获取请求参数)
  13. 土财主休闲威客-可行性分析(评测)
  14. 计算机无法访问桌面,桌面无法显示_电脑桌面显示:无法访问,你可能没有权限使用网络......
  15. Web项目部署到阿里云
  16. 计算机中b代表的含义是什么意思,表示文件大小的MB,KB,B等是什么意思?
  17. Linux的进程管理,ssh创建远程连接与免密操作,文件传输,虚拟机联网
  18. PMBOK(第六版) 学习笔记 ——《第十三章 项目相关方管理》
  19. 短期盈利无望,Uber到底值多少钱?
  20. 一个域名下面能搭建多个网站吗?

热门文章

  1. ESP8266的Arduino IDE下载和TTL下载
  2. pickerView的简单使用
  3. 菜鸟教程java在线编辑器_HTML 编辑器
  4. 地点坐标拾取,经纬度精确到小数点后6位
  5. Phobos勒索病毒完整处理过程
  6. FreeFileSync 免费文件同步软件 实时自动备份重要资料
  7. Redis数据结构总结
  8. Windows系统安装Redis(详细)
  9. 从B站 (哔哩哔哩) 泄露的源码里发现了B站视频推荐的秘密
  10. 计算机数字雨代码,cmd命令数字雨教程