linux背光系统--背光延时点亮

由于液晶屏是由屏幕和背光两部分组成,液晶的排列不同又导致屏幕分为常黑屏和常白屏,在系统起来一瞬间液晶的初始化需要一段时间,此时的液晶状态是不确定的,倘若背光起来的时候,液晶还未初始化完毕,通常会遇到屏幕闪白屏或花屏的现象;

通常嵌入式系统都将背光开光单独拿出来由IO控制,系统起来的一瞬间总是会看到闪花屏或白屏,一种解决办法就是延迟背光点亮的时间,在要求快速启动的嵌入式linux系统中背光延时打开的方法,似乎写此类文章的人不多,如下代码提供一种开启kthread_create内核线程的的方式来延迟打开背光;


在pwm驱动的probe函数里创建一个内核线程


   
   
  1. static int pwm_backlight_probe(struct platform_device *pdev)
  2. {
  3. ......
  4. // add-begin by xiongqin.bi
  5. bl_delay_task = kthread_create(bl_on_fuction, bl, "bl_delay");
  6. if( IS_ERR(bl_delay_task))
  7. {
  8. printk( "[bgk] bl_on kthread_create Unable to start kernel thread. \n");
  9. err = PTR_ERR(bl_delay_task);
  10. bl_delay_task = NULL;
  11. goto err_thread;
  12. }
  13. wake_up_process(bl_delay_task);
  14. // add-end by xiongqin.bi
  15. ......
  16.   return ret;
  17. }

在线程里进行延时,然后打开背光


   
   
  1. int bl_on_fuction(void *arg)
  2. {
  3. struct backlight_device *bl;
  4. bl = ( struct backlight_device *)arg;
  5. printk( "[bgk] bl_on gpio before mdelay \n");
  6. msleep( 800);
  7. bl->props.power = 0; // xiongqin.bi
  8. backlight_update_status(bl);
  9. printk( "[bgk] bl_on gpio after mdeay \n");
  10. return 0;
  11. }

功能简单的实现了,最后注销内核线程


   
   
  1. static int pwm_backlight_remove(struct platform_device *pdev)
  2. {
  3. struct backlight_device *bl = platform_get_drvdata(pdev);
  4. struct pwm_bl_data *pb = bl_get_data(bl);
  5. backlight_device_unregister(bl);
  6. pwm_backlight_power_off(pb);
  7. // add-begin by xiongqin.bi
  8. if (bl_delay_task)
  9. {
  10. kthread_stop(bl_delay_task);
  11. bl_delay_task = NULL;
  12. }
  13. // add-end by xiongqin.bi
  14. if (pb->exit)
  15. pb-> exit(&pdev->dev);
  16. return 0;
  17. }

附上pwm_bl.c完整代码


   
   
  1. /*
  2. * linux/drivers/video/backlight/pwm_bl.c
  3. *
  4. * simple PWM based backlight control, board code has to setup
  5. * 1) pin configuration so PWM waveforms can output
  6. * 2) platform_data being correctly configured
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/gpio.h>
  13. #include <linux/of_gpio.h>
  14. #include <linux/module.h>
  15. #include <linux/kernel.h>
  16. #include <linux/init.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/fb.h>
  19. #include <linux/backlight.h>
  20. #include <linux/err.h>
  21. #include <linux/pwm.h>
  22. #include <linux/pwm_backlight.h>
  23. #include <linux/regulator/consumer.h>
  24. #include <linux/slab.h>
  25. #include <linux/clk.h>
  26. #include <linux/delay.h>
  27. #include <linux/param.h>
  28. #include <linux/jiffies.h>
  29. #include <asm/system.h>
  30. #include <asm/processor.h>
  31. #include <linux/kthread.h>
  32. struct pwm_bl_data {
  33. struct pwm_device *pwm;
  34. struct device *dev;
  35. unsigned int period;
  36. unsigned int lth_brightness;
  37. unsigned int *levels;
  38. bool enabled;
  39. struct regulator *power_supply;
  40. int enable_gpio;
  41. unsigned long enable_gpio_flags;
  42. unsigned int scale;
  43. int (*notify)( struct device *,
  44. int brightness);
  45. void (*notify_after)( struct device *,
  46. int brightness);
  47. int (*check_fb)( struct device *, struct fb_info *);
  48. void (*exit)( struct device *);
  49. };
  50. static struct task_struct *bl_delay_task; // add by xiongqin.bi
  51. static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
  52. {
  53. int err;
  54. if (pb->enabled)
  55. return;
  56. err = regulator_enable(pb->power_supply);
  57. if (err < 0)
  58. dev_err(pb->dev, "failed to enable power supply\n");
  59. if ( gpio_is_valid(pb->enable_gpio)) {
  60. if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
  61. gpio_set_value(pb->enable_gpio, 0);
  62. else
  63. gpio_set_value(pb->enable_gpio, 1);
  64. }
  65. pwm_enable(pb->pwm);
  66. pb->enabled = true;
  67. }
  68. static void pwm_backlight_power_off(struct pwm_bl_data *pb)
  69. {
  70. if (!pb->enabled)
  71. return;
  72. pwm_config(pb->pwm, 0, pb->period);
  73. pwm_disable(pb->pwm);
  74. if ( gpio_is_valid(pb->enable_gpio)) {
  75. if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
  76. gpio_set_value(pb->enable_gpio, 1);
  77. else
  78. gpio_set_value(pb->enable_gpio, 0);
  79. }
  80. regulator_disable(pb->power_supply);
  81. pb->enabled = false;
  82. }
  83. static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
  84. {
  85. unsigned int lth = pb->lth_brightness;
  86. int duty_cycle;
  87. if (pb->levels)
  88. duty_cycle = pb->levels[brightness];
  89. else
  90. duty_cycle = brightness;
  91. return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
  92. }
  93. // add-begin by bixiongqin for backlight gradual
  94. static int compute_duty_cycle_gradual(struct pwm_bl_data *pb, int brightness, int old_brightness, int cur_range, int total_range)
  95. {
  96. unsigned int lth = pb->lth_brightness;
  97. int duty_cycle;
  98. int old_duty_cycle;
  99. int i_gap;
  100. int i_range;
  101. if (pb->levels)
  102. {
  103. duty_cycle = pb->levels[brightness];
  104. old_duty_cycle = pb->levels[old_brightness];
  105. }
  106. else
  107. {
  108. duty_cycle = brightness;
  109. }
  110. duty_cycle = duty_cycle * 100;
  111. old_duty_cycle = old_duty_cycle * 100;
  112. i_gap = duty_cycle - old_duty_cycle;
  113. i_range = i_gap / total_range;
  114. duty_cycle = (old_duty_cycle + i_range * cur_range) / 100;
  115. return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
  116. }
  117. // add-end by bixiongqin for backlight gradual
  118. // modified-begin by bixiongqin for backlight gradual
  119. static int pwm_backlight_update_status(struct backlight_device *bl)
  120. {
  121. struct pwm_bl_data *pb = bl_get_data(bl);
  122. int brightness = bl->props.brightness;
  123. int old_brightness = bl->props.old_brightness;
  124. int duty_cycle;
  125. int total_range;
  126. int cur_range;
  127. if (bl->props.power != FB_BLANK_UNBLANK ||
  128. bl->props.fb_blank != FB_BLANK_UNBLANK ||
  129. bl->props.state & BL_CORE_FBBLANK)
  130. brightness = 0;
  131. if (pb->notify)
  132. brightness = pb-> notify(pb->dev, brightness);
  133. total_range = 20;
  134. for (cur_range = 1; cur_range <= total_range; cur_range++)
  135. {
  136. if (brightness > 0) {
  137. //duty_cycle = compute_duty_cycle(pb, brightness);
  138. duty_cycle = compute_duty_cycle_gradual(pb, brightness, old_brightness, cur_range, total_range);
  139. pwm_config(pb->pwm, duty_cycle, pb->period);
  140. pwm_backlight_power_on(pb, brightness);
  141. } else
  142. {
  143. // pwm_backlight_power_off(pb);
  144. duty_cycle = compute_duty_cycle_gradual(pb, brightness, old_brightness, cur_range, total_range);
  145. pwm_config(pb->pwm, duty_cycle, pb->period);
  146. pwm_backlight_power_on(pb, brightness);
  147. }
  148. if (pb->notify_after)
  149. pb-> notify_after(pb->dev, brightness);
  150. msleep( 8);
  151. }
  152. bl->props.old_brightness = brightness;
  153. if (brightness == 0)
  154. {
  155. pwm_backlight_power_off(pb);
  156. }
  157. return 0;
  158. }
  159. // modified-end by bixiongqin for backlight gradual
  160. static int pwm_backlight_get_brightness(struct backlight_device *bl)
  161. {
  162. return bl->props.brightness;
  163. }
  164. static int pwm_backlight_check_fb(struct backlight_device *bl,
  165. struct fb_info *info)
  166. {
  167. struct pwm_bl_data *pb = bl_get_data(bl);
  168. return !pb->check_fb || pb-> check_fb(pb->dev, info);
  169. }
  170. static const struct backlight_ops pwm_backlight_ops = {
  171. .update_status = pwm_backlight_update_status,
  172. .get_brightness = pwm_backlight_get_brightness,
  173. .check_fb = pwm_backlight_check_fb,
  174. };
  175. #ifdef CONFIG_OF
  176. static int pwm_backlight_parse_dt(struct device *dev,
  177. struct platform_pwm_backlight_data *data)
  178. {
  179. struct device_node *node = dev->of_node;
  180. enum of_gpio_flags flags;
  181. struct property *prop;
  182. int length;
  183. u32 value;
  184. int ret;
  185. if (!node)
  186. return -ENODEV;
  187. memset(data, 0, sizeof(*data));
  188. /* determine the number of brightness levels */
  189. prop = of_find_property(node, "brightness-levels", &length);
  190. if (!prop)
  191. return -EINVAL;
  192. data->max_brightness = length / sizeof(u32);
  193. /* read brightness levels from DT property */
  194. if (data->max_brightness > 0) {
  195. size_t size = sizeof(*data->levels) * data->max_brightness;
  196. data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
  197. if (!data->levels)
  198. return -ENOMEM;
  199. ret = of_property_read_u32_array(node, "brightness-levels",
  200. data->levels,
  201. data->max_brightness);
  202. if (ret < 0)
  203. return ret;
  204. ret = of_property_read_u32(node, "default-brightness-level",
  205. &value);
  206. if (ret < 0)
  207. return ret;
  208. data->dft_brightness = value;
  209. data->max_brightness--;
  210. }
  211. data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0,
  212. &flags);
  213. if (data->enable_gpio == -EPROBE_DEFER)
  214. return -EPROBE_DEFER;
  215. if ( gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW))
  216. data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW;
  217. return 0;
  218. }
  219. static struct of_device_id pwm_backlight_of_match[] = {
  220. { .compatible = "pwm-backlight" },
  221. { }
  222. };
  223. MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
  224. #else
  225. static int pwm_backlight_parse_dt(struct device *dev,
  226. struct platform_pwm_backlight_data *data)
  227. {
  228. return -ENODEV;
  229. }
  230. #endif
  231. // add-begin by xiongqin
  232. int bl_on_fuction(void *arg)
  233. {
  234. struct backlight_device *bl;
  235. bl = ( struct backlight_device *)arg;
  236. printk( "[bgk] bl_on gpio before mdelay \n");
  237. msleep( 800);
  238. bl->props.power = 0; // xiongqin.bi
  239. backlight_update_status(bl);
  240. printk( "[bgk] bl_on gpio after mdeay \n");
  241. return 0;
  242. }
  243. // add-end by xiongqin
  244. static int pwm_backlight_probe(struct platform_device *pdev)
  245. {
  246. struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
  247. struct platform_pwm_backlight_data defdata;
  248. struct backlight_properties props;
  249. struct backlight_device *bl;
  250. struct pwm_bl_data *pb;
  251. int ret;
  252. int err;
  253. if (!data) {
  254. ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
  255. if (ret < 0) {
  256. dev_err(&pdev->dev, "failed to find platform data\n");
  257. return ret;
  258. }
  259. data = &defdata;
  260. }
  261. if (data->init) {
  262. ret = data-> init(&pdev->dev);
  263. if (ret < 0)
  264. return ret;
  265. }
  266. pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
  267. if (!pb) {
  268. ret = -ENOMEM;
  269. goto err_alloc;
  270. }
  271. if (data->levels) {
  272. unsigned int i;
  273. for (i = 0; i <= data->max_brightness; i++)
  274. if (data->levels[i] > pb->scale)
  275. pb->scale = data->levels[i];
  276. pb->levels = data->levels;
  277. } else
  278. pb->scale = data->max_brightness;
  279. pb->enable_gpio = data->enable_gpio;
  280. pb->enable_gpio_flags = data->enable_gpio_flags;
  281. pb->notify = data->notify;
  282. pb->notify_after = data->notify_after;
  283. pb->check_fb = data->check_fb;
  284. pb->exit = data->exit;
  285. pb->dev = &pdev->dev;
  286. pb->enabled = false;
  287. if ( gpio_is_valid(pb->enable_gpio)) {
  288. unsigned long flags;
  289. if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
  290. flags = GPIOF_OUT_INIT_HIGH;
  291. else
  292. flags = GPIOF_OUT_INIT_LOW;
  293. ret = gpio_request_one(pb->enable_gpio, flags, "enable");
  294. if (ret < 0) {
  295. dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
  296. pb->enable_gpio, ret);
  297. goto err_alloc;
  298. }
  299. }
  300. pb->power_supply = devm_regulator_get(&pdev->dev, "power");
  301. if ( IS_ERR(pb->power_supply)) {
  302. ret = PTR_ERR(pb->power_supply);
  303. goto err_gpio;
  304. }
  305. pb->pwm = devm_pwm_get(&pdev->dev, NULL);
  306. if ( IS_ERR(pb->pwm)) {
  307. dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
  308. pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
  309. if ( IS_ERR(pb->pwm)) {
  310. dev_err(&pdev->dev, "unable to request legacy PWM\n");
  311. ret = PTR_ERR(pb->pwm);
  312. goto err_gpio;
  313. }
  314. }
  315. dev_dbg(&pdev->dev, "got pwm for backlight\n");
  316. /*
  317. * The DT case will set the pwm_period_ns field to 0 and store the
  318. * period, parsed from the DT, in the PWM device. For the non-DT case,
  319. * set the period from platform data.
  320. */
  321. if (data->pwm_period_ns > 0)
  322. pwm_set_period(pb->pwm, data->pwm_period_ns);
  323. pb->period = pwm_get_period(pb->pwm);
  324. pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
  325. memset(&props, 0, sizeof( struct backlight_properties));
  326. props.type = BACKLIGHT_RAW;
  327. props.max_brightness = data->max_brightness;
  328. bl = backlight_device_register( dev_name(&pdev->dev), &pdev->dev, pb,
  329. &pwm_backlight_ops, &props);
  330. if ( IS_ERR(bl)) {
  331. dev_err(&pdev->dev, "failed to register backlight\n");
  332. ret = PTR_ERR(bl);
  333. goto err_gpio;
  334. }
  335. if (data->dft_brightness > data->max_brightness) {
  336. dev_warn(&pdev->dev,
  337. "invalid default brightness level: %u, using %u\n",
  338. data->dft_brightness, data->max_brightness);
  339. data->dft_brightness = data->max_brightness;
  340. }
  341. bl->props.brightness = data->dft_brightness;
  342. bl->props.power = 1; // xiongqin.bi
  343. backlight_update_status(bl);
  344. platform_set_drvdata(pdev, bl);
  345. // add-begin by xiongqin.bi
  346. bl_delay_task = kthread_create(bl_on_fuction, bl, "bl_delay");
  347. if( IS_ERR(bl_delay_task))
  348. {
  349. printk( "[bgk] bl_on kthread_create Unable to start kernel thread. \n");
  350. err = PTR_ERR(bl_delay_task);
  351. bl_delay_task = NULL;
  352. goto err_thread;
  353. }
  354. wake_up_process(bl_delay_task);
  355. // add-end by xiongqin.bi
  356. err_thread:
  357. return 0;
  358. err_gpio:
  359. if ( gpio_is_valid(pb->enable_gpio))
  360. gpio_free(pb->enable_gpio);
  361. err_alloc:
  362. if (data->exit)
  363. data-> exit(&pdev->dev);
  364. return ret;
  365. }
  366. static int pwm_backlight_remove(struct platform_device *pdev)
  367. {
  368. struct backlight_device *bl = platform_get_drvdata(pdev);
  369. struct pwm_bl_data *pb = bl_get_data(bl);
  370. backlight_device_unregister(bl);
  371. pwm_backlight_power_off(pb);
  372. // add-begin by xiongqin.bi
  373. if (bl_delay_task)
  374. {
  375. kthread_stop(bl_delay_task);
  376. bl_delay_task = NULL;
  377. }
  378. // add-end by xiongqin.bi
  379. if (pb->exit)
  380. pb-> exit(&pdev->dev);
  381. return 0;
  382. }
  383. #ifdef CONFIG_PM_SLEEP
  384. static int pwm_backlight_suspend(struct device *dev)
  385. {
  386. struct backlight_device *bl = dev_get_drvdata(dev);
  387. struct pwm_bl_data *pb = bl_get_data(bl);
  388. if (pb->notify)
  389. pb-> notify(pb->dev, 0);
  390. pwm_backlight_power_off(pb);
  391. if (pb->notify_after)
  392. pb-> notify_after(pb->dev, 0);
  393. return 0;
  394. }
  395. static int pwm_backlight_resume(struct device *dev)
  396. {
  397. struct backlight_device *bl = dev_get_drvdata(dev);
  398. backlight_update_status(bl);
  399. return 0;
  400. }
  401. #endif
  402. static const struct dev_pm_ops pwm_backlight_pm_ops = {
  403. #ifdef CONFIG_PM_SLEEP
  404. .suspend = pwm_backlight_suspend,
  405. .resume = pwm_backlight_resume,
  406. .poweroff = pwm_backlight_suspend,
  407. .restore = pwm_backlight_resume,
  408. #endif
  409. };
  410. static struct platform_driver pwm_backlight_driver = {
  411. .driver = {
  412. .name = "pwm-backlight",
  413. .owner = THIS_MODULE,
  414. .pm = &pwm_backlight_pm_ops,
  415. .of_match_table = of_match_ptr(pwm_backlight_of_match),
  416. },
  417. .probe = pwm_backlight_probe,
  418. .remove = pwm_backlight_remove,
  419. };
  420. module_platform_driver(pwm_backlight_driver);
  421. MODULE_DESCRIPTION( "PWM based Backlight Driver");
  422. MODULE_LICENSE( "GPL");
  423. MODULE_ALIAS( "platform:pwm-backlight");

上边的代码里有一段是做pwm背光渐变的程序,下一篇文章讲pwm渐变的实现方法


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,在设备树中需要定义背光驱动节点的属性,例如: ``` backlight { compatible = "backlight-gpio"; gpios = <&gpio1 23 0>; default-on; brightness-levels = <0 25 50 75 100>; brightness-levels-names = "off", "dim", "medium", "bright", "max"; }; ``` 在这个示例中,我们使用了 GPIO 1.23 作为背光驱动的控制引脚,并指定了默认启,并定义了亮度级别和名称。 接下来,需要在设备树的板级别中引用该节点,并指定其与 LCD 控制器的关联,例如: ``` &lcd { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&lcd_data_16_pins &lcd_ctrl_pins>; backlight { compatible = "gpio-backlight"; pwms = <&pwm1 0 1000000>; brightness-levels = <0 25 50 75 100>; default-brightness-level = <3>; status = "okay"; }; }; ``` 在这个示例中,我们将背光节点与 LCD 控制器相关联,并指定了使用 PWM 1.0 作为背光控制,以及亮度级别和默认亮度级别。 最后,需要在内核驱动程序中实现背光驱动的控制逻辑,例如: ``` static int my_backlight_update_status(struct backlight_device *bd) { struct my_backlight_data *data = bl_get_data(bd); /* set the brightness level */ /* ... */ return 0; } static const struct backlight_ops my_backlight_ops = { .update_status = my_backlight_update_status, }; static int my_backlight_probe(struct platform_device *pdev) { struct my_backlight_data *data; /* ... */ data->bl_dev = devm_backlight_device_register(&pdev->dev, "my_backlight", &pdev->dev, data, &my_backlight_ops); /* ... */ } static struct platform_driver my_backlight_driver = { .probe = my_backlight_probe, .driver = { .name = "my_backlight", }, }; ``` 在这个示例中,我们定义了控制背光驱动的操作函数,并在驱动程序中将其注册为一个设备节点,以便在内核中使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值