由于液晶屏是由屏幕和背光两部分组成,液晶的排列不同又导致屏幕分为常黑屏和常白屏,在系统起来一瞬间液晶的初始化需要一段时间,此时的液晶状态是不确定的,倘若背光起来的时候,液晶还未初始化完毕,通常会遇到屏幕闪白屏或花屏的现象;
通常嵌入式系统都将背光开光单独拿出来由IO控制,系统起来的一瞬间总是会看到闪花屏或白屏,一种解决办法就是延迟背光点亮的时间,在要求快速启动的嵌入式linux系统中背光延时打开的方法,似乎写此类文章的人不多,如下代码提供一种开启kthread_create内核线程的的方式来延迟打开背光;
在pwm驱动的probe函数里创建一个内核线程
-
static int pwm_backlight_probe(struct platform_device *pdev)
-
{
-
......
-
// add-begin by xiongqin.bi
-
bl_delay_task =
kthread_create(bl_on_fuction, bl,
"bl_delay");
-
if(
IS_ERR(bl_delay_task))
-
{
-
printk(
"[bgk] bl_on kthread_create Unable to start kernel thread. \n");
-
err =
PTR_ERR(bl_delay_task);
-
bl_delay_task =
NULL;
-
goto err_thread;
-
}
-
wake_up_process(bl_delay_task);
-
// add-end by xiongqin.bi
-
......
-
return ret;
-
}
在线程里进行延时,然后打开背光
-
int bl_on_fuction(void *arg)
-
{
-
struct
backlight_device *bl;
-
bl = (
struct backlight_device *)arg;
-
-
printk(
"[bgk] bl_on gpio before mdelay \n");
-
msleep(
800);
-
-
bl->props.power =
0;
// xiongqin.bi
-
backlight_update_status(bl);
-
-
printk(
"[bgk] bl_on gpio after mdeay \n");
-
-
return
0;
-
}
功能简单的实现了,最后注销内核线程
-
static int pwm_backlight_remove(struct platform_device *pdev)
-
{
-
struct
backlight_device *bl =
platform_get_drvdata(pdev);
-
struct
pwm_bl_data *pb =
bl_get_data(bl);
-
-
backlight_device_unregister(bl);
-
pwm_backlight_power_off(pb);
-
// add-begin by xiongqin.bi
-
if (bl_delay_task)
-
{
-
kthread_stop(bl_delay_task);
-
bl_delay_task =
NULL;
-
}
-
// add-end by xiongqin.bi
-
if (pb->exit)
-
pb->
exit(&pdev->dev);
-
-
return
0;
-
}
附上pwm_bl.c完整代码
-
/*
-
* linux/drivers/video/backlight/pwm_bl.c
-
*
-
* simple PWM based backlight control, board code has to setup
-
* 1) pin configuration so PWM waveforms can output
-
* 2) platform_data being correctly configured
-
*
-
* This program is free software; you can redistribute it and/or modify
-
* it under the terms of the GNU General Public License version 2 as
-
* published by the Free Software Foundation.
-
*/
-
-
#include <linux/gpio.h>
-
#include <linux/of_gpio.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/platform_device.h>
-
#include <linux/fb.h>
-
#include <linux/backlight.h>
-
#include <linux/err.h>
-
#include <linux/pwm.h>
-
#include <linux/pwm_backlight.h>
-
#include <linux/regulator/consumer.h>
-
#include <linux/slab.h>
-
-
#include <linux/clk.h>
-
#include <linux/delay.h>
-
#include <linux/param.h>
-
#include <linux/jiffies.h>
-
#include <asm/system.h>
-
#include <asm/processor.h>
-
#include <linux/kthread.h>
-
-
struct
pwm_bl_data {
-
struct
pwm_device *pwm;
-
struct
device *dev;
-
unsigned
int period;
-
unsigned
int lth_brightness;
-
unsigned
int *levels;
-
bool enabled;
-
struct
regulator *power_supply;
-
int enable_gpio;
-
unsigned
long enable_gpio_flags;
-
unsigned
int scale;
-
int (*notify)(
struct device *,
-
int brightness);
-
void (*notify_after)(
struct device *,
-
int brightness);
-
int (*check_fb)(
struct device *,
struct fb_info *);
-
void (*exit)(
struct device *);
-
};
-
-
static
struct
task_struct *bl_delay_task;
// add by xiongqin.bi
-
-
static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
-
{
-
int err;
-
-
if (pb->enabled)
-
return;
-
-
err =
regulator_enable(pb->power_supply);
-
if (err <
0)
-
dev_err(pb->dev,
"failed to enable power supply\n");
-
-
if (
gpio_is_valid(pb->enable_gpio)) {
-
if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
-
gpio_set_value(pb->enable_gpio,
0);
-
else
-
gpio_set_value(pb->enable_gpio,
1);
-
}
-
-
pwm_enable(pb->pwm);
-
pb->enabled =
true;
-
}
-
-
static void pwm_backlight_power_off(struct pwm_bl_data *pb)
-
{
-
if (!pb->enabled)
-
return;
-
-
pwm_config(pb->pwm,
0, pb->period);
-
pwm_disable(pb->pwm);
-
-
if (
gpio_is_valid(pb->enable_gpio)) {
-
if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
-
gpio_set_value(pb->enable_gpio,
1);
-
else
-
gpio_set_value(pb->enable_gpio,
0);
-
}
-
-
regulator_disable(pb->power_supply);
-
pb->enabled =
false;
-
}
-
-
static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
-
{
-
unsigned
int lth = pb->lth_brightness;
-
int duty_cycle;
-
-
if (pb->levels)
-
duty_cycle = pb->levels[brightness];
-
else
-
duty_cycle = brightness;
-
-
return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
-
}
-
-
// add-begin by bixiongqin for backlight gradual
-
static int compute_duty_cycle_gradual(struct pwm_bl_data *pb, int brightness, int old_brightness, int cur_range, int total_range)
-
{
-
unsigned
int lth = pb->lth_brightness;
-
int duty_cycle;
-
int old_duty_cycle;
-
int i_gap;
-
int i_range;
-
-
if (pb->levels)
-
{
-
duty_cycle = pb->levels[brightness];
-
old_duty_cycle = pb->levels[old_brightness];
-
}
-
else
-
{
-
duty_cycle = brightness;
-
}
-
-
duty_cycle = duty_cycle *
100;
-
old_duty_cycle = old_duty_cycle *
100;
-
-
i_gap = duty_cycle - old_duty_cycle;
-
i_range = i_gap / total_range;
-
duty_cycle = (old_duty_cycle + i_range * cur_range) /
100;
-
-
return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
-
}
-
// add-end by bixiongqin for backlight gradual
-
-
// modified-begin by bixiongqin for backlight gradual
-
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 old_brightness = bl->props.old_brightness;
-
int duty_cycle;
-
-
int total_range;
-
int cur_range;
-
-
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);
-
-
total_range =
20;
-
for (cur_range =
1; cur_range <= total_range; cur_range++)
-
{
-
if (brightness >
0) {
-
//duty_cycle = compute_duty_cycle(pb, brightness);
-
duty_cycle =
compute_duty_cycle_gradual(pb, brightness, old_brightness, cur_range, total_range);
-
pwm_config(pb->pwm, duty_cycle, pb->period);
-
pwm_backlight_power_on(pb, brightness);
-
}
else
-
{
-
// pwm_backlight_power_off(pb);
-
duty_cycle =
compute_duty_cycle_gradual(pb, brightness, old_brightness, cur_range, total_range);
-
pwm_config(pb->pwm, duty_cycle, pb->period);
-
pwm_backlight_power_on(pb, brightness);
-
}
-
-
if (pb->notify_after)
-
pb->
notify_after(pb->dev, brightness);
-
-
msleep(
8);
-
}
-
bl->props.old_brightness = brightness;
-
-
if (brightness ==
0)
-
{
-
pwm_backlight_power_off(pb);
-
}
-
-
return
0;
-
}
-
// modified-end by bixiongqin for backlight gradual
-
-
static int pwm_backlight_get_brightness(struct backlight_device *bl)
-
{
-
return bl->props.brightness;
-
}
-
-
static int pwm_backlight_check_fb(struct backlight_device *bl,
-
struct fb_info *info)
-
{
-
struct
pwm_bl_data *pb =
bl_get_data(bl);
-
-
return !pb->check_fb || pb->
check_fb(pb->dev, info);
-
}
-
-
static
const
struct
backlight_ops pwm_backlight_ops = {
-
.update_status = pwm_backlight_update_status,
-
.get_brightness = pwm_backlight_get_brightness,
-
.check_fb = pwm_backlight_check_fb,
-
};
-
-
#ifdef CONFIG_OF
-
static int pwm_backlight_parse_dt(struct device *dev,
-
struct platform_pwm_backlight_data *data)
-
{
-
struct
device_node *node = dev->of_node;
-
enum
of_gpio_flags flags;
-
struct
property *prop;
-
int length;
-
u32 value;
-
int ret;
-
-
if (!node)
-
return -ENODEV;
-
-
memset(data,
0,
sizeof(*data));
-
-
/* determine the number of brightness levels */
-
prop =
of_find_property(node,
"brightness-levels", &length);
-
if (!prop)
-
return -EINVAL;
-
-
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);
-
if (!data->levels)
-
return -ENOMEM;
-
-
ret =
of_property_read_u32_array(node,
"brightness-levels",
-
data->levels,
-
data->max_brightness);
-
if (ret <
0)
-
return ret;
-
-
ret =
of_property_read_u32(node,
"default-brightness-level",
-
&value);
-
if (ret <
0)
-
return ret;
-
-
data->dft_brightness = value;
-
data->max_brightness--;
-
}
-
-
data->enable_gpio =
of_get_named_gpio_flags(node,
"enable-gpios",
0,
-
&flags);
-
if (data->enable_gpio == -EPROBE_DEFER)
-
return -EPROBE_DEFER;
-
-
if (
gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW))
-
data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW;
-
-
return
0;
-
}
-
-
static
struct
of_device_id pwm_backlight_of_match[] = {
-
{ .compatible =
"pwm-backlight" },
-
{ }
-
};
-
-
MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
-
#else
-
static int pwm_backlight_parse_dt(struct device *dev,
-
struct platform_pwm_backlight_data *data)
-
{
-
return -ENODEV;
-
}
-
#endif
-
-
// add-begin by xiongqin
-
int bl_on_fuction(void *arg)
-
{
-
struct
backlight_device *bl;
-
bl = (
struct backlight_device *)arg;
-
-
printk(
"[bgk] bl_on gpio before mdelay \n");
-
msleep(
800);
-
-
bl->props.power =
0;
// xiongqin.bi
-
backlight_update_status(bl);
-
-
printk(
"[bgk] bl_on gpio after mdeay \n");
-
-
return
0;
-
}
-
// add-end by xiongqin
-
-
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
pwm_bl_data *pb;
-
int ret;
-
int err;
-
-
if (!data) {
-
ret =
pwm_backlight_parse_dt(&pdev->dev, &defdata);
-
if (ret <
0) {
-
dev_err(&pdev->dev,
"failed to find platform data\n");
-
return ret;
-
}
-
-
data = &defdata;
-
}
-
-
if (data->init) {
-
ret = data->
init(&pdev->dev);
-
if (ret <
0)
-
return ret;
-
}
-
-
pb =
devm_kzalloc(&pdev->dev,
sizeof(*pb), GFP_KERNEL);
-
if (!pb) {
-
ret = -ENOMEM;
-
goto err_alloc;
-
}
-
-
if (data->levels) {
-
unsigned
int i;
-
-
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->enable_gpio = data->enable_gpio;
-
pb->enable_gpio_flags = data->enable_gpio_flags;
-
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;
-
-
if (
gpio_is_valid(pb->enable_gpio)) {
-
unsigned
long flags;
-
-
if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
-
flags = GPIOF_OUT_INIT_HIGH;
-
else
-
flags = GPIOF_OUT_INIT_LOW;
-
-
ret =
gpio_request_one(pb->enable_gpio, flags,
"enable");
-
if (ret <
0) {
-
dev_err(&pdev->dev,
"failed to request GPIO#%d: %d\n",
-
pb->enable_gpio, ret);
-
goto err_alloc;
-
}
-
}
-
-
pb->power_supply =
devm_regulator_get(&pdev->dev,
"power");
-
if (
IS_ERR(pb->power_supply)) {
-
ret =
PTR_ERR(pb->power_supply);
-
goto err_gpio;
-
}
-
-
pb->pwm =
devm_pwm_get(&pdev->dev,
NULL);
-
if (
IS_ERR(pb->pwm)) {
-
dev_err(&pdev->dev,
"unable to request PWM, trying legacy API\n");
-
-
pb->pwm =
pwm_request(data->pwm_id,
"pwm-backlight");
-
if (
IS_ERR(pb->pwm)) {
-
dev_err(&pdev->dev,
"unable to request legacy PWM\n");
-
ret =
PTR_ERR(pb->pwm);
-
goto err_gpio;
-
}
-
}
-
-
dev_dbg(&pdev->dev,
"got pwm for backlight\n");
-
-
/*
-
* The DT case will set the pwm_period_ns field to 0 and store the
-
* period, parsed from the DT, in the PWM device. For the non-DT case,
-
* set the period from platform data.
-
*/
-
if (data->pwm_period_ns >
0)
-
pwm_set_period(pb->pwm, data->pwm_period_ns);
-
-
pb->period =
pwm_get_period(pb->pwm);
-
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;
-
bl =
backlight_device_register(
dev_name(&pdev->dev), &pdev->dev, pb,
-
&pwm_backlight_ops, &props);
-
if (
IS_ERR(bl)) {
-
dev_err(&pdev->dev,
"failed to register backlight\n");
-
ret =
PTR_ERR(bl);
-
goto err_gpio;
-
}
-
-
if (data->dft_brightness > data->max_brightness) {
-
dev_warn(&pdev->dev,
-
"invalid default brightness level: %u, using %u\n",
-
data->dft_brightness, data->max_brightness);
-
data->dft_brightness = data->max_brightness;
-
}
-
-
bl->props.brightness = data->dft_brightness;
-
-
bl->props.power =
1;
// xiongqin.bi
-
-
backlight_update_status(bl);
-
-
platform_set_drvdata(pdev, bl);
-
-
// add-begin by xiongqin.bi
-
bl_delay_task =
kthread_create(bl_on_fuction, bl,
"bl_delay");
-
if(
IS_ERR(bl_delay_task))
-
{
-
printk(
"[bgk] bl_on kthread_create Unable to start kernel thread. \n");
-
err =
PTR_ERR(bl_delay_task);
-
bl_delay_task =
NULL;
-
goto err_thread;
-
}
-
wake_up_process(bl_delay_task);
-
// add-end by xiongqin.bi
-
-
err_thread:
-
return
0;
-
-
err_gpio:
-
if (
gpio_is_valid(pb->enable_gpio))
-
gpio_free(pb->enable_gpio);
-
err_alloc:
-
if (data->exit)
-
data->
exit(&pdev->dev);
-
return ret;
-
}
-
-
static int pwm_backlight_remove(struct platform_device *pdev)
-
{
-
struct
backlight_device *bl =
platform_get_drvdata(pdev);
-
struct
pwm_bl_data *pb =
bl_get_data(bl);
-
-
backlight_device_unregister(bl);
-
pwm_backlight_power_off(pb);
-
// add-begin by xiongqin.bi
-
if (bl_delay_task)
-
{
-
kthread_stop(bl_delay_task);
-
bl_delay_task =
NULL;
-
}
-
// add-end by xiongqin.bi
-
if (pb->exit)
-
pb->
exit(&pdev->dev);
-
-
return
0;
-
}
-
-
#ifdef CONFIG_PM_SLEEP
-
static int pwm_backlight_suspend(struct device *dev)
-
{
-
struct
backlight_device *bl =
dev_get_drvdata(dev);
-
struct
pwm_bl_data *pb =
bl_get_data(bl);
-
-
if (pb->notify)
-
pb->
notify(pb->dev,
0);
-
-
pwm_backlight_power_off(pb);
-
-
if (pb->notify_after)
-
pb->
notify_after(pb->dev,
0);
-
-
return
0;
-
}
-
-
static int pwm_backlight_resume(struct device *dev)
-
{
-
struct
backlight_device *bl =
dev_get_drvdata(dev);
-
-
backlight_update_status(bl);
-
-
return
0;
-
}
-
#endif
-
-
static
const
struct
dev_pm_ops pwm_backlight_pm_ops = {
-
#ifdef CONFIG_PM_SLEEP
-
.suspend = pwm_backlight_suspend,
-
.resume = pwm_backlight_resume,
-
.poweroff = pwm_backlight_suspend,
-
.restore = pwm_backlight_resume,
-
#endif
-
};
-
-
static
struct
platform_driver pwm_backlight_driver = {
-
.driver = {
-
.name =
"pwm-backlight",
-
.owner = THIS_MODULE,
-
.pm = &pwm_backlight_pm_ops,
-
.of_match_table =
of_match_ptr(pwm_backlight_of_match),
-
},
-
.probe = pwm_backlight_probe,
-
.remove = pwm_backlight_remove,
-
};
-
-
module_platform_driver(pwm_backlight_driver);
-
-
MODULE_DESCRIPTION(
"PWM based Backlight Driver");
-
MODULE_LICENSE(
"GPL");
-
MODULE_ALIAS(
"platform:pwm-backlight");
上边的代码里有一段是做pwm背光渐变的程序,下一篇文章讲pwm渐变的实现方法