展讯7731C_M Android6.0 充电指示灯实现(二)------开机充电实现

转载:https://blog.csdn.net/xiaopangzi313/article/details/52199407

 上一节已经了解了展讯7731C_M Android6.0 充电指示灯实的关机部分,这一节主要介绍开机部分,也就是kernel部分。
kernel 部分主要功能是提供对硬件led控制接口,如设置亮度,获取亮度,设置颜色等。

一、整个guide-led 驱动调用逻辑序列图下



二、驱动框架的具体实现

1.定义平台驱动结构,注册platform 驱动
文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c
[cpp]  view plain  copy
  1. static int __init sprd_leds_bltc_rgb_init(void)  
  2. {  
  3.         PRINT_INFO("Locate in sprd_leds_bltc_rgb_init!\n");  
  4.   
  5.   
  6.         return platform_driver_register(&sprd_leds_bltc_rgb_driver);  
  7. }  
  8. module_init(sprd_leds_bltc_rgb_init);  
//匹配函数结构体
[cpp]  view plain  copy
  1. static const struct of_device_id sprd_leds_bltc_rgb_of_match[] = {  
  2.         { .compatible = "sprd,sprd-leds-bltc-rgb", },  
  3.         { }  
  4. };  
//注册用的sprd_leds_bltc_rgb_driver
[cpp]  view plain  copy
  1. static struct platform_driver sprd_leds_bltc_rgb_driver = {  
  2.         .driver = {  
  3.                 .name  = "sprd-leds-bltc-rgb",  
  4.                 .owner = THIS_MODULE,  
  5.                 .of_match_table = sprd_leds_bltc_rgb_of_match,  
  6.         },  
  7.         .probe    = sprd_leds_bltc_rgb_probe,  
  8.         .remove   = sprd_leds_bltc_rgb_remove,  
  9.         .shutdown = sprd_leds_bltc_rgb_shutdown,  
  10. };  
2. 设备dts定义:
[cpp]  view plain  copy
  1. sprd-leds-bltc-rgb {  
  2.                 compatible = "sprd,sprd-leds-bltc-rgb";  
  3. };  
3.设备驱动屏匹配后进入probe
[cpp]  view plain  copy
  1. static int sprd_leds_bltc_rgb_probe(struct platform_device *dev)  
  2. {  
  3.         .....  
  4.         for (i = 0; i < SPRD_LED_TYPE_TOTAL; i++) {  
  5.                 .....  
  6.                 //将本地方法封装到brgb结构中  
  7.                 brgb->cdev.brightness_set = sprd_leds_bltc_rgb_set;  
  8.                 brgb->cdev.name = sprd_leds_rgb_name[i];  
  9.                 brgb->cdev.brightness_get = NULL;  
  10.                 brgb->enable = 0;  
  11.                 //设置操作whiteled的寄存器地址  
  12. #if CONFIG_OF  
  13.                 brgb->sprd_bltc_base_addr = SPRD_ADI_BASE + 0x8440;      /***0x40038440***/  
  14.                 brgb->sprd_arm_module_en_addr = SPRD_ADI_BASE + 0x8800;  /***0x40038800***/  
  15. #endif  
  16.                 spin_lock_init(&brgb->value_lock);  
  17.                 mutex_init(&brgb->mutex);  
  18.                 //默认关闭guide-led  
  19.                 brgb->value = LED_OFF;  
  20.                 //将本地私有的driver数据地址付给platform_dev中的driver_data  
  21.                 platform_set_drvdata(dev, brgb);  
  22.                   
  23.                 //创建class 下面的brightness节点  
  24.                 ret = led_classdev_register(&dev->dev, &brgb->cdev);  
  25.                 //创建设备节点 rising_time,falling_time,  
  26.                 .....  
  27.         }  
  28.         return 0;  
  29. err:  
  30.         if (i) {  
  31.                 for (i = i-1; i >=0; i--) {  
  32.                         if (!brgb)  
  33.                                 continue;  
  34.                         led_classdev_unregister(&brgb->cdev);  
  35.                         kfree(brgb);  
  36.                         brgb = NULL;  
  37.                         --brgb;  
  38.                 }  
  39.         }  
  40.   
  41.   
  42.         return ret;  
  43. }  

4.led 创建brightneess  等节点

文件:kernel/drivers/leds/led-class.c

[cpp]  view plain  copy
  1. ret = led_classdev_register(&dev->dev, &brgb->cdev);  
  2.   
  3.   
  4.  /** 
  5.  * led_classdev_register - register a new object of led_classdev class. 
  6.  * @parent: The device to register. 
  7.  * @led_cdev: the led_classdev structure for this device. 
  8.  */  
  9. int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  
  10. {  
  11.     led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,  
  12.                       "%s", led_cdev->name);  
  13.     .......  
  14.   
  15.     return 0;  
  16. }  
5.分析:device_create
[cpp]  view plain  copy
  1. led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,  
  2.                       "%s", led_cdev->name);  
  3. leds_class 对应leds 类,其中<span style="font-family: Arial, Helvetica, sans-serif;">leds_class->dev_attr 对应brightness</span>  
  4. parents 对应platform 的dev   
  5. led_dev 对应led_classdev 对象  
  6. led_cdev 对应参数  
  7. led_cdev->name:设备节点的名称   
  8.   
  9.   
  10. 创建leds_class ==> 对应/sys/class/leds    
  11.  static int __init leds_init(void)  
  12. {  
  13.     leds_class = class_create(THIS_MODULE, "leds");  
  14.     ....  
  15.     leds_class->dev_attrs = led_class_attrs; //这里为属性节点brightness定义信息  
  16.     return 0;  
  17. }  
  18.   
  19.   
  20. led_cdev->name ->  
  21.  brgb->cdev.name = sprd_leds_rgb_name[i];                  
  22.  ==>对应 /sys/class/leds/red  
  23.          /sys/class/leds/red    
  24.          /sys/class/leds/red  
  25.          .....  
  26. static char *sprd_leds_rgb_name[SPRD_LED_TYPE_TOTAL] = {  
  27.         "red",  
  28.         "green",  
  29.         "blue",  
  30.         "red_bl",  
  31.         "green_bl",  
  32.         "blue_bl",  
  33. };    
//定义brightness属性 ===> 对应 /sys/class/leds/xxx/brightness
[cpp]  view plain  copy
  1. static struct device_attribute led_class_attrs[] = {  
  2.     __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),  
  3.     __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),  
  4. #ifdef CONFIG_LEDS_TRIGGERS  
  5.     __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),  
  6. #endif  
  7.     __ATTR_NULL,  
  8. };  
//定义brightness 的读写方法
[cpp]  view plain  copy
  1. static ssize_t led_brightness_show(struct device *dev,  
  2.         struct device_attribute *attr, char *buf)  
  3. {  
  4.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  5.   
  6.   
  7.     /* no lock needed for this */  
  8.     led_update_brightness(led_cdev);  
  9.   
  10.   
  11.     return sprintf(buf, "%u\n", led_cdev->brightness);  
  12. }  
//将读取的值,传入__led_set_brightness
[cpp]  view plain  copy
  1. static ssize_t led_brightness_store(struct device *dev,  
  2.         struct device_attribute *attr, const char *buf, size_t size)  
  3. {  
  4.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  5.     .....  
  6.     ret = kstrtoul(buf, 10, &state);  
  7.     .....  
  8.     __led_set_brightness(led_cdev, state);  
  9.   
  10.   
  11.     return size;  
  12. }  
创建完节点之后,再看led_update_brightness ,将实际的rgb灯中的brightness值存放在 led_cdev->brightness中
5.led_update_brightness的实现

[cpp]  view plain  copy
  1. static void led_update_brightness(struct led_classdev *led_cdev)  
  2. {  
  3.     if (led_cdev->brightness_get)  
  4.         led_cdev->brightness = led_cdev->brightness_get(led_cdev);  
  5. }  
由于brgb->cdev.brightness_get = NULL; 所以 led_update_brightness 对该guide-led 没有多少实际意义
6.创建工作队列
[cpp]  view plain  copy
  1. INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);  
  2. static void set_brightness_delayed(struct work_struct *ws)  
  3. {  
  4.     struct led_classdev *led_cdev =  
  5.         container_of(ws, struct led_classdev, set_brightness_work);  
  6.   
  7.   
  8.     led_stop_software_blink(led_cdev);  
  9.   
  10.   
  11.     __led_set_brightness(led_cdev, led_cdev->delayed_set_value);  
  12. }  
这里调用led_stop_software_blink删除定时器
[cpp]  view plain  copy
  1. void led_stop_software_blink(struct led_classdev *led_cdev)  
  2. {  
  3.     del_timer_sync(&led_cdev->blink_timer);  
  4.     led_cdev->blink_delay_on = 0;  
  5.     led_cdev->blink_delay_off = 0;  //这两个个延迟时间用来控制指示灯闪灭程度的  
  6. }  
判断brightness 的上下限值,重新设定brightness 
[cpp]  view plain  copy
  1. static inline void __led_set_brightness(struct led_classdev *led_cdev,  
  2.                     enum led_brightness value)  
  3. {  
  4.     if (value > led_cdev->max_brightness)  
  5.         value = led_cdev->max_brightness;  
  6.     led_cdev->brightness = value;  
  7.     if (!(led_cdev->flags & LED_SUSPENDED))  
  8.         led_cdev->brightness_set(led_cdev, value);  
  9. }  
这里的led_cdev->brightness_set最终会调用leds-sprd-bltc-rgb.c的sprd_leds_bltc_rgb_set_brightness,将设置值写入寄存器
7.定义定时器,定时更新 brightness 值
[cpp]  view plain  copy
  1. init_timer(&led_cdev->blink_timer);  
  2. led_cdev->blink_timer.function = led_timer_function;  
  3. led_cdev->blink_timer.data = (unsigned long)led_cdev;     
  4.   
  5.   
  6.  static void led_timer_function(unsigned long data)  
  7. {  
  8.     struct led_classdev *led_cdev = (void *)data;  
  9.     unsigned long brightness;  
  10.     unsigned long delay;  
  11.   
  12.   
  13.     if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {  
  14.         __led_set_brightness(led_cdev, LED_OFF);  
  15.         return;  
  16.     }  
  17.   
  18.   
  19.     if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {  
  20.         led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;  
  21.         return;  
  22.     }  
  23.   
  24.   
  25.     brightness = led_get_brightness(led_cdev);  
  26.     if (!brightness) {  
  27.         /* Time to switch the LED on. */  
  28.         brightness = led_cdev->blink_brightness;  
  29.         delay = led_cdev->blink_delay_on;  
  30.     } else {  
  31.         /* Store the current brightness value to be able 
  32.          * to restore it when the delay_off period is over. 
  33.          */  
  34.         led_cdev->blink_brightness = brightness;  
  35.         brightness = LED_OFF;  
  36.         delay = led_cdev->blink_delay_off;  
  37.     }  
  38.   
  39.   
  40.     __led_set_brightness(led_cdev, brightness);  
  41.   
  42.   
  43.     /* Return in next iteration if led is in one-shot mode and we are in 
  44.      * the final blink state so that the led is toggled each delay_on + 
  45.      * delay_off milliseconds in worst case. 
  46.      */  
  47.     if (led_cdev->flags & LED_BLINK_ONESHOT) {  
  48.         if (led_cdev->flags & LED_BLINK_INVERT) {  
  49.             if (brightness)  
  50.                 led_cdev->flags |= LED_BLINK_ONESHOT_STOP;  
  51.         } else {  
  52.             if (!brightness)  
  53.                 led_cdev->flags |= LED_BLINK_ONESHOT_STOP;  
  54.         }  
  55.     }  
  56.   
  57.   
  58.     mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));  
  59. //所以如果没有这mod_timer,func 只会执行一次,也就是timer第一次超时时执行的那次  
  60. }  

8.sprd_leds_bltc_rgb_set的实现

文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c

[cpp]  view plain  copy
  1. static void sprd_leds_bltc_rgb_set(struct led_classdev *bltc_rgb_cdev,enum led_brightness value)  
  2. {  
  3.         struct sprd_leds_bltc_rgb *brgb;  
  4.         unsigned long flags;  
  5.   
  6.   
  7.                 brgb = to_sprd_bltc_rgb(bltc_rgb_cdev);  
  8.                 spin_lock_irqsave(&brgb->value_lock, flags);  
  9.                 brgb->leds_flag = bltc_rgb_cdev->flags;  
  10.                 brgb->value = value;  
  11.                 spin_unlock_irqrestore(&brgb->value_lock, flags);  
  12.   
  13.   
  14.                 if(1 == brgb->suspend) {  
  15.                         PRINT_WARN("Do NOT change brightness in suspend mode\n");  
  16.                         return;  
  17.                 }  
  18.                 if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0 || \  
  19.                     strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0 || \  
  20.                     strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0)  
  21.                         sprd_leds_rgb_work(brgb);  
  22.                 else  
  23.                         sprd_leds_bltc_work(brgb);  
  24. }  
9.sprd_leds_rgb_work的实现
[cpp]  view plain  copy
  1. static void sprd_leds_rgb_work(struct sprd_leds_bltc_rgb *brgb)  
  2. {  
  3.         unsigned long flags;  
  4.   
  5.   
  6.         mutex_lock(&brgb->mutex);  
  7.         spin_lock_irqsave(&brgb->value_lock, flags);  
  8.         if (brgb->value == LED_OFF) {  
  9.                 spin_unlock_irqrestore(&brgb->value_lock, flags);  
  10.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
  11.                 goto out;  
  12.         }  
  13.         spin_unlock_irqrestore(&brgb->value_lock, flags);  
  14.         sprd_leds_bltc_rgb_enable(brgb);  
  15.         PRINT_INFO("sprd_leds_bltc_rgb_work_for rgb!\n");  
  16.   
  17.   
  18. out:  
  19.         mutex_unlock(&brgb->mutex);  
  20. }  
10.sprd_leds_bltc_rgb_enable的实现:
[cpp]  view plain  copy
  1. static void sprd_leds_bltc_rgb_enable(struct sprd_leds_bltc_rgb *brgb)  
  2. {  
  3.         sprd_bltc_rgb_init(brgb);  
  4.   
  5.   
  6.         if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0) {  
  7.                 sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<0)|(0x1<<1));  
  8.                 brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_R_PRESCL + BLTC_DUTY_OFFSET;  
  9.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
  10.         }  
  11.         if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0) {  
  12.                 sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<4)|(0x1<<5));  
  13.                 brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_G_PRESCL + BLTC_DUTY_OFFSET;  
  14.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
  15.         }  
  16.         if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0) {  
  17.                 sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<8)|(0x1<<9));  
  18.                 brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_B_PRESCL + BLTC_DUTY_OFFSET;  
  19.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
  20.         }  
  21.         ....  
  22. }  
11.sprd_leds_bltc_rgb_set_brightness的实现:
[cpp]  view plain  copy
  1. static void sprd_leds_bltc_rgb_set_brightness(struct sprd_leds_bltc_rgb *brgb)  
  2. {  
  3.         unsigned long brightness = brgb->value;  
  4.         unsigned long pwm_duty;  
  5.   
  6.   
  7.         pwm_duty = brightness;  
  8.         if(pwm_duty > 255)  
  9.                 pwm_duty = 255;  
  10.         sci_adi_write(brgb->bltc_addr, (pwm_duty<<8)|PWM_MOD_COUNTER,0xffff);  
  11.         PRINT_INFO("reg:0x%1LX set_val:0x%08X  brightness:%ld  brightness_level:%ld(0~15)\n", \  
  12.                    brgb->bltc_addr, sprd_leds_bltc_rgb_read(brgb->bltc_addr),brightness, pwm_duty);  
  13. }  

三、总结

    本文着重分析开机状态充电指示灯kernel部分驱动实现,在上述的分析中,我们可以看到驱动主要做了以下工作:
1).建立相关的/sys节点,如/sys/class/leds/red/brightness ,并且提供相应的读写方法
2).提供对led whiteled寄存器控制方式,如本篇中有指定whiteled寄存器的地址,并提供了操作写寄存器的方法
sci_adi_write等
      通过上述的工作,上层应用程序只需要操作相关的/sys节点即控制led的亮灭,当然这只是最基本的功能,该驱
动还提供了led 周期性亮灭灯的实现,可以用来实现呼吸灯灯操作,本文不再赘述!


猜你喜欢

转载自blog.csdn.net/m0_37870649/article/details/80566139
今日推荐