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

Liam Girdwood lrg at slimlogic.co.uk
Wed Dec 2 13:23:55 EST 2009


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 at studenti.unina.it>

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 at studenti.unina.it>
> + *
> + * 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 at studenti.unina.it>");
> +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 at studenti.unina.it>
> + *
> + * 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 */





More information about the linux-arm-kernel mailing list