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