MTK6735(Android6.0)-按键灯的实现

一、按键灯的简介
    最近调试一下按键灯,今天抽空顺便把的流程分析了一下。按键灯也是一种led,它的使用规则如命名一样,当按键按
下亮灯,如果一定时间不操作的话,一会会灭灯。其实这里的按键灯亮灭策略通常不是驱动来完成的,而是有用户空间来
控制。正如一句老话“驱动注重的不是策略,而是机制”;所以我们在编写驱动只需要完成led的点亮和熄灭功能即可。当
然在实际使用中我们会发现不是所有驱动都如此,有时候平台中也会添加一定的策略,这个本章不作分析。 调试按键灯,
当然首先从硬件开始。按键灯的原理图如下:


从原理图中我们发现,button-backlight是由两路ISINK控制的,ISNIK是一种类似于PWM的控制器,它可以发出类似PWM的信号,可以通过寄存器的配置

调整其占空比等参数,进入调节输出电流,从而控制led的亮度。
二、按键灯的驱动实现
1. 设备和驱动的匹配
//驱动文件中定义platform_driver
file:kernel-3.18/drivers/misc/mediatek/leds/leds_drv.c
[cpp]  view plain  copy
  1. #define USE_PINCTRL  
  2. #ifdef USE_PINCTRL  
  3. static const struct of_device_id leds_of_ids[] = {  
  4.     {.compatible = "mediatek,leds-mt65xx",},  
  5.     {}  
  6. };  
  7. #endif  
  8. static struct platform_driver mt65xx_leds_driver = {  
  9.     .driver = {  
  10.            .name = "leds-mt65xx",  
  11.            .owner = THIS_MODULE,  
  12. #ifdef USE_PINCTRL  
  13.     .of_match_table = leds_of_ids,  //和dts中定义一致  
  14. #endif  
  15.            },  
  16.     .probe = mt65xx_leds_probe,  
  17.     .remove = mt65xx_leds_remove,  
  18.     /* .suspend      = mt65xx_leds_suspend, */  
  19.     .shutdown = mt65xx_leds_shutdown,  
  20.   
  21.   
  22. };  
//驱动模块的加载
[cpp]  view plain  copy
  1. static int __init mt65xx_leds_init(void)  
  2. {  
  3.     ret = platform_driver_register(&mt65xx_leds_driver);  
  4.   
  5.   
  6.     ....  
  7.     return ret;  
  8. }  
//dts中定义leds 的相关节点如:red,green,blue,keyboard-backlight,button-backlight等(后面会用到)
file:kernel-3.18/arch/arm/boot/dts/rlk6737m_35g_c_m0.dts
[cpp]  view plain  copy
  1. led0:led@0 {  
  2.     compatible = "mediatek,red";  
  3.     led_mode = <0>;  
  4.     data = <1>;  
  5.     pwm_config = <0 0 0 0 0>;  
  6. };  
  7. led1:led@1 {  
  8.     compatible = "mediatek,green";  
  9.     led_mode = <0>;  
  10.     data = <1>;  
  11.     pwm_config = <0 0 0 0 0>;  
  12. };  
  13. led2:led@2 {  
  14.     compatible = "mediatek,blue";  
  15.     led_mode = <0>;  
  16.     data = <1>;  
  17.     pwm_config = <0 0 0 0 0>;  
  18. };  
  19. led4:led@4 {  
  20.     compatible = "mediatek,keyboard-backlight";  
  21.     led_mode = <0>;  
  22.     data = <1>;  
  23.     pwm_config = <0 0 0 0 0>;  
  24. };  
  25. led5:led@5 {  
  26.     compatible = "mediatek,button-backlight"//这里着重分析按键灯button-backlight  
  27.     led_mode = <3>;  
  28.     data = <1>;  
  29.     pwm_config = <0 0 0 0 0>;  
  30. };  
  31. led6:led@6 {  
  32.     compatible = "mediatek,lcd-backlight";  
  33.     led_mode = <5>;  
  34.     data = <1>;  
  35.     pwm_config = <0 0 0 0 0>;  
  36. };  
//dts中定义和platform_device相关的节点信息
[cpp]  view plain  copy
  1. file:kernel-3.18/arch/arm/boot/dts/mt6735m.dtsi  
  2. lightsystem: leds {  
  3.     compatible = "mediatek,leds-mt65xx"//这里的定义和上面platform_driver中定义的一致  
  4. };  
  上述dts中定义按键灯leds节点配置,内核起来后会解析dts生成相关的设备,并与驱动中的driver匹配,如果匹配成功就执行下面的probe
2.leds probe 函数实现
file:kernel-3.18/drivers/misc/mediatek/leds/leds_drv.c
[cpp]  view plain  copy
  1. static int mt65xx_leds_probe(struct platform_device *pdev)  
  2. {  
  3.     int i;  
  4.     int ret;/* rc; */  
  5.     //进入probe后,会从dts中获取led节点的mode和data  
  6.     struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list();  
  7.   
  8.   
  9.     LEDS_DRV_DEBUG("%s\n", __func__);  
  10.     get_div_array();  
  11.     //MT65XX_LED_TYPE_TOTAL为改通用(common)驱动所支持灯的个数  
  12.     for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) {   
  13.     //观察上面dts中mode如果mode为0(MT65XX_LED_MODE_NONE),则遍历下个元素  
  14.         if (cust_led_list[i].mode == MT65XX_LED_MODE_NONE) {    
  15.             g_leds_data[i] = NULL;  
  16.             continue;  
  17.         }  
  18.         ...  
  19.         //将dts中配置的mode,name ,data 保存起来后面会用到  
  20.         //通过观察上面的button-backlight 的配置,得出其mode为3,data为1,name为button-backlight  
  21.         g_leds_data[i]->cust.mode = cust_led_list[i].mode;   
  22.         g_leds_data[i]->cust.data = cust_led_list[i].data;  
  23.         g_leds_data[i]->cust.name = cust_led_list[i].name;  
  24.   
  25.   
  26.         g_leds_data[i]->cdev.name = cust_led_list[i].name;  
  27.         g_leds_data[i]->cust.config_data = cust_led_list[i].config_data; /* bei add */  
  28.   
  29.   
  30.         g_leds_data[i]->cdev.brightness_set = mt65xx_led_set; //设置led亮度的函数  
  31.           
  32.         //创建sys目录下的brightness等属性节点,提供给用户空间调用  
  33.         ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);  
  34.         ...  
  35.     return 0;  
  36.         ...  
  37. }  
//后面会用到的一些结构的定义
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds_sw.h
[cpp]  view plain  copy
  1. enum mt65xx_led_type {  
  2.     MT65XX_LED_TYPE_RED = 0,  
  3.     MT65XX_LED_TYPE_GREEN,  
  4.     MT65XX_LED_TYPE_BLUE,  
  5.     MT65XX_LED_TYPE_JOGBALL,  
  6.     MT65XX_LED_TYPE_KEYBOARD,  
  7.     MT65XX_LED_TYPE_BUTTON,    
  8.     MT65XX_LED_TYPE_LCD,  
  9.     MT65XX_LED_TYPE_TOTAL,  
  10. };  
  11. /** 
  12.  * led customization data structure 
  13.  * name : must the same as lights HAL 
  14.  * mode : control mode 
  15.  * data : 
  16.  *    PWM:  pwm number 
  17.  *    GPIO: gpio id 
  18.  *    PMIC: enum mt65xx_led_pmic 
  19.  *    CUST: custom set brightness function pointer 
  20.  * config_data: pwm config data 
  21.  */  
  22. struct cust_mt65xx_led {  
  23.     char *name;  
  24.     enum mt65xx_led_mode mode;  
  25.     long data;  
  26.     struct PWM_config config_data;  
  27. };  
  28. /** 
  29.  * led device node structure with mtk extentions 
  30.  * cdev: common led device structure 
  31.  * cust: customization data from device tree 
  32.  * work: workqueue for specialfied led device 
  33.  * level: brightness level 
  34.  * delay_on: on time if led is blinking 
  35.  * delay_off: off time if led is blinking 
  36.  */  
  37. struct mt65xx_led_data {  
  38.     struct led_classdev cdev;  
  39.     struct cust_mt65xx_led cust;  
  40.     struct work_struct work;  
  41.     int level;  
  42.     int delay_on;  
  43.     int delay_off;  
  44. };  
  45. file:kernel-3.18/include/linux/leds.h  
  46. enum led_brightness {  
  47.     LED_OFF     = 0,  
  48.     LED_HALF    = 127,  
  49.     LED_FULL    = 255,  
  50. };  
  51. struct led_classdev {  
  52.     const char      *name;  
  53.     enum led_brightness  brightness;  
  54.     enum led_brightness  max_brightness;  
  55.     int          flags;  
  56.     ...  
  57.     /* Set LED brightness level */  
  58.     /* Must not sleep, use a workqueue if needed */  
  59.     void        (*brightness_set)(struct led_classdev *led_cdev,  
  60.                       enum led_brightness brightness);  
  61.     /* Get LED brightness level */  
  62.     enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);  
  63.   
  64.   
  65.     .....  
  66.   
  67.   
  68.     struct device       *dev;  
  69.     const struct attribute_group    **groups;  
  70.   
  71.   
  72.     struct list_head     node;          /* LED Device list */  
  73.     ...  
  74. };  
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
[cpp]  view plain  copy
  1. char *leds_name[MT65XX_LED_TYPE_TOTAL] = {  
  2.     "red",  
  3.     "green",  
  4.     "blue",  
  5.     "jogball-backlight",  
  6.     "keyboard-backlight",  
  7.     "button-backlight",  
  8.     "lcd-backlight",  
  9. }  
3.从dts中获取各种led的配置信息
[cpp]  view plain  copy
  1. struct cust_mt65xx_led *mt_get_cust_led_list(void)  
  2. {  
  3.     struct cust_mt65xx_led *cust_led_list = get_cust_led_dtsi();  
  4.     return cust_led_list;  
  5. }  
  6. struct cust_mt65xx_led *get_cust_led_dtsi(void)  
  7. {  
  8.     struct device_node *led_node = NULL;  
  9.     ...  
  10.         //MT65XX_LED_TYPE_TOTAL 为led数组长度,即可以支持led的个数  
  11.         for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) {  
  12.   
  13.   
  14.             char node_name[32] = "mediatek,";  
  15.   
  16.   
  17.             pled_dtsi[i].name = leds_name[i];  
  18.               
  19.             //使用"mediatek,button-backlight"寻找dtsi中定义的节点  
  20.             led_node =  
  21.                 of_find_compatible_node(NULL, NULL,  
  22.                             strcat(node_name,  
  23.                                leds_name[i]));   
  24.             if (!led_node) {  
  25.                 LEDS_DEBUG("Cannot find LED node from dts\n");  
  26.                 pled_dtsi[i].mode = 0;  
  27.                 pled_dtsi[i].data = -1;  
  28.             } else {  
  29.                 isSupportDTS = true;  
  30.                 //读取led_mode值  
  31.                 ret =  
  32.                     of_property_read_u32(led_node, "led_mode",    
  33.                              &mode);  
  34.                 if (!ret) {  
  35.                     pled_dtsi[i].mode = mode;  
  36.                     LEDS_DEBUG  
  37.                         ("The %s's led mode is : %d\n",  
  38.                          pled_dtsi[i].name,  
  39.                          pled_dtsi[i].mode);  
  40.                 }  
  41.                 //读取led的data值  
  42.                 ret =  
  43.                     of_property_read_u32(led_node, "data",  
  44.                              &data);  
  45.                 if (!ret) {  
  46.                     pled_dtsi[i].data = data;  
  47.                     LEDS_DEBUG  
  48.                         ("The %s's led data is : %ld\n",  
  49.                          pled_dtsi[i].name,  
  50.                          pled_dtsi[i].data);  
  51.                 }   
  52.                 ...  
  53.     return pled_dtsi;  
  54. }  
4. 创建相关的设备节点
[cpp]  view plain  copy
  1. **  
  2.  * led_classdev_register - register a new object of led_classdev class.  
  3.  * @parent: The device to register.  
  4.  * @led_cdev: the led_classdev structure for this device.  
  5.  */  
  6. int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  
  7. {  
  8.     led_cdev->dev = device_create_with_groups(leds_class, parent, 0,  
  9.                     led_cdev, led_cdev->groups,  
  10.                     "%s", led_cdev->name);  
  11.     ...  
  12.   
  13.   
  14.     return 0;  
  15. }  
//device_create_with_groups的实现
file:kernel-3.18/drivers/base/core.c
[cpp]  view plain  copy
  1. /** 
  2.  * device_create_with_groups - creates a device and registers it with sysfs 
  3.  * @class: pointer to the struct class that this device should be registered to 
  4.  * @parent: pointer to the parent struct device of this new device, if any 
  5.  * @devt: the dev_t for the char device to be added 
  6.  * @drvdata: the data to be added to the device for callbacks 
  7.  * @groups: NULL-terminated list of attribute groups to be created 
  8.  * @fmt: string for the device's name 
  9.  * 
  10.  * This function can be used by char device classes.  A struct device 
  11.  * will be created in sysfs, registered to the specified class. 
  12.  * Additional attributes specified in the groups parameter will also 
  13.  * be created automatically. 
  14.  * 
  15.  * A "dev" file will be created, showing the dev_t for the device, if 
  16.  * the dev_t is not 0,0. 
  17.  * If a pointer to a parent struct device is passed in, the newly created 
  18.  * struct device will be a child of that device in sysfs. 
  19.  * The pointer to the struct device will be returned from the call. 
  20.  * Any further sysfs files that might be required can be created using this 
  21.  * pointer. 
  22.  * 
  23.  * Returns &struct device pointer on success, or ERR_PTR() on error. 
  24.  * 
  25.  * Note: the struct class passed to this function must have previously 
  26.  * been created with a call to class_create(). 
  27.  */  
  28. struct device *device_create_with_groups(struct class *class,  
  29.                      struct device *parent, dev_t devt,  
  30.                      void *drvdata,  
  31.                      const struct attribute_group **groups,  
  32.                      const char *fmt, ...)  
  33. {  
  34.     va_list vargs;  
  35.     struct device *dev;  
  36.   
  37.   
  38.     va_start(vargs, fmt);  
  39.     dev = device_create_groups_vargs(class, parent, devt, drvdata, groups,  
  40.                      fmt, vargs);  
  41.     va_end(vargs);  
  42.     return dev;  
  43. }  
//device_create_groups_vargs 的实现
[cpp]  view plain  copy
  1. static struct device *  
  2. device_create_groups_vargs(struct class *classstruct device *parent,  
  3.                dev_t devt, void *drvdata,  
  4.                const struct attribute_group **groups,  
  5.                const char *fmt, va_list args)  
  6. {  
  7.     struct device *dev = NULL;  
  8.     int retval = -ENODEV;  
  9.   
  10.   
  11.     if (class == NULL || IS_ERR(class))  
  12.         goto error;  
  13.   
  14.   
  15.     dev = kzalloc(sizeof(*dev), GFP_KERNEL);  
  16.     if (!dev) {  
  17.         retval = -ENOMEM;  
  18.         goto error;  
  19.     }  
  20.   
  21.   
  22.     device_initialize(dev);  
  23.     dev->devt = devt;  
  24.     dev->class = class;  
  25.     dev->parent = parent;  
  26.     dev->groups = groups;  
  27.     dev->release = device_create_release;  
  28.     dev_set_drvdata(dev, drvdata);  
  29.   
  30.   
  31.     retval = kobject_set_name_vargs(&dev->kobj, fmt, args);  
  32.     if (retval)  
  33.         goto error;  
  34.   
  35.   
  36.     retval = device_add(dev);  
  37.     if (retval)  
  38.         goto error;  
  39.   
  40.   
  41.     return dev;  
  42.   
  43.   
  44. error:  
  45.     put_device(dev);  
  46.     return ERR_PTR(retval);  
  47. }  
//device_add的实现
[cpp]  view plain  copy
  1. int device_add(struct device *dev)  
  2. {  
  3.     ...  
  4.     /* first, register with generic layer. */  
  5.     /* we require the name to be set before, and pass NULL */  
  6.     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);  
  7.     if (error)  
  8.         goto Error;  
  9.   
  10.   
  11.     ...  
  12.     error = device_add_attrs(dev);  
  13.     ...  
  14. }  
device_add_attrs的实现这里将会调用device_add_groups,class->dev_groups 作为参数呗传入,此时节点/sys/class/leds/xxx/brightness 已经被
创建 这里的xxx 对应驱动中的red,green,button_backlight,
lcd-backlight ... 已经创建
[cpp]  view plain  copy
  1. static int device_add_attrs(struct device *dev)  
  2. {  
  3.     struct class *class = dev->class;  
  4.     const struct device_type *type = dev->type;  
  5.     int error;  
  6.       
  7.      //这里class->dev_groups先前已经在led-class.c的leds_init中被赋值 leds_class->dev_groups = led_groups;  
  8.     if (class) {  
  9.         error = device_add_groups(dev, class->dev_groups);  
  10.         if (error)  
  11.             return error;  
  12.     }  
  13.     ...  
  14.     return 0;  
  15. }  
5、属性节点的读写方法定义
file:kernel-3.18/drivers/leds/led-class.c
[cpp]  view plain  copy
  1. static int __init leds_init(void)  
  2. {  
  3.     leds_class = class_create(THIS_MODULE, "leds"); //创建class对象  
  4.     ...  
  5.     leds_class->dev_groups = led_groups; //传入brightness节点参数,led属性节点组赋值给leds_class  
  6.     ...  
  7.     return 0;  
  8. }  
再看led_groups的定义如下:
[cpp]  view plain  copy
  1. static const struct attribute_group *led_groups[] = {  
  2.     &led_group,  
  3. #ifdef CONFIG_LEDS_TRIGGERS  
  4.     &led_trigger_group,  
  5. #endif  
  6.     NULL,  
  7. };  
  8. static const struct attribute_group led_group = {  
  9.     .attrs = led_class_attrs,  
  10. };  
  11. static struct attribute *led_class_attrs[] = {  
  12.     &dev_attr_brightness.attr,  
  13.     &dev_attr_max_brightness.attr,  
  14.     NULL,  
  15. };  
当用户空间读取属性节点时候,会直接输入当前亮度值
[cpp]  view plain  copy
  1. static ssize_t 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. }  
  13. static ssize_t brightness_store(struct device *dev,  
  14.         struct device_attribute *attr, const char *buf, size_t size)  
  15. {  
  16.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  17.     unsigned long state;  
  18.     ssize_t ret = -EINVAL;  
  19.   
  20.   
  21.     ret = kstrtoul(buf, 10, &state); //将传入的字符串转换为十进制  
  22.     if (ret)  
  23.         return ret;  
  24.   
  25.   
  26.     if (state == LED_OFF)  
  27.         led_trigger_remove(led_cdev);  
  28.     __led_set_brightness(led_cdev, state); //设置灯的亮度(亮灭)  
  29.   
  30.   
  31.     return size;  
  32. }  
定义brightness属性的变量
[cpp]  view plain  copy
  1. static DEVICE_ATTR_RW(brightness);  
6.button-backlight 亮灯的实现
通过上面节点的 /sys/class/leds/button-backlight/brightness 写方法brightness_store的定义可知,当brightness节点被用户空间写入后,将触发
执行__led_set_brightness,我们可以通过用户空间传入的参数调节灯的亮度,这里这里的传入参数范围0~255 
这里的__led_set_brightness如下定义:
file:kernel-3.18/drivers/leds/leds.h
[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,而 led_cdev->brightness_set在leds_drv.c 中已经被赋值过如下:
file:kernel-3.18/drivers/misc/mediatek/leds/leds_drv.c
[cpp]  view plain  copy
  1. g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;  
[cpp]  view plain  copy
  1. static void mt65xx_led_set(struct led_classdev *led_cdev,  
  2.                enum led_brightness level)  
  3. {  
  4.     struct mt65xx_led_data *led_data =  
  5.         container_of(led_cdev, struct mt65xx_led_data, cdev);  
  6.     ...  
  7.     mt_mt65xx_led_set(led_cdev, level);  
  8.     ...  
  9. }  
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
[cpp]  view plain  copy
  1. void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)  
  2. {  
  3.     struct mt65xx_led_data *led_data =  
  4.         container_of(led_cdev, struct mt65xx_led_data, cdev);  
  5.     /* unsigned long flags; */  
  6.     /* spin_lock_irqsave(&leds_lock, flags); */  
  7.     ...  
  8.   
  9.     /* do something only when level is changed */  
  10.     if (led_data->level != level) {  
  11.         led_data->level = level;  
  12.         if (strcmp(led_data->cust.name, "lcd-backlight") != 0) {  
  13.             LEDS_DEBUG("Set NLED directly %d at time %lu\n",  
  14.                    led_data->level, jiffies);  
  15.             schedule_work(&led_data->work);  
  16.         } else {  
  17.             if (level != 0  
  18.                 && level * CONFIG_LIGHTNESS_MAPPING_VALUE < 255) {  
  19.                 level = 1;  
  20.             } else {  
  21.                 level =  
  22.                     (level * CONFIG_LIGHTNESS_MAPPING_VALUE) /  
  23.                     255;  
  24.             }  
  25.             LEDS_DEBUG  
  26.                 ("Set Backlight directly %d at time %lu, mapping level is %d\n",  
  27.                  led_data->level, jiffies, level);  
  28.             if (MT65XX_LED_MODE_CUST_BLS_PWM == led_data->cust.mode) {  
  29.                 mt_mt65xx_led_set_cust(&led_data->cust,  
  30.                                ((((1 <<  
  31.                                MT_LED_INTERNAL_LEVEL_BIT_CNT)  
  32.                               - 1) * level +  
  33.                              127) / 255));  
  34.             } else {  
  35.             //最终调用mt_mt65xx_led_set_cust  
  36.                 mt_mt65xx_led_set_cust(&led_data->cust, level);   
  37.             }  
  38.         }  
  39.     }  
  40. }  
mt_mt65xx_led_set_cust的实现
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
[cpp]  view plain  copy
  1. int mt_mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level)  
  2. {  
  3.     struct nled_setting led_tmp_setting = { 0, 0, 0 };  
  4.     int tmp_level = level;  
  5.     static bool button_flag;  
  6.     unsigned int BacklightLevelSupport =  
  7.         Cust_GetBacklightLevelSupport_byPWM();  
  8.   
  9.   
  10.     switch (cust->mode) {  
  11.   
  12.   
  13.     case MT65XX_LED_MODE_PWM:  
  14.         if (strcmp(cust->name, "lcd-backlight") == 0) {  
  15.             bl_brightness_hal = level;  
  16.             if (level == 0) {  
  17.                 mt_pwm_disable(cust->data,  
  18.                            cust->config_data.pmic_pad);  
  19.   
  20.   
  21.             } else {  
  22.   
  23.   
  24.                 if (BacklightLevelSupport ==  
  25.                     BACKLIGHT_LEVEL_PWM_256_SUPPORT)  
  26.                     level = brightness_mapping(tmp_level);  
  27.                 else  
  28.                     level = brightness_mapto64(tmp_level);  
  29.                 mt_backlight_set_pwm(cust->data, level,  
  30.                              bl_div_hal,  
  31.                              &cust->config_data);  
  32.             }  
  33.             bl_duty_hal = level;  
  34.   
  35.   
  36.         } else {  
  37.             if (level == 0) {  
  38.                 led_tmp_setting.nled_mode = NLED_OFF;  
  39.                 mt_led_set_pwm(cust->data, &led_tmp_setting);  
  40.                 mt_pwm_disable(cust->data,  
  41.                            cust->config_data.pmic_pad);  
  42.             } else {  
  43.                 led_tmp_setting.nled_mode = NLED_ON;  
  44.                 mt_led_set_pwm(cust->data, &led_tmp_setting);  
  45.             }  
  46.         }  
  47.         return 1;  
  48.   
  49.   
  50.     case MT65XX_LED_MODE_GPIO:  
  51.         LEDS_DEBUG("brightness_set_cust:go GPIO mode!!!!!\n");  
  52.         return ((cust_set_brightness) (cust->data)) (level);  
  53.    //这里的MT65XX_LED_MODE_PMIC对应button-backlight的配置的mode=3   
  54.     case MT65XX_LED_MODE_PMIC:    
  55.         /* for button baclight used SINK channel, when set button ISINK, 
  56.             don't do disable other ISINK channel */  
  57.         //使用button-backlight的调用如下:  
  58.         if ((strcmp(cust->name, "button-backlight") == 0)) {   
  59.             if (button_flag == false) {  
  60.                 switch (cust->data) {  
  61.                 case MT65XX_LED_PMIC_NLED_ISINK0:  
  62.                     button_flag_isink0 = 1;  
  63.                     break;  
  64.                 case MT65XX_LED_PMIC_NLED_ISINK1:  
  65.                     button_flag_isink1 = 1;  
  66.                     break;  
  67.                 case MT65XX_LED_PMIC_NLED_ISINK2:  
  68.                     button_flag_isink2 = 1;  
  69.                     break;  
  70.                 case MT65XX_LED_PMIC_NLED_ISINK3:  
  71.                     button_flag_isink3 = 1;  
  72.                     break;  
  73.                 default:  
  74.                     break;  
  75.                 }  
  76.                 button_flag = true;  
  77.             }  
  78.         }  
  79.         return mt_brightness_set_pmic(cust->data, level, bl_div_hal);  
  80.   
  81.   
  82.     case MT65XX_LED_MODE_CUST_LCM:  
  83.         if (strcmp(cust->name, "lcd-backlight") == 0)  
  84.             bl_brightness_hal = level;  
  85.         LEDS_DEBUG("brightness_set_cust:backlight control by LCM\n");  
  86.         /* warning for this API revork */  
  87.         return ((cust_brightness_set) (cust->data)) (level, bl_div_hal);  
  88.   
  89.   
  90.     case MT65XX_LED_MODE_CUST_BLS_PWM:  
  91.         if (strcmp(cust->name, "lcd-backlight") == 0)  
  92.             bl_brightness_hal = level;  
  93.         return ((cust_set_brightness) (cust->data)) (level);  
  94.   
  95.   
  96.     case MT65XX_LED_MODE_NONE:  
  97.     default:  
  98.         break;  
  99.     }  
  100.     return -1;  
  101. }  
也就是说当用户对属性节点 /sys/class/leds/button-backlight/brightness 写入时最终调用mt_brightness_set_pmic函数,
mt_brightness_set_pmic的实现如下:
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
[cpp]  view plain  copy
  1. int mt_brightness_set_pmic(enum mt65xx_led_pmic pmic_type, u32 level, u32 div)  
  2. {  
  3.     static bool first_time = true;  
  4.   
  5.   
  6.     LEDS_DEBUG("PMIC#%d:%d\n", pmic_type, level);  
  7.     mutex_lock(&leds_pmic_mutex);  
  8.     if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK0) {  
  9.         if ((button_flag_isink0 == 0) && (first_time == true)) {    /* button 
  10.             flag ==0, means this ISINK is not for button backlight */  
  11.             if (button_flag_isink1 == 0)  
  12.                 pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF);   /* sw 
  13.             workround for sync leds status */  
  14.             if (button_flag_isink2 == 0)  
  15.                 pmic_set_register_value(PMIC_ISINK_CH2_EN,  
  16.                             NLED_OFF);  
  17.             if (button_flag_isink3 == 0)  
  18.                 pmic_set_register_value(PMIC_ISINK_CH3_EN,  
  19.                             NLED_OFF);  
  20.             first_time = false;  
  21.         }  
  22.         pmic_set_register_value(PMIC_RG_DRV_32K_CK_PDN, 0x0);   /* Disable power down */  
  23.         pmic_set_register_value(PMIC_RG_DRV_ISINK0_CK_PDN, 0);  
  24.         pmic_set_register_value(PMIC_RG_DRV_ISINK0_CK_CKSEL, 0);  
  25.         pmic_set_register_value(PMIC_ISINK_CH0_MODE, ISINK_PWM_MODE);  
  26.         pmic_set_register_value(PMIC_ISINK_CH0_STEP, ISINK_3);  /* 16mA */  
  27.         pmic_set_register_value(PMIC_ISINK_DIM0_DUTY, 15);  
  28.         pmic_set_register_value(PMIC_ISINK_DIM0_FSEL, ISINK_1KHZ);  /* 1KHz */  
  29.         if (level)  
  30.             pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_ON);  
  31.         else  
  32.             pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF);  
  33.         mutex_unlock(&leds_pmic_mutex);  
  34.         return 0;  
  35.     } else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK1) {  
  36.         if ((button_flag_isink1 == 0) && (first_time == true)) {    /* button 
  37.             flag ==0, means this ISINK is not for button backlight */  
  38.             if (button_flag_isink0 == 0)  
  39.                 pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF);   /* sw 
  40.                 workround for sync leds status */  
  41.             if (button_flag_isink2 == 0)  
  42.                 pmic_set_register_value(PMIC_ISINK_CH2_EN,  
  43.                             NLED_OFF);  
  44.             if (button_flag_isink3 == 0)  
  45.                 pmic_set_register_value(PMIC_ISINK_CH3_EN,  
  46.                             NLED_OFF);  
  47.             first_time = false;  
  48.         }  
  49.         pmic_set_register_value(PMIC_RG_DRV_32K_CK_PDN, 0x0);   /* Disable power down */  
  50.         pmic_set_register_value(PMIC_RG_DRV_ISINK1_CK_PDN, 0);  
  51.         pmic_set_register_value(PMIC_RG_DRV_ISINK1_CK_CKSEL, 0);  
  52.         pmic_set_register_value(PMIC_ISINK_CH1_MODE, ISINK_PWM_MODE);  
  53.         pmic_set_register_value(PMIC_ISINK_CH1_STEP, ISINK_3);  /* 16mA */  
  54.         pmic_set_register_value(PMIC_ISINK_DIM1_DUTY, 15);  
  55.         pmic_set_register_value(PMIC_ISINK_DIM1_FSEL, ISINK_1KHZ);  /* 1KHz */  
  56.         if (level)  
  57.             pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_ON);  
  58.         else  
  59.             pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF);  
  60.         mutex_unlock(&leds_pmic_mutex);  
  61.         return 0;  
  62.     }  
  63.     mutex_unlock(&leds_pmic_mutex);  
  64.     return -1;  
  65. }  
上述pmic_set_register_value的操作就是对ISINK具体寄存器的操作,本文不作深入研究
三、总结
通过上述的分析,我们大致可以看出在mtk平台上leds系列的驱动流程大致如下,先在dts中定义各个led节点的配置,配
置如mode,name,data 预留给driver调用,然后创建common drver (通用驱动)对各个led统一管理,在通用驱动中各个不
同类型led 通过数组区分,common driver对各个led进行统一的设备注册,属性节点创建等。当然led的种类繁多还有充
电指示灯,呼吸灯等,在加上每种灯硬件配置不一样驱动实现方式也不同,这个需要另行分析了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值