sys的位置
/sys/class/backlight/lcd_backlight@0
cat max_brightness //获取最大的亮度:100
cat actual_brightness //获取实际的亮度:80
echo 100 > brightness //设置亮度为100
echo 80 > brightness //设置亮度为80
驱动位置:
/drivers/pwm/pwm-imx.c //imx的和cpu相关的pwm操作。
/drivers/pwm/core.c
/drivers/video/backlight/pwm_bl.c //这个驱动里面会和设备树匹配。
/drivers/video/backlight/backlight.c //对应于/sys/class/backlight,提供属性和操作函数
它们的调用关系:
PS:整个背光调节代码可以分为2个部分:1.pwm控制相关的。2.和backlight相关的(其中包含sysfs相关的操作)
设备树位置:
/arch/arm64/boot/dts/freescale/panel-atk-10-1.dtsi
源码分析
重要的数据结构
/* pwm控制器的抽象 */
struct pwm_chip {
struct device *dev;
struct list_head list;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device *pwms;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
};
其中最重要的数据结构如下:
/* pwm相关的主要操作函数,其中config()最重要 */
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 (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_capture *result, unsigned long timeout);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
...
};
PWM部分代码分析
pwm-imx.c中重要的函数分析:
/* 顶层的配置函数最终使用的这个函数来实现pwm配置 */
static int imx_pwm_config_v1(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns)
{
struct imx_chip *imx = to_imx_chip(chip);
...
u32 max = readl(imx->mmio_base + MX1_PWMP);
u32 p = max * duty_ns / period_ns;
writel(max - p, imx->mmio_base + MX1_PWMS);
return 0;
}
/* imx8mm使用的这个结构体 */
static const struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable_v1,
.disable = imx_pwm_disable_v1,
.config = imx_pwm_config_v1,
.owner = THIS_MODULE,
};
static int imx_pwm_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(imx_pwm_dt_ids, &pdev->dev);
/* 下面的两个结构体可以深入的看下,很重要 */
const struct imx_pwm_data *data;
struct imx_chip *imx;
...
data = of_id->data;
...
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL)
return -ENOMEM;
....
/* 在这里对操作函数进行关联 */
imx->chip.ops = data->ops;
imx->chip.dev = &pdev->dev;
imx->chip.base = -1;
imx->chip.npwm = 1;
...
/* 注册到pwm核心模块中,下面的函数在/driver/pwm/core.c中 */
ret = pwmchip_add(&imx->chip);
...
}
core.c中重要函数分析:
在/drivers/pwm/core.c中有3个关键函数,通过这3个函数就能理解驱动架构的操作过程。
/* 这个函数的主要作用就是向系统添加pwm控制器 */
int pwmchip_add_with_polarity(struct pwm_chip *chip,
enum pwm_polarity polarity)
{
...
chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);
for (i = 0; i < chip->npwm; i++) {
pwm = &chip->pwms[i];
pwm->chip = chip;
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
pwm->state.polarity = polarity;
if (chip->ops->get_state)
chip->ops->get_state(chip, pwm, &pwm->state);
radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
}
bitmap_set(allocated_pwms, chip->base, chip->npwm);
INIT_LIST_HEAD(&chip->list);
list_add(&chip->list, &pwm_chips);
if (IS_ENABLED(CONFIG_OF))
of_pwmchip_add(chip);
pwmchip_sysfs_export(chip);
}
EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
/* 下面的这个函数实际是调用上面的函数来操作的 */
int pwmchip_add(struct pwm_chip *chip)
{
return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL);
}
EXPORT_SYMBOL_GPL(pwmchip_add);
/* pwm架构中的中间调用函数,这个函数中的使用到的函数都是
* pwm-imx.c中向/drivers/pwm/core.c中注册的函数
*/
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
{
...
if (pwm->chip->ops->apply) {
err = pwm->chip->ops->apply(pwm->chip, pwm, state);
} else {
if (state->polarity != pwm->state.polarity) {
err = pwm->chip->ops->set_polarity(pwm->chip, pwm,
state->polarity);
}
if (state->period != pwm->state.period ||
state->duty_cycle != pwm->state.duty_cycle) {
/* 在sysfs文件系统中设置pwm的参数就是使用下面的操作 */
err = pwm->chip->ops->config(pwm->chip, pwm,
state->duty_cycle,
state->period);
...
pwm->state.duty_cycle = state->duty_cycle;
pwm->state.period = state->period;
}
if (state->enabled != pwm->state.enabled) {
if (state->enabled) {
err = pwm->chip->ops->enable(pwm->chip, pwm);
} else {
pwm->chip->ops->disable(pwm->chip, pwm);
}
pwm->state.enabled = state->enabled;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(pwm_apply_state);
backlight代码分析
pwm_bl.c中重要函数分析
/* 更新状态的核心函数,在backlight.c中的bl_power_store()函数最终
* 调用的就是这个函数
*/
static int pwm_backlight_update_status(struct backlight_device *bl)
{
/* 设置函数 */
if (brightness > 0) {
duty_cycle = compute_duty_cycle(pb, brightness);
/* pb->pwm数据结构底层包含有对应的pwm操作函数 */
pwm_config(pb->pwm, duty_cycle, pb->period);
pwm_backlight_power_on(pb, brightness);
} else
pwm_backlight_power_off(pb);
}
pwm_bl.c中的探测函数:
static int pwm_backlight_probe(struct platform_device *pdev)
{
...
/* 这个函数调用里面会打印没有申请到regulater信息 */
pb->power_supply = devm_regulator_get(&pdev->dev, "power");
if (IS_ERR(pb->power_supply)) {
ret = PTR_ERR(pb->power_supply);
goto err_alloc;
}
/* 调用/driver/pwm/core.c中的函数 */
pb->pwm = devm_pwm_get(&pdev->dev, NULL);
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");
}
pwm_apply_args(pb->pwm);
...
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = data->max_brightness;
/* 核心函数 */
bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
&pwm_backlight_ops, &props);
bl->props.brightness = data->dft_brightness;
bl->props.power = pwm_backlight_initial_power_state(pb);
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
return 0;
}
backlight.c中主要函数分析:
上面的backlight_device_register函数在/drivers/video/backlight/backlight.c中实现:
struct backlight_device *backlight_device_register(const char *name,
struct device *parent, void *devdata, const struct backlight_ops *ops,
const struct backlight_properties *props)
{
new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
/* 赋值本页面中的的静态变量 */
new_bd->dev.class = backlight_class;
new_bd->dev.parent = parent;
new_bd->dev.release = bl_device_release;
/* 设置kobject的名字 */
dev_set_name(&new_bd->dev, "%s", name);
dev_set_drvdata(&new_bd->dev, devdata);
rc = device_register(&new_bd->dev);
rc = backlight_register_fb(new_bd);
/* 设置操作函数 */
new_bd->ops = ops;
mutex_lock(&backlight_dev_list_mutex);
list_add(&new_bd->entry, &backlight_dev_list);
mutex_unlock(&backlight_dev_list_mutex);
blocking_notifier_call_chain(&backlight_notifier,
BACKLIGHT_REGISTERED, new_bd);
}
和sysfs相关的操作:
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,
};
/* 这个宏会定义一个group的属性,然后和上面的数组数据关联起来
* 展开后的数据结构为bl_device_groups
*/
ATTRIBUTE_GROUPS(bl_device);
下面以设置亮度为例来说明,其它的属性类似,实际上更简单。
int backlight_device_set_brightness(struct backlight_device *bd,
unsigned long brightness)
{
...
if (bd->ops) {
if (brightness > bd->props.max_brightness)
rc = -EINVAL;
else {
pr_debug("set brightness to %lu\n", brightness);
bd->props.brightness = brightness;
/* 调用背光设置函数,见函数里面,典型的c面向对象的思想 */
rc = backlight_update_status(bd);
}
}
}
EXPORT_SYMBOL(backlight_device_set_brightness);
/* 设置背光的顶层函数 */
static ssize_t brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct backlight_device *bd = to_backlight_device(dev);
unsigned long brightness;
...
rc = backlight_device_set_brightness(bd, brightness);
}
/* 这个函数会定义一个结构体和上面的函数关联起来 */
static DEVICE_ATTR_RW(brightness);
例程代码
下面的两种方式都可以操作。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define LCD_BACKLIGHT "/sys/class/backlight/lcd_backlight@0/brightness"
int main(int argc, char *argv[])
{
#if 0
int fd;
char *testwrite = "/sys/class/backlight/lcd_backlight@0/brightness";
ssize_t len_w;
char buf_w[] = "10";
//if((fd = open("/sys/class/backlight/lcd_backlight@0/brightness", O_RDWR))<0){
/* open()可以带2/3个参数 */
//if((fd = open(testwrite,O_RDWR,0777))<0){
if((fd = open(LCD_BACKLIGHT, O_RDWR, 0777)) <0 ){
printf("open %s failed!\n",testwrite);
}
//len_w = write(fd, "99", 2);
len_w = write(fd, buf_w, strlen(buf_w));
if(len_w == -1){
perror("write");
}
else{
printf("write function ok!\n");
}
close(fd);
#else
FILE * fp =fopen("/sys/class/backlight/lcd_backlight@0/brightness","w");
if (fp == NULL)
perror("export open filed");
else
fprintf(fp,"%d",100);
fclose(fp);
#endif
return 0;
}