查看: 2389|回复: 2

[分享] 关于i.MX6Q上的背光驱动分析

[复制链接]
  • TA的每日心情
    开心
    2025-7-11 08:53
  • 签到天数: 301 天

    连续签到: 2 天

    [LV.8]以坛为家I

    3926

    主题

    7545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    40030
    最后登录
    2025-8-26
    发表于 2019-12-25 17:37:04 | 显示全部楼层 |阅读模式
    i.MX6Q上的背光驱动分析




    imx6q设备树上的pwm背光驱动描述如下:
    1. pwm-backlight {
    2.                 compatible = "pwm-backlight";
    3.                 pwms = <&pwm1 0 50000>;
    4.                 brightness-levels = <
    5. <font color="#a0522d">                        0  1  2  3  4  5  6  7  8  9
    6.                         10 11 12 13 14 15 16 17 18 19
    7.                         20 21 22 23 24 25 26 27 28 29
    8.                         30 31 32 33 34 35 36 37 38 39
    9.                         40 41 42 43 44 45 46 47 48 49
    10.                         50 51 52 53 54 55 56 57 58 59
    11.                         60 61 62 63 64 65 66 67 68 69
    12.                         70 71 72 73 74 75 76 77 78 79
    13.                         80 81 82 83 84 85 86 87 88 89
    14.                         90 91 92 93 94 95 96 97 98 99
    15.                         100</font>
    16.                         >;
    17.                 default-brightness-level = <<font color="#ff0000">94</font>>;
    18.         };
    复制代码




    1. &pwm1 {
    2.         pinctrl-names = "<font color="#9acd32">default</font>";
    3.         pinctrl-0 = <&pinctrl_pwm1>;
    4.         status = "<font color="#9acd32">okay</font>";
    5. };
    复制代码




    1. pinctrl_pwm1: pwm1grp {
    2.                         fsl,pins = <
    3. <font color="#a0522d">                                MX6QDL_PAD_SD1_DAT3__PWM1_OUT                0x1b0b1</font>
    4.                         >;
    5.                 };
    复制代码



    具体的驱动文件是imx6qdl-sabresd/kernel_imx/drivers/video/backlight/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. struct pwm_bl_data {
    26.         struct pwm_device        *pwm;
    27.         struct device                *dev;
    28.         unsigned int                period;
    29.         unsigned int                lth_brightness;
    30.         unsigned int                *levels;
    31.         bool                        enabled;
    32.         struct regulator        *power_supply;
    33.         int                        enable_gpio;
    34.         unsigned long                enable_gpio_flags;
    35.         unsigned int                scale;
    36.         int                        (*notify)(struct device *,
    37.                                           int brightness);
    38.         void                        (*notify_after)(struct device *,
    39.                                         int brightness);
    40.         int                        (*check_fb)(struct device *, struct fb_info *);
    41.         void                        (*exit)(struct device *);
    42. };

    43. static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
    44. {
    45.         int err;

    46.         if (pb->enabled)
    47.                 return;

    48.         err = regulator_enable(pb->power_supply);
    49.         if (err < 0)
    50.                 dev_err(pb->dev, "failed to enable power supply\n");

    51.         if (gpio_is_valid(pb->enable_gpio)) {
    52.                 if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
    53.                         gpio_set_value(pb->enable_gpio, 0);
    54.                 else
    55.                         gpio_set_value(pb->enable_gpio, 1);
    56.         }

    57.         pwm_enable(pb->pwm);
    58.         pb->enabled = true;
    59. }

    60. static void pwm_backlight_power_off(struct pwm_bl_data *pb)
    61. {
    62.         if (!pb->enabled)
    63.                 return;

    64.         pwm_config(pb->pwm, 0, pb->period);
    65.         pwm_disable(pb->pwm);

    66.         if (gpio_is_valid(pb->enable_gpio)) {
    67.                 if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
    68.                         gpio_set_value(pb->enable_gpio, 1);
    69.                 else
    70.                         gpio_set_value(pb->enable_gpio, 0);
    71.         }

    72.         regulator_disable(pb->power_supply);
    73.         pb->enabled = false;
    74. }

    75. static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
    76. {
    77.         unsigned int lth = pb->lth_brightness;
    78.         int duty_cycle;

    79.         if (pb->levels)
    80.                 duty_cycle = pb->levels[brightness];
    81.         else
    82.                 duty_cycle = brightness;

    83.         return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
    84. }

    85. static int pwm_backlight_update_status(struct backlight_device *bl)
    86. {
    87.         struct pwm_bl_data *pb = bl_get_data(bl);
    88.         int brightness = bl->props.brightness;
    89.         int duty_cycle;

    90.         if (bl->props.power != FB_BLANK_UNBLANK ||
    91.             bl->props.fb_blank != FB_BLANK_UNBLANK ||
    92.             bl->props.state & BL_CORE_FBBLANK)
    93.                 brightness = 0;

    94.         if (pb->notify)
    95.                 brightness = pb->notify(pb->dev, brightness);

    96.         if (brightness > 0) {
    97.                 duty_cycle = compute_duty_cycle(pb, brightness);
    98.                 pwm_config(pb->pwm, duty_cycle, pb->period);
    99.                 pwm_backlight_power_on(pb, brightness);
    100.         } else
    101.                 pwm_backlight_power_off(pb);

    102.         if (pb->notify_after)
    103.                 pb->notify_after(pb->dev, brightness);

    104.         return 0;
    105. }

    106. static int pwm_backlight_get_brightness(struct backlight_device *bl)
    107. {
    108.         return bl->props.brightness;
    109. }

    110. static int pwm_backlight_check_fb(struct backlight_device *bl,
    111.                                   struct fb_info *info)
    112. {
    113.         struct pwm_bl_data *pb = bl_get_data(bl);

    114.         return !pb->check_fb || pb->check_fb(pb->dev, info);
    115. }

    116. static const struct backlight_ops pwm_backlight_ops = {
    117.         .update_status        = pwm_backlight_update_status,
    118.         .get_brightness        = pwm_backlight_get_brightness,
    119.         .check_fb        = pwm_backlight_check_fb,
    120. };

    121. #ifdef CONFIG_OF
    122. static int pwm_backlight_parse_dt(struct device *dev,
    123.                                   struct platform_pwm_backlight_data *data)
    124. {
    125.         struct device_node *node = dev->of_node;
    126.         enum of_gpio_flags flags;
    127.         struct property *prop;
    128.         int length;
    129.         u32 value;
    130.         int ret;

    131.         if (!node)
    132.                 return -ENODEV;

    133.         memset(data, 0, sizeof(*data));

    134.         /* determine the number of brightness levels */
    135.         prop = of_find_property(node, "brightness-levels", &length);
    136.         if (!prop)
    137.                 return -EINVAL;

    138.         data->max_brightness = length / sizeof(u32);

    139.         /* read brightness levels from DT property */
    140.         if (data->max_brightness > 0) {
    141.                 size_t size = sizeof(*data->levels) * data->max_brightness;

    142.                 data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
    143.                 if (!data->levels)
    144.                         return -ENOMEM;

    145.                 ret = of_property_read_u32_array(node, "brightness-levels",
    146.                                                  data->levels,
    147.                                                  data->max_brightness);
    148.                 if (ret < 0)
    149.                         return ret;

    150.                 ret = of_property_read_u32(node, "default-brightness-level",
    151.                                            &value);
    152.                 if (ret < 0)
    153.                         return ret;

    154.                 data->dft_brightness = value;
    155.                 data->max_brightness--;
    156.         }

    157.         data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0,
    158.                                                     &flags);
    159.         if (data->enable_gpio == -EPROBE_DEFER)
    160.                 return -EPROBE_DEFER;

    161.         if (gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW))
    162.                 data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW;

    163.         return 0;
    164. }

    165. static struct of_device_id pwm_backlight_of_match[] = {
    166.         { .compatible = "pwm-backlight" },
    167.         { }
    168. };

    169. MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
    170. #else
    171. static int pwm_backlight_parse_dt(struct device *dev,
    172.                                   struct platform_pwm_backlight_data *data)
    173. {
    174.         return -ENODEV;
    175. }
    176. #endif

    177. static int pwm_backlight_probe(struct platform_device *pdev)
    178. {
    179.         struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
    180.         struct platform_pwm_backlight_data defdata;
    181.         struct backlight_properties props;
    182.         struct backlight_device *bl;
    183.         struct pwm_bl_data *pb;
    184.         int ret;

    185.         if (!data) {
    186.                 ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
    187.                 if (ret < 0) {
    188.                         dev_err(&pdev->dev, "failed to find platform data\n");
    189.                         return ret;
    190.                 }

    191.                 data = &defdata;
    192.         }

    193.         if (data->init) {
    194.                 ret = data->init(&pdev->dev);
    195.                 if (ret < 0)
    196.                         return ret;
    197.         }

    198.         pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
    199.         if (!pb) {
    200.                 ret = -ENOMEM;
    201.                 goto err_alloc;
    202.         }

    203.         if (data->levels) {
    204.                 unsigned int i;

    205.                 for (i = 0; i <= data->max_brightness; i++)
    206.                         if (data->levels[i] > pb->scale)
    207.                                 pb->scale = data->levels[i];

    208.                 pb->levels = data->levels;
    209.         } else
    210.                 pb->scale = data->max_brightness;

    211.         pb->enable_gpio = data->enable_gpio;
    212.         pb->enable_gpio_flags = data->enable_gpio_flags;
    213.         pb->notify = data->notify;
    214.         pb->notify_after = data->notify_after;
    215.         pb->check_fb = data->check_fb;
    216.         pb->exit = data->exit;
    217.         pb->dev = &pdev->dev;
    218.         pb->enabled = false;

    219.         if (gpio_is_valid(pb->enable_gpio)) {
    220.                 unsigned long flags;

    221.                 if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
    222.                         flags = GPIOF_OUT_INIT_HIGH;
    223.                 else
    224.                         flags = GPIOF_OUT_INIT_LOW;

    225.                 ret = gpio_request_one(pb->enable_gpio, flags, "enable");
    226.                 if (ret < 0) {
    227.                         dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
    228.                                 pb->enable_gpio, ret);
    229.                         goto err_alloc;
    230.                 }
    231.         }

    232.         pb->power_supply = devm_regulator_get(&pdev->dev, "power");
    233.         if (IS_ERR(pb->power_supply)) {
    234.                 ret = PTR_ERR(pb->power_supply);
    235.                 goto err_gpio;
    236.         }

    237.         pb->pwm = devm_pwm_get(&pdev->dev, NULL);
    238.         if (IS_ERR(pb->pwm)) {
    239.                 dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");

    240.                 pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
    241.                 if (IS_ERR(pb->pwm)) {
    242.                         dev_err(&pdev->dev, "unable to request legacy PWM\n");
    243.                         ret = PTR_ERR(pb->pwm);
    244.                         goto err_gpio;
    245.                 }
    246.         }

    247.         dev_dbg(&pdev->dev, "got pwm for backlight\n");

    248.         /*
    249.          * The DT case will set the pwm_period_ns field to 0 and store the
    250.          * period, parsed from the DT, in the PWM device. For the non-DT case,
    251.          * set the period from platform data.
    252.          */
    253.         if (data->pwm_period_ns > 0)
    254.                 pwm_set_period(pb->pwm, data->pwm_period_ns);

    255.         pb->period = pwm_get_period(pb->pwm);
    256.         pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);

    257.         memset(&props, 0, sizeof(struct backlight_properties));
    258.         props.type = BACKLIGHT_RAW;
    259.         props.max_brightness = data->max_brightness;
    260.         bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
    261.                                        &pwm_backlight_ops, &props);
    262.         if (IS_ERR(bl)) {
    263.                 dev_err(&pdev->dev, "failed to register backlight\n");
    264.                 ret = PTR_ERR(bl);
    265.                 goto err_gpio;
    266.         }

    267.         if (data->dft_brightness > data->max_brightness) {
    268.                 dev_warn(&pdev->dev,
    269.                          "invalid default brightness level: %u, using %u\n",
    270.                          data->dft_brightness, data->max_brightness);
    271.                 data->dft_brightness = data->max_brightness;
    272.         }

    273.         bl->props.brightness = data->dft_brightness;
    274.         backlight_update_status(bl);

    275.         platform_set_drvdata(pdev, bl);
    276.         return 0;

    277. err_gpio:
    278.         if (gpio_is_valid(pb->enable_gpio))
    279.                 gpio_free(pb->enable_gpio);
    280. err_alloc:
    281.         if (data->exit)
    282.                 data->exit(&pdev->dev);
    283.         return ret;
    284. }

    285. static int pwm_backlight_remove(struct platform_device *pdev)
    286. {
    287.         struct backlight_device *bl = platform_get_drvdata(pdev);
    288.         struct pwm_bl_data *pb = bl_get_data(bl);

    289.         backlight_device_unregister(bl);
    290.         pwm_backlight_power_off(pb);

    291.         if (pb->exit)
    292.                 pb->exit(&pdev->dev);

    293.         return 0;
    294. }

    295. #ifdef CONFIG_PM_SLEEP
    296. static int pwm_backlight_suspend(struct device *dev)
    297. {
    298.         struct backlight_device *bl = dev_get_drvdata(dev);
    299.         struct pwm_bl_data *pb = bl_get_data(bl);

    300.         if (pb->notify)
    301.                 pb->notify(pb->dev, 0);

    302.         pwm_backlight_power_off(pb);

    303.         if (pb->notify_after)
    304.                 pb->notify_after(pb->dev, 0);

    305.         return 0;
    306. }

    307. static int pwm_backlight_resume(struct device *dev)
    308. {
    309.         struct backlight_device *bl = dev_get_drvdata(dev);

    310.         backlight_update_status(bl);

    311.         return 0;
    312. }
    313. #endif

    314. static const struct dev_pm_ops pwm_backlight_pm_ops = {
    315. #ifdef CONFIG_PM_SLEEP
    316.         .suspend = pwm_backlight_suspend,
    317.         .resume = pwm_backlight_resume,
    318.         .poweroff = pwm_backlight_suspend,
    319.         .restore = pwm_backlight_resume,
    320. #endif
    321. };

    322. static struct platform_driver pwm_backlight_driver = {
    323.         .driver                = {
    324.                 .name                = "pwm-backlight",
    325.                 .owner                = THIS_MODULE,
    326.                 .pm                = &pwm_backlight_pm_ops,
    327.                 .of_match_table        = of_match_ptr(pwm_backlight_of_match),
    328.         },
    329.         .probe                = pwm_backlight_probe,
    330.         .remove                = pwm_backlight_remove,
    331. };

    332. module_platform_driver(pwm_backlight_driver);

    333. MODULE_DESCRIPTION("PWM based Backlight Driver");
    334. MODULE_LICENSE("GPL");
    335. MODULE_ALIAS("platform:pwm-backlight");
    复制代码


    通过adb进入shell终端,通过echo xxx >brightness就可改变背光亮度。


    1. root@sabresd_6dq:<font color="#4169e1">/sys/class/backlight/pwm-backlight.</font>0 # ls -al
    2. -r--r--r-- root     root         4096 1970-01-01 04:39 actual_brightness
    3. -rw-r--r-- root     root         4096 1970-01-01 04:39 bl_power
    4. -rw-rw---- system   system       4096 1970-01-01 04:30 brightness
    5. lrwxrwxrwx root     root              1970-01-01 04:39 device -> ../../../pwm-backlight.33
    6. -r--r--r-- root     root         4096 1970-01-01 04:30 max_brightness
    7. drwxr-xr-x root     root              1970-01-01 04:30 power
    8. lrwxrwxrwx root     root              1970-01-01 04:39 subsystem -> ../../../../../class/backlight
    9. -r--r--r-- root     root         4096 1970-01-01 04:39 type
    10. -rw-r--r-- root     root         4096 1970-01-01 04:30 uevent
    复制代码


    ————————————————

    qiandao qiandao
    回复

    使用道具 举报

    该用户从未签到

    2

    主题

    108

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    281
    最后登录
    2020-9-10
    发表于 2020-4-9 20:41:42 | 显示全部楼层
    内核要怎么配置呢?
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2021-6-14 00:30
  • 签到天数: 10 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    1

    主题

    54

    帖子

    0

    注册会员

    Rank: 2

    积分
    108
    最后登录
    2021-6-14
    发表于 2021-5-29 18:01:10 | 显示全部楼层
    收藏了,正要要设自动光感调节
    开心
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-8-27 03:33 , Processed in 0.082931 second(s), 21 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表