Re: [PATCH] leds: Add LED class driver for regulator driven LEDs.

From: Liam Girdwood
Date: Wed Dec 02 2009 - 13:24:07 EST


On Wed, 2009-12-02 at 18:40 +0100, Antonio Ospite wrote:
> This driver provides an interface for controlling LEDs (or vibrators)
> connected to PMICs for which there is a regulator framework driver.
>
> This driver can be used, for instance, to control vibrator on all Motorola EZX
> phones using the pcap-regulator driver services.
>
> Signed-off-by: Antonio Ospite <ospite@xxxxxxxxxxxxxxxxx>

Some very minor points below.

> ---
> The patch is against:
> git://git.o-hand.com/linux-rpurdie-leds for-mm
>
> A doubt I had was about leaving the 'supply' variable configurable from
> platform data, or relying on some fixed value like "vled", but leaving it
> configurable covers the case when we have different regulators used for
> different LEDs or vibrators.
>
> Should I add a note in Documentation/ about how to use it? Tell me if so.
>
> Thanks,
> Antonio
>
> P.S.: for those who get the mail from LKML, please Cc me on reply.
>
> drivers/leds/Kconfig | 6 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-regulator.c | 214 ++++++++++++++++++++++++++++++++++++++++
> include/linux/leds-regulator.h | 20 ++++
> 4 files changed, 241 insertions(+), 0 deletions(-)
> create mode 100644 drivers/leds/leds-regulator.c
> create mode 100644 include/linux/leds-regulator.h
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index f12a996..8a0e1ec 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -229,6 +229,12 @@ config LEDS_PWM
> help
> This option enables support for pwm driven LEDs
>
> +config LEDS_REGULATOR
> + tristate "REGULATOR driven LED support"
> + depends on LEDS_CLASS && REGULATOR
> + help
> + This option enables support for regulator driven LEDs.
> +
> config LEDS_BD2802
> tristate "LED driver for BD2802 RGB LED"
> depends on LEDS_CLASS && I2C
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 176f0c6..9e63869 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -29,6 +29,7 @@ obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
> obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
> obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
> obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
> +obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
> obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
> obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
> obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
> diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
> new file mode 100644
> index 0000000..fca5d42
> --- /dev/null
> +++ b/drivers/leds/leds-regulator.c
> @@ -0,0 +1,214 @@
> +/*
> + * leds-regulator.c - LED class driver for regulator driven LEDs.
> + *
> + * Copyright (C) 2009 Antonio Ospite <ospite@xxxxxxxxxxxxxxxxx>
> + *
> + * Inspired by leds-wm8350 driver.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/workqueue.h>
> +#include <linux/leds.h>
> +#include <linux/leds-regulator.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define to_regulator_led(led_cdev) \
> + container_of(led_cdev, struct regulator_led, cdev)
> +
> +struct regulator_led {
> + struct led_classdev cdev;
> + enum led_brightness value;
> + int enabled;
> + struct mutex mutex;
> + struct work_struct work;
> +
> + struct regulator *vcc;
> +};
> +
> +static inline int led_regulator_get_max_brightness(struct regulator *supply)
> +{
> + return regulator_count_voltages(supply);
> +}
> +
> +static int led_regulator_get_vdd(struct regulator *supply,
> + enum led_brightness brightness)
> +{
> + if (brightness == 0)
> + return 0;
> +
> + return regulator_list_voltage(supply, brightness - 1);
> +}
> +
> +
> +static void regulator_led_enable(struct regulator_led *led)
> +{
> + int ret;
> +
> + if (led->enabled)
> + return;
> +
> + ret = regulator_enable(led->vcc);
> + if (ret != 0) {
> + dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);
> + return;
> + }
> +
> + led->enabled = 1;
> +}
> +
> +static void regulator_led_disable(struct regulator_led *led)
> +{
> + int ret;
> +
> + if (!led->enabled)
> + return;
> +
> + ret = regulator_disable(led->vcc);
> + if (ret != 0) {
> + dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);
> + return;
> + }
> +
> + led->enabled = 0;
> +}
> +
> +static void led_work(struct work_struct *work)
> +{
> + struct regulator_led *led;
> + int voltage;
> + int ret;
> +
> + led = container_of(work, struct regulator_led, work);
> +
> + mutex_lock(&led->mutex);
> +
> + if (led->value == 0) {

LED_OFF instead of 0 here ?

> + regulator_led_disable(led);
> + goto out;
> + }
> +
> + voltage = led_regulator_get_vdd(led->vcc, led->value);
> + dev_dbg(led->cdev.dev, "brightness: %d voltage: %d",
> + led->value, voltage);
> +
> + ret = regulator_set_voltage(led->vcc, voltage, voltage);
> + if (ret != 0)
> + dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",
> + voltage, ret);

Some regulators may not support voltage change (via hw design or
constraints) so set_voltage() will fail. We probably want to handle this
regulator type so we don't call set_voltage().

> +
> + regulator_led_enable(led);
> +
> +out:
> + mutex_unlock(&led->mutex);
> +}
> +
> +static void regulator_led_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness value)
> +{
> + struct regulator_led *led = to_regulator_led(led_cdev);
> +
> + led->value = value;
> + schedule_work(&led->work);
> +}
> +
> +static int regulator_led_probe(struct platform_device *pdev)
> +{
> + struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
> + struct regulator_led *led;
> + struct regulator *vcc;
> + int ret;
> +
> + if (pdata == NULL) {
> + dev_err(&pdev->dev, "no platform data\n");
> + return -ENODEV;
> + }
> +
> + vcc = regulator_get(&pdev->dev, pdata->supply);
> + if (IS_ERR(vcc)) {
> + dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
> + return PTR_ERR(vcc);;

double ;; here

> + }
> +
> + led = kzalloc(sizeof(*led), GFP_KERNEL);
> + if (led == NULL) {
> + ret = -ENOMEM;
> + goto err_vcc;
> + }
> +
> + ret = led_regulator_get_max_brightness(vcc);
> + if (ret < 0)
> + goto err_led;
> +
> + led->cdev.max_brightness = ret;
> +
> + led->cdev.brightness_set = regulator_led_brightness_set;
> + led->cdev.name = pdata->name;
> + led->cdev.flags |= LED_CORE_SUSPENDRESUME;
> + led->enabled = regulator_is_enabled(vcc);
> + led->vcc = vcc;
> +
> + mutex_init(&led->mutex);
> + INIT_WORK(&led->work, led_work);
> +
> + led->value = LED_OFF;
> + platform_set_drvdata(pdev, led);
> +
> + ret = led_classdev_register(&pdev->dev, &led->cdev);
> + if (ret < 0) {
> + cancel_work_sync(&led->work);
> + goto err_led;
> + }
> +
> + return 0;
> +
> +err_led:
> + kfree(led);
> +err_vcc:
> + regulator_put(vcc);
> + return ret;
> +}
> +
> +static int regulator_led_remove(struct platform_device *pdev)
> +{
> + struct regulator_led *led = platform_get_drvdata(pdev);
> +
> + led_classdev_unregister(&led->cdev);
> + cancel_work_sync(&led->work);
> + regulator_led_disable(led);
> + regulator_put(led->vcc);
> + kfree(led);
> + return 0;
> +}
> +
> +static struct platform_driver regulator_led_driver = {
> + .driver = {
> + .name = "leds-regulator",
> + .owner = THIS_MODULE,
> + },
> + .probe = regulator_led_probe,
> + .remove = regulator_led_remove,
> +};
> +
> +static int __devinit regulator_led_init(void)
> +{
> + return platform_driver_register(&regulator_led_driver);
> +}
> +module_init(regulator_led_init);
> +
> +static void regulator_led_exit(void)
> +{
> + platform_driver_unregister(&regulator_led_driver);
> +}
> +module_exit(regulator_led_exit);
> +
> +MODULE_AUTHOR("Antonio Ospite <ospite@xxxxxxxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Regulator driven LED driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:leds-regulator");
> diff --git a/include/linux/leds-regulator.h b/include/linux/leds-regulator.h
> new file mode 100644
> index 0000000..a5850fd
> --- /dev/null
> +++ b/include/linux/leds-regulator.h
> @@ -0,0 +1,20 @@
> +/*
> + * leds-regulator.h - platform data structure for regulator driven LEDs.
> + *
> + * Copyright (C) 2009 Antonio Ospite <ospite@xxxxxxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#ifndef __LINUX_LEDS_REGULATOR_H
> +#define __LINUX_LEDS_REGULATOR_H
> +
> +struct led_regulator_platform_data {
> + char *name; /* LED name as expected by LED class */
> + char *supply;
> +};
> +
> +#endif /* __LINUX_LEDS_REGULATOR_H */


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/