sys文件系统调节lcd背光

相关文章链接:sysfs文件系统分析-请点击这里查看文章

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;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
int dsi_panel_set_backlight(struct dsi_panel panel, u32 bl_lvl) { int rc = 0; struct dsi_backlight_config bl = &panel->bl_config; if (panel->host_config.ext_bridge_mode) return 0; DSI_DEBUG("backlight type:%d lvl:%d\n", bl->type, bl_lvl); switch (bl->type) { case DSI_BACKLIGHT_WLED: rc = backlight_device_set_brightness(bl->raw_bd, bl_lvl); break; case DSI_BACKLIGHT_DCS: rc = dsi_panel_update_backlight(panel, bl_lvl); break; case DSI_BACKLIGHT_EXTERNAL: rc = lcd_bl_set_led_brightness(bl_lvl); //pr_err("dsi set bias brightness: %d\n", bl_lvl); rc = lcd_bias_set_led_brightness(bl_lvl); //pr_err("dsi set brightness: %d\n", bl_lvl); break; case DSI_BACKLIGHT_PWM: rc = dsi_panel_update_pwm_backlight(panel, bl_lvl); break; default: DSI_ERR("Backlight type(%d) not supported\n", bl->type); rc = -ENOTSUPP; } return rc; } int lcd_bl_set_led_brightness(int value)//for set bringhtness { dev_warn(&lcd_bl_i2c_client->dev, "lcm 8866 bl = %d\n", value); if (value < 0) { dev_warn(&lcd_bl_i2c_client->dev, "value=%d\n", value); return 0; } if (value > 0) { lcd_bl_write_byte(KTZ8866_DISP_BB_LSB, value & 0x07);// lsb lcd_bl_write_byte(KTZ8866_DISP_BB_MSB, (value >> 3) & 0xFF);// msb lcd_bl_write_byte(KTZ8866_DISP_BL_ENABLE, 0x4F); / BL enabled and Current sink 1/2/3/4 enabled;/ } else { lcd_bl_write_byte(KTZ8866_DISP_BB_LSB, 0x00);// lsb lcd_bl_write_byte(KTZ8866_DISP_BB_MSB, 0x00);// msb lcd_bl_write_byte(KTZ8866_DISP_BL_ENABLE, 0x00); /* BL enabled and Current sink 1/2/3/4 disabled;/ } return 0; } int lcd_bias_set_led_brightness(int value)//for set bringhtness { dev_warn(&lcd_bl_bias_i2c_client->dev, "lcm 8866 bl = %d\n", value); if (value < 0) { dev_warn(&lcd_bl_bias_i2c_client->dev, "invalid value=%d\n", value); return 0; } if (value > 0) { lcd_bl_bias_write_byte(KTZ8866_DISP_BB_LSB, value & 0x07);// lsb lcd_bl_bias_write_byte(KTZ8866_DISP_BB_MSB, (value >> 3) & 0xFF);// msb lcd_bl_bias_write_byte(KTZ8866_DISP_BL_ENABLE, 0x4F); / BL enabled and Current sink 1/2/3/4 enabled;/ } else { lcd_bl_bias_write_byte(KTZ8866_DISP_BB_LSB, 0x00);// lsb lcd_bl_bias_write_byte(KTZ8866_DISP_BB_MSB, 0x00);// msb lcd_bl_bias_write_byte(KTZ8866_DISP_BL_ENABLE, 0x00); / BL enabled and Current sink 1/2/3/4 disabled;*/ } return 0; } dsi_panel_set_backlight、lcd_bl_set_led_brightness和lcd_bias_set_led_brightness源码如上,帮忙用工作队列的方式,同时实现在case DSI_BACKLIGHT_EXTERNAL:下并发执行lcd_bl_set_led_brightness和lcd_bias_set_led_brightness两个函数
06-07
好的,我可以为您提供一个使用工作队列来实现并发执行`lcd_bl_set_led_brightness`和`lcd_bias_set_led_brightness`的示例代码。具体实现方法如下: 1. 定义一个工作结构体`struct work_struct`,并在其中传递参数。 ``` struct backlight_work { struct work_struct work; struct i2c_client *lcd_bl_i2c_client; struct i2c_client *lcd_bl_bias_i2c_client; int value; }; ``` 2. 定义工作队列。 ``` static struct workqueue_struct *bl_workqueue; ``` 3. 在`dsi_panel_set_backlight`函数中,将`lcd_bl_set_led_brightness`和`lcd_bias_set_led_brightness`函数的调用封装成一个工作任务,并将任务推入工作队列。 ``` case DSI_BACKLIGHT_EXTERNAL: { struct backlight_work *bl_work; bl_work = kzalloc(sizeof(*bl_work), GFP_KERNEL); if (!bl_work) return -ENOMEM; INIT_WORK(&bl_work->work, backlight_worker); bl_work->lcd_bl_i2c_client = lcd_bl_i2c_client; bl_work->lcd_bl_bias_i2c_client = lcd_bl_bias_i2c_client; bl_work->value = bl_lvl; queue_work(bl_workqueue, &bl_work->work); break; } ``` 4. 在`backlight_worker`函数中,实现`lcd_bl_set_led_brightness`和`lcd_bias_set_led_brightness`函数的并发执行。 ``` static void backlight_worker(struct work_struct *work) { struct backlight_work *bl_work = container_of(work, struct backlight_work, work); int value = bl_work->value; if (value < 0) { dev_warn(&bl_work->lcd_bl_i2c_client->dev, "invalid value=%d\n", value); } else if (value > 0) { lcd_bl_write_byte(KTZ8866_DISP_BB_LSB, value & 0x07); lcd_bl_write_byte(KTZ8866_DISP_BB_MSB, (value >> 3) & 0xFF); lcd_bl_write_byte(KTZ8866_DISP_BL_ENABLE, 0x4F); lcd_bl_bias_write_byte(KTZ8866_DISP_BB_LSB, value & 0x07); lcd_bl_bias_write_byte(KTZ8866_DISP_BB_MSB, (value >> 3) & 0xFF); lcd_bl_bias_write_byte(KTZ8866_DISP_BL_ENABLE, 0x4F); } else { lcd_bl_write_byte(KTZ8866_DISP_BB_LSB, 0x00); lcd_bl_write_byte(KTZ8866_DISP_BB_MSB, 0x00); lcd_bl_write_byte(KTZ8866_DISP_BL_ENABLE, 0x00); lcd_bl_bias_write_byte(KTZ8866_DISP_BB_LSB, 0x00); lcd_bl_bias_write_byte(KTZ8866_DISP_BB_MSB, 0x00); lcd_bl_bias_write_byte(KTZ8866_DISP_BL_ENABLE, 0x00); } kfree(bl_work); } ``` 这样,当`dsi_panel_set_backlight`函数中调用`lcd_bl_set_led_brightness`和`lcd_bias_set_led_brightness`时,会将其封装成一个工作任务并推入工作队列中,`backlight_worker`函数实现这两个函数的并发执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值