aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-04-06 13:22:45 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-06 13:22:45 -0700
commit3cd69271f86770499425c7cea2902512ba936a75 (patch)
tree5b1bbd7ae199afcf4e2f283c95c72be03013e069
parent1aa2a7cc6fd7b5c86681a6ae9dfd1072c261a435 (diff)
parent67a32ec750109fdfc7cba311145a18d543521822 (diff)
Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds: leds: introduce lp5521 led driver leds: just ignore invalid GPIOs in leds-gpio leds: Fix &&/|| confusion in leds-pca9532.c leds: move h1940-leds's probe function to .devinit.text leds: remove an unnecessary "goto" on drivers/leds/leds-s3c24.c leds: add BD2802GU LED driver leds: remove experimental flag from leds-clevo-mail leds: Prevent multiple LED triggers with the same name leds: Add gpio-led trigger leds: Add rb532 LED driver for the User LED leds: Add suspend/resume state flags to leds-gpio leds: simple driver for pwm driven LEDs leds: Fix leds-gpio driver multiple module_init/exit usage leds: Add dac124s085 driver leds: allow led-drivers to use a variable range of brightness values leds: Add openfirmware platform device support
-rw-r--r--Documentation/powerpc/dts-bindings/gpio/led.txt46
-rw-r--r--drivers/leds/Kconfig75
-rw-r--r--drivers/leds/Makefile7
-rw-r--r--drivers/leds/led-class.c21
-rw-r--r--drivers/leds/led-triggers.c10
-rw-r--r--drivers/leds/leds-bd2802.c765
-rw-r--r--drivers/leds/leds-dac124s085.c150
-rw-r--r--drivers/leds/leds-gpio.c225
-rw-r--r--drivers/leds/leds-h1940.c2
-rw-r--r--drivers/leds/leds-pca9532.c2
-rw-r--r--drivers/leds/leds-pwm.c153
-rw-r--r--drivers/leds/leds-rb532.c77
-rw-r--r--drivers/leds/leds-s3c24xx.c7
-rw-r--r--drivers/leds/leds.h4
-rw-r--r--drivers/leds/ledtrig-default-on.c2
-rw-r--r--drivers/leds/ledtrig-gpio.c239
-rw-r--r--drivers/leds/ledtrig-heartbeat.c4
-rw-r--r--drivers/leds/ledtrig-ide-disk.c3
-rw-r--r--drivers/leds/ledtrig-timer.c2
-rw-r--r--include/linux/leds-bd2802.h26
-rw-r--r--include/linux/leds.h4
-rw-r--r--include/linux/leds_pwm.h21
22 files changed, 1772 insertions, 73 deletions
diff --git a/Documentation/powerpc/dts-bindings/gpio/led.txt b/Documentation/powerpc/dts-bindings/gpio/led.txt
index ff51f4c0fa9..4fe14deedc0 100644
--- a/Documentation/powerpc/dts-bindings/gpio/led.txt
+++ b/Documentation/powerpc/dts-bindings/gpio/led.txt
@@ -1,15 +1,43 @@
-LED connected to GPIO
+LEDs connected to GPIO lines
Required properties:
-- compatible : should be "gpio-led".
-- label : (optional) the label for this LED. If omitted, the label is
+- compatible : should be "gpio-leds".
+
+Each LED is represented as a sub-node of the gpio-leds device. Each
+node's name represents the name of the corresponding LED.
+
+LED sub-node properties:
+- gpios : Should specify the LED's GPIO, see "Specifying GPIO information
+ for devices" in Documentation/powerpc/booting-without-of.txt. Active
+ low LEDs should be indicated using flags in the GPIO specifier.
+- label : (optional) The label for this LED. If omitted, the label is
taken from the node name (excluding the unit address).
-- gpios : should specify LED GPIO.
+- linux,default-trigger : (optional) This parameter, if present, is a
+ string defining the trigger assigned to the LED. Current triggers are:
+ "backlight" - LED will act as a back-light, controlled by the framebuffer
+ system
+ "default-on" - LED will turn on
+ "heartbeat" - LED "double" flashes at a load average based rate
+ "ide-disk" - LED indicates disk activity
+ "timer" - LED flashes at a fixed, configurable rate
-Example:
+Examples:
-led@0 {
- compatible = "gpio-led";
- label = "hdd";
- gpios = <&mcu_pio 0 1>;
+leds {
+ compatible = "gpio-leds";
+ hdd {
+ label = "IDE Activity";
+ gpios = <&mcu_pio 0 1>; /* Active low */
+ linux,default-trigger = "ide-disk";
+ };
};
+
+run-control {
+ compatible = "gpio-leds";
+ red {
+ gpios = <&mpc8572 6 0>;
+ };
+ green {
+ gpios = <&mpc8572 7 0>;
+ };
+}
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index d9db17624f1..9b60b6b684d 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -31,6 +31,13 @@ config LEDS_LOCOMO
This option enables support for the LEDs on Sharp Locomo.
Zaurus models SL-5500 and SL-5600.
+config LEDS_MIKROTIK_RB532
+ tristate "LED Support for Mikrotik Routerboard 532"
+ depends on LEDS_CLASS && MIKROTIK_RB532
+ help
+ This option enables support for the so called "User LED" of
+ Mikrotik's Routerboard 532.
+
config LEDS_S3C24XX
tristate "LED Support for Samsung S3C24XX GPIO LEDs"
depends on LEDS_CLASS && ARCH_S3C2410
@@ -117,11 +124,40 @@ config LEDS_GPIO
help
This option enables support for the LEDs connected to GPIO
outputs. To be useful the particular board must have LEDs
- and they must be connected to the GPIO lines.
+ and they must be connected to the GPIO lines. The LEDs must be
+ defined as platform devices and/or OpenFirmware platform devices.
+ The code to use these bindings can be selected below.
+
+config LEDS_GPIO_PLATFORM
+ bool "Platform device bindings for GPIO LEDs"
+ depends on LEDS_GPIO
+ default y
+ help
+ Let the leds-gpio driver drive LEDs which have been defined as
+ platform devices. If you don't know what this means, say yes.
+
+config LEDS_GPIO_OF
+ bool "OpenFirmware platform device bindings for GPIO LEDs"
+ depends on LEDS_GPIO && OF_DEVICE
+ default y
+ help
+ Let the leds-gpio driver drive LEDs which have been defined as
+ of_platform devices. For instance, LEDs which are listed in a "dts"
+ file.
+
+config LEDS_LP5521
+ tristate "LED Support for the LP5521 LEDs"
+ depends on LEDS_CLASS && I2C
+ help
+ If you say 'Y' here you get support for the National Semiconductor
+ LP5521 LED driver used in n8x0 boards.
+
+ This driver can be built as a module by choosing 'M'. The module
+ will be called leds-lp5521.
config LEDS_CLEVO_MAIL
- tristate "Mail LED on Clevo notebook (EXPERIMENTAL)"
- depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI && EXPERIMENTAL
+ tristate "Mail LED on Clevo notebook"
+ depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
help
This driver makes the mail LED accessible from userspace
programs through the leds subsystem. This LED have three
@@ -171,6 +207,26 @@ config LEDS_DA903X
This option enables support for on-chip LED drivers found
on Dialog Semiconductor DA9030/DA9034 PMICs.
+config LEDS_DAC124S085
+ tristate "LED Support for DAC124S085 SPI DAC"
+ depends on LEDS_CLASS && SPI
+ help
+ This option enables support for DAC124S085 SPI DAC from NatSemi,
+ which can be used to control up to four LEDs.
+
+config LEDS_PWM
+ tristate "PWM driven LED Support"
+ depends on LEDS_CLASS && HAVE_PWM
+ help
+ This option enables support for pwm driven LEDs
+
+config LEDS_BD2802
+ tristate "LED driver for BD2802 RGB LED"
+ depends on LEDS_CLASS && I2C
+ help
+ This option enables support for BD2802GU RGB LED driver chips
+ accessed via the I2C bus.
+
comment "LED Triggers"
config LEDS_TRIGGERS
@@ -216,6 +272,19 @@ config LEDS_TRIGGER_BACKLIGHT
If unsure, say N.
+config LEDS_TRIGGER_GPIO
+ tristate "LED GPIO Trigger"
+ depends on LEDS_TRIGGERS
+ depends on GPIOLIB
+ help
+ This allows LEDs to be controlled by gpio events. It's good
+ when using gpios as switches and triggering the needed LEDs
+ from there. One use case is n810's keypad LEDs that could
+ be triggered by this trigger when user slides up to show
+ keypad.
+
+ If unsure, say N.
+
config LEDS_TRIGGER_DEFAULT_ON
tristate "LED Default ON Trigger"
depends on LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9d76f0f160a..2d41c4dcf92 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -6,7 +6,9 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
+obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
+obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
@@ -24,10 +26,15 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
+obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
+
+# LED SPI Drivers
+obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
+obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 52f82e3ea13..f2cc13d7681 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -64,7 +64,16 @@ static ssize_t led_brightness_store(struct device *dev,
return ret;
}
+static ssize_t led_max_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", led_cdev->max_brightness);
+}
+
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
+static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
#endif
@@ -138,6 +147,13 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
list_add_tail(&led_cdev->node, &leds_list);
up_write(&leds_list_lock);
+ if (!led_cdev->max_brightness)
+ led_cdev->max_brightness = LED_FULL;
+
+ rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
+ if (rc)
+ goto err_out_attr_max;
+
led_update_brightness(led_cdev);
#ifdef CONFIG_LEDS_TRIGGERS
@@ -155,9 +171,11 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
+ device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
+#endif
+err_out_attr_max:
device_remove_file(led_cdev->dev, &dev_attr_brightness);
list_del(&led_cdev->node);
-#endif
err_out:
device_unregister(led_cdev->dev);
return rc;
@@ -172,6 +190,7 @@ EXPORT_SYMBOL_GPL(led_classdev_register);
*/
void led_classdev_unregister(struct led_classdev *led_cdev)
{
+ device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
device_remove_file(led_cdev->dev, &dev_attr_brightness);
#ifdef CONFIG_LEDS_TRIGGERS
device_remove_file(led_cdev->dev, &dev_attr_trigger);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index f910eaffe3a..d8ddd9ef899 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -156,12 +156,20 @@ EXPORT_SYMBOL_GPL(led_trigger_set_default);
int led_trigger_register(struct led_trigger *trigger)
{
struct led_classdev *led_cdev;
+ struct led_trigger *trig;
rwlock_init(&trigger->leddev_list_lock);
INIT_LIST_HEAD(&trigger->led_cdevs);
- /* Add to the list of led triggers */
down_write(&triggers_list_lock);
+ /* Make sure the trigger's name isn't already in use */
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(trig->name, trigger->name)) {
+ up_write(&triggers_list_lock);
+ return -EEXIST;
+ }
+ }
+ /* Add to the list of led triggers */
list_add_tail(&trigger->next_trig, &trigger_list);
up_write(&triggers_list_lock);
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
new file mode 100644
index 00000000000..4149ecb3a9b
--- /dev/null
+++ b/drivers/leds/leds-bd2802.c
@@ -0,0 +1,765 @@
+/*
+ * leds-bd2802.c - RGB LED Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.com>
+ *
+ * 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.
+ *
+ * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/leds-bd2802.h>
+
+
+#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
+
+#define BD2802_LED_OFFSET 0xa
+#define BD2802_COLOR_OFFSET 0x3
+
+#define BD2802_REG_CLKSETUP 0x00
+#define BD2802_REG_CONTROL 0x01
+#define BD2802_REG_HOURSETUP 0x02
+#define BD2802_REG_CURRENT1SETUP 0x03
+#define BD2802_REG_CURRENT2SETUP 0x04
+#define BD2802_REG_WAVEPATTERN 0x05
+
+#define BD2802_CURRENT_032 0x10 /* 3.2mA */
+#define BD2802_CURRENT_000 0x00 /* 0.0mA */
+
+#define BD2802_PATTERN_FULL 0x07
+#define BD2802_PATTERN_HALF 0x03
+
+enum led_ids {
+ LED1,
+ LED2,
+ LED_NUM,
+};
+
+enum led_colors {
+ RED,
+ GREEN,
+ BLUE,
+};
+
+enum led_bits {
+ BD2802_OFF,
+ BD2802_BLINK,
+ BD2802_ON,
+};
+
+/*
+ * State '0' : 'off'
+ * State '1' : 'blink'
+ * State '2' : 'on'.
+ */
+struct led_state {
+ unsigned r:2;
+ unsigned g:2;
+ unsigned b:2;
+};
+
+struct bd2802_led {
+ struct bd2802_led_platform_data *pdata;
+ struct i2c_client *client;
+ struct rw_semaphore rwsem;
+ struct work_struct work;
+
+ struct led_state led[2];
+
+ /*
+ * Making led_classdev as array is not recommended, because array
+ * members prevent using 'container_of' macro. So repetitive works
+ * are needed.
+ */
+ struct led_classdev cdev_led1r;
+ struct led_classdev cdev_led1g;
+ struct led_classdev cdev_led1b;
+ struct led_classdev cdev_led2r;
+ struct led_classdev cdev_led2g;
+ struct led_classdev cdev_led2b;
+
+ /*
+ * Advanced Configuration Function(ADF) mode:
+ * In ADF mode, user can set registers of BD2802GU directly,
+ * therefore BD2802GU doesn't enter reset state.
+ */
+ int adf_on;
+
+ enum led_ids led_id;
+ enum led_colors color;
+ enum led_bits state;
+};
+
+
+/*--------------------------------------------------------------*/
+/* BD2802GU helper functions */
+/*--------------------------------------------------------------*/
+
+static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ switch (color) {
+ case RED:
+ return !led->led[id].r;
+ case GREEN:
+ return !led->led[id].g;
+ case BLUE:
+ return !led->led[id].b;
+ default:
+ dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
+ return -EINVAL;
+ }
+}
+
+static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
+{
+ if (led->led[id].r || led->led[id].g || led->led[id].b)
+ return 0;
+
+ return 1;
+}
+
+static inline int bd2802_is_all_off(struct bd2802_led *led)
+{
+ int i;
+
+ for (i = 0; i < LED_NUM; i++)
+ if (!bd2802_is_led_off(led, i))
+ return 0;
+
+ return 1;
+}
+
+static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
+{
+ return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
+}
+
+static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
+ u8 reg_offset)
+{
+ return reg_offset + bd2802_get_base_offset(id, color);
+}
+
+
+/*--------------------------------------------------------------*/
+/* BD2802GU core functions */
+/*--------------------------------------------------------------*/
+
+static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret >= 0)
+ return 0;
+
+ dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+ __func__, reg, val, ret);
+
+ return ret;
+}
+
+static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color, enum led_bits led_bit)
+{
+ int i;
+ u8 value;
+
+ for (i = 0; i < LED_NUM; i++) {
+ if (i == id) {
+ switch (color) {
+ case RED:
+ led->led[i].r = led_bit;
+ break;
+ case GREEN:
+ led->led[i].g = led_bit;
+ break;
+ case BLUE:
+ led->led[i].b = led_bit;
+ break;
+ default:
+ dev_err(&led->client->dev,
+ "%s: Invalid color\n", __func__);
+ return;
+ }
+ }
+ }
+
+ if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
+ return;
+
+ if (!bd2802_is_led_off(led, id))
+ return;
+
+ if (bd2802_is_all_off(led) && !led->adf_on) {
+ gpio_set_value(led->pdata->reset_gpio, 0);
+ return;
+ }
+
+ /*
+ * In this case, other led is turned on, and current led is turned
+ * off. So set RGB LED Control register to stop the current RGB LED
+ */
+ value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
+ bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
+}
+
+static void bd2802_configure(struct bd2802_led *led)
+{
+ struct bd2802_led_platform_data *pdata = led->pdata;
+ u8 reg;
+
+ reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
+ bd2802_write_byte(led->client, reg, pdata->rgb_time);
+
+ reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
+ bd2802_write_byte(led->client, reg, pdata->rgb_time);
+}
+
+static void bd2802_reset_cancel(struct bd2802_led *led)
+{
+ gpio_set_value(led->pdata->reset_gpio, 1);
+ udelay(100);
+ bd2802_configure(led);
+}
+
+static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
+{
+ enum led_ids other_led = (id == LED1) ? LED2 : LED1;
+ u8 value, other_led_on;
+
+ other_led_on = !bd2802_is_led_off(led, other_led);
+ if (id == LED1)
+ value = LED_CTL(other_led_on, 1);
+ else
+ value = LED_CTL(1 , other_led_on);
+
+ bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
+}
+
+static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ u8 reg;
+
+ if (bd2802_is_all_off(led) && !led->adf_on)
+ bd2802_reset_cancel(led);
+
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
+ bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
+
+ bd2802_enable(led, id);
+ bd2802_update_state(led, id, color, BD2802_ON);
+}
+
+static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ u8 reg;
+
+ if (bd2802_is_all_off(led) && !led->adf_on)
+ bd2802_reset_cancel(led);
+
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
+ bd2802_write_byte(led->client, reg, BD2802_PATTERN_HALF);
+
+ bd2802_enable(led, id);
+ bd2802_update_state(led, id, color, BD2802_BLINK);
+}
+
+static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color, enum led_bits led_bit)
+{
+ if (led_bit == BD2802_OFF) {
+ dev_err(&led->client->dev,
+ "Only 'blink' and 'on' are allowed\n");
+ return;
+ }
+
+ if (led_bit == BD2802_BLINK)
+ bd2802_set_blink(led, id, color);
+ else
+ bd2802_set_on(led, id, color);
+}
+
+static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
+ enum led_colors color)
+{
+ u8 reg;
+
+ if (bd2802_is_rgb_off(led, id, color))
+ return;
+
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+ reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+ bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+
+ bd2802_update_state(led, id, color, BD2802_OFF);
+}
+
+static void bd2802_restore_state(struct bd2802_led *led)
+{
+ int i;
+
+ for (i = 0; i < LED_NUM; i++) {
+ if (led->led[i].r)
+ bd2802_turn_on(led, i, RED, led->led[i].r);
+ if (led->led[i].g)
+ bd2802_turn_on(led, i, GREEN, led->led[i].g);
+ if (led->led[i].b)
+ bd2802_turn_on(led, i, BLUE, led->led[i].b);
+ }
+}
+
+#define BD2802_SET_REGISTER(reg_addr, reg_name) \
+static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
+ unsigned long val; \
+ int ret; \
+ if (!count) \
+ return -EINVAL; \
+ ret = strict_strtoul(buf, 16, &val); \
+ if (ret) \
+ return ret; \
+ down_write(&led->rwsem); \
+ bd2802_write_byte(led->client, reg_addr, (u8) val); \
+ up_write(&led->rwsem); \
+ return count; \
+} \
+static struct device_attribute bd2802_reg##reg_addr##_attr = { \
+ .attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \
+ .store = bd2802_store_reg##reg_addr, \
+};
+
+BD2802_SET_REGISTER(0x00, "0x00");
+BD2802_SET_REGISTER(0x01, "0x01");
+BD2802_SET_REGISTER(0x02, "0x02");
+BD2802_SET_REGISTER(0x03, "0x03");
+BD2802_SET_REGISTER(0x04, "0x04");
+BD2802_SET_REGISTER(0x05, "0x05");
+BD2802_SET_REGISTER(0x06, "0x06");
+BD2802_SET_REGISTER(0x07, "0x07");
+BD2802_SET_REGISTER(0x08, "0x08");
+BD2802_SET_REGISTER(0x09, "0x09");
+BD2802_SET_REGISTER(0x0a, "0x0a");
+BD2802_SET_REGISTER(0x0b, "0x0b");
+BD2802_SET_REGISTER(0x0c, "0x0c");
+BD2802_SET_REGISTER(0x0d, "0x0d");
+BD2802_SET_REGISTER(0x0e, "0x0e");
+BD2802_SET_REGISTER(0x0f, "0x0f");
+BD2802_SET_REGISTER(0x10, "0x10");
+BD2802_SET_REGISTER(0x11, "0x11");
+BD2802_SET_REGISTER(0x12, "0x12");
+BD2802_SET_REGISTER(0x13, "0x13");
+BD2802_SET_REGISTER(0x14, "0x14");
+BD2802_SET_REGISTER(0x15, "0x15");
+
+static struct device_attribute *bd2802_addr_attributes[] = {
+ &bd2802_reg0x00_attr,
+ &bd2802_reg0x01_attr,
+ &bd2802_reg0x02_attr,
+ &bd2802_reg0x03_attr,
+ &bd2802_reg0x04_attr,
+ &bd2802_reg0x05_attr,
+ &bd2802_reg0x06_attr,
+ &bd2802_reg0x07_attr,
+ &bd2802_reg0x08_attr,
+ &bd2802_reg0x09_attr,
+ &bd2802_reg0x0a_attr,
+ &bd2802_reg0x0b_attr,
+ &bd2802_reg0x0c_attr,
+ &bd2802_reg0x0d_attr,
+ &bd2802_reg0x0e_attr,
+ &bd2802_reg0x0f_attr,
+ &bd2802_reg0x10_attr,
+ &bd2802_reg0x11_attr,
+ &bd2802_reg0x12_attr,
+ &bd2802_reg0x13_attr,
+ &bd2802_reg0x14_attr,
+ &bd2802_reg0x15_attr,
+};
+
+static void bd2802_enable_adv_conf(struct bd2802_led *led)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
+ ret = device_create_file(&led->client->dev,
+ bd2802_addr_attributes[i]);
+ if (ret) {
+ dev_err(&led->client->dev, "failed to sysfs file %s\n",
+ bd2802_addr_attributes[i]->attr.name);
+ goto failed_remove_files;
+ }
+ }
+
+ if (bd2802_is_all_off(led))
+ bd2802_reset_cancel(led);
+
+ led->adf_on = 1;
+
+ return;
+
+failed_remove_files:
+ for (i--; i >= 0; i--)
+ device_remove_file(&led->client->dev,
+ bd2802_addr_attributes[i]);
+}
+
+static void bd2802_disable_adv_conf(struct bd2802_led *led)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
+ device_remove_file(&led->client->dev,
+ bd2802_addr_attributes[i]);
+
+ if (bd2802_is_all_off(led))
+ gpio_set_value(led->pdata->reset_gpio, 0);
+
+ led->adf_on = 0;
+}
+
+static ssize_t bd2802_show_adv_conf(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ ssize_t ret;
+
+ down_read(&led->rwsem);
+ if (led->adf_on)
+ ret = sprintf(buf, "on\n");
+ else
+ ret = sprintf(buf, "off\n");
+ up_read(&led->rwsem);
+
+ return ret;
+}
+
+static ssize_t bd2802_store_adv_conf(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
+
+ if (!count)
+ return -EINVAL;
+
+ down_write(&led->rwsem);
+ if (!led->adf_on && !strncmp(buf, "on", 2))
+ bd2802_enable_adv_conf(led);
+ else if (led->adf_on && !strncmp(buf, "off", 3))
+ bd2802_disable_adv_conf(led);
+ up_write(&led->rwsem);
+
+ return count;
+}
+
+static struct device_attribute bd2802_adv_conf_attr = {
+ .attr = {
+ .name = "advanced_configuration",
+ .mode = 0644,
+ .owner = THIS_MODULE
+ },
+ .show = bd2802_show_adv_conf,
+ .store = bd2802_store_adv_conf,
+};
+
+static void bd2802_led_work(struct work_struct *work)
+{
+ struct bd2802_led *led = container_of(work, struct bd2802_led, work);
+
+ if (led->state)
+ bd2802_turn_on(led, led->led_id, led->color, led->state);
+ else
+ bd2802_turn_off(led, led->led_id, led->color);
+}
+
+#define BD2802_CONTROL_RGBS(name, id, clr) \
+static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
+ enum led_brightness value) \
+{ \
+ struct bd2802_led *led = \
+ container_of(led_cdev, struct bd2802_led, cdev_##name); \
+ led->led_id = id; \
+ led->color = clr; \
+ if (value == LED_OFF) \
+ led->state = BD2802_OFF; \
+ else \
+ led->state = BD2802_ON; \
+ schedule_work(&led->work); \
+} \
+static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
+ unsigned long *delay_on, unsigned long *delay_off) \
+{ \
+ struct bd2802_led *led = \
+ container_of(led_cdev, struct bd2802_led, cdev_##name); \
+ if (*delay_on == 0 || *delay_off == 0) \
+ return -EINVAL; \
+ led->led_id = id; \
+ led->color = clr; \
+ led->state = BD2802_BLINK; \
+ schedule_work(&led->work); \
+ return 0; \
+}
+
+BD2802_CONTROL_RGBS(led1r, LED1, RED);
+BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
+BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
+BD2802_CONTROL_RGBS(led2r, LED2, RED);
+BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
+BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
+
+static int bd2802_register_led_classdev(struct bd2802_led *led)
+{
+ int ret;
+
+ INIT_WORK(&led->work, bd2802_led_work);
+
+ led->cdev_led1r.name = "led1_R";
+ led->cdev_led1r.brightness = LED_OFF;
+ led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
+ led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
+ led->cdev_led1r.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led1r.name);
+ goto failed_unregister_led1_R;
+ }
+
+ led->cdev_led1g.name = "led1_G";
+ led->cdev_led1g.brightness = LED_OFF;
+ led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
+ led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
+ led->cdev_led1g.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led1g.name);
+ goto failed_unregister_led1_G;
+ }
+
+ led->cdev_led1b.name = "led1_B";
+ led->cdev_led1b.brightness = LED_OFF;
+ led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
+ led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
+ led->cdev_led1b.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led1b.name);
+ goto failed_unregister_led1_B;
+ }
+
+ led->cdev_led2r.name = "led2_R";
+ led->cdev_led2r.brightness = LED_OFF;
+ led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
+ led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
+ led->cdev_led2r.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led2r.name);
+ goto failed_unregister_led2_R;
+ }
+
+ led->cdev_led2g.name = "led2_G";
+ led->cdev_led2g.brightness = LED_OFF;
+ led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
+ led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
+ led->cdev_led2g.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led2g.name);
+ goto failed_unregister_led2_G;
+ }
+
+ led->cdev_led2b.name = "led2_B";
+ led->cdev_led2b.brightness = LED_OFF;
+ led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
+ led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
+ led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
+ if (ret < 0) {
+ dev_err(&led->client->dev, "couldn't register LED %s\n",
+ led->cdev_led2b.name);
+ goto failed_unregister_led2_B;
+ }
+
+ return 0;
+
+failed_unregister_led2_B:
+ led_classdev_unregister(&led->cdev_led2g);
+failed_unregister_led2_G:
+ led_classdev_unregister(&led->cdev_led2r);
+failed_unregister_led2_R:
+ led_classdev_unregister(&led->cdev_led1b);
+failed_unregister_led1_B:
+ led_classdev_unregister(&led->cdev_led1g);
+failed_unregister_led1_G:
+ led_classdev_unregister(&led->cdev_led1r);
+failed_unregister_led1_R:
+
+ return ret;
+}
+
+static void bd2802_unregister_led_classdev(struct bd2802_led *led)
+{
+ cancel_work_sync(&led->work);
+ led_classdev_unregister(&led->cdev_led1r);
+}
+
+static int __devinit bd2802_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bd2802_led *led;
+ struct bd2802_led_platform_data *pdata;
+ int ret;
+
+ led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
+ if (!led) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ led->client = client;
+ pdata = led->pdata = client->dev.platform_data;
+ i2c_set_clientdata(client, led);
+
+ /* Configure RESET GPIO (L: RESET, H: RESET cancel) */
+ gpio_request(pdata->reset_gpio, "RGB_RESETB");
+ gpio_direction_output(pdata->reset_gpio, 1);
+
+ /* Tacss = min 0.1ms */
+ udelay(100);
+
+ /* Detect BD2802GU */
+ ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ goto failed_free;
+ } else
+ dev_info(&client->dev, "return 0x%02x\n", ret);
+
+ /* To save the power, reset BD2802 after detecting */
+ gpio_set_value(led->pdata->reset_gpio, 0);
+
+ init_rwsem(&led->rwsem);
+
+ ret = device_create_file(&client->dev, &bd2802_adv_conf_attr);
+ if (ret) {
+ dev_err(&client->dev, "failed to create sysfs file %s\n",
+ bd2802_adv_conf_attr.attr.name);
+ goto failed_free;
+ }
+
+ ret = bd2802_register_led_classdev(led);
+ if (ret < 0)
+ goto failed_unregister_dev_file;
+
+ return 0;
+
+failed_unregister_dev_file:
+ device_remove_file(&client->dev, &bd2802_adv_conf_attr);
+failed_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(led);
+
+ return ret;
+}
+
+static int __exit bd2802_remove(struct i2c_client *client)
+{
+ struct bd2802_led *led = i2c_get_clientdata(client);
+
+ bd2802_unregister_led_classdev(led);
+ gpio_set_value(led->pdata->reset_gpio, 0);
+ if (led->adf_on)
+ bd2802_disable_adv_conf(led);
+ device_remove_file(&client->dev, &bd2802_adv_conf_attr);
+ i2c_set_clientdata(client, NULL);
+ kfree(led);
+
+ return 0;
+}
+
+static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct bd2802_led *led = i2c_get_clientdata(client);
+
+ gpio_set_value(led->pdata->reset_gpio, 0);
+
+ return 0;
+}
+
+static int bd2802_resume(struct i2c_client *client)
+{
+ struct bd2802_led *led = i2c_get_clientdata(client);
+
+ if (!bd2802_is_all_off(led) || led->adf_on) {
+ gpio_set_value(led->pdata->reset_gpio, 1);
+ udelay(100);
+ bd2802_restore_state(led);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id bd2802_id[] = {
+ { "BD2802", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bd2802_id);
+
+static struct i2c_driver bd2802_i2c_driver = {
+ .driver = {
+ .name = "BD2802",
+ },
+ .probe = bd2802_probe,
+ .remove = __exit_p(bd2802_remove),
+ .suspend = bd2802_suspend,
+ .resume = bd2802_resume,
+ .id_table = bd2802_id,
+};
+
+static int __init bd2802_init(void)
+{
+ return i2c_add_driver(&bd2802_i2c_driver);
+}
+module_init(bd2802_init);
+
+static void __exit bd2802_exit(void)
+{
+ i2c_del_driver(&bd2802_i2c_driver);
+}
+module_exit(bd2802_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("BD2802 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c
new file mode 100644
index 00000000000..098d9aae725
--- /dev/null
+++ b/drivers/leds/leds-dac124s085.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for the DAC124S085 SPI DAC
+ */
+
+#include <linux/gfp.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+struct dac124s085_led {
+ struct led_classdev ldev;
+ struct spi_device *spi;
+ int id;
+ int brightness;
+ char name[sizeof("dac124s085-3")];
+
+ struct mutex mutex;
+ struct work_struct work;
+ spinlock_t lock;
+};
+
+struct dac124s085 {
+ struct dac124s085_led leds[4];
+};
+
+#define REG_WRITE (0 << 12)
+#define REG_WRITE_UPDATE (1 << 12)
+#define ALL_WRITE_UPDATE (2 << 12)
+#define POWER_DOWN_OUTPUT (3 << 12)
+
+static void dac124s085_led_work(struct work_struct *work)
+{
+ struct dac124s085_led *led = container_of(work, struct dac124s085_led,
+ work);
+ u16 word;
+
+ mutex_lock(&led->mutex);
+ word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
+ (led->brightness & 0xfff));
+ spi_write(led->spi, (const u8 *)&word, sizeof(word));
+ mutex_unlock(&led->mutex);
+}
+
+static void dac124s085_set_brightness(struct led_classdev *ldev,
+ enum led_brightness brightness)
+{
+ struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
+ ldev);
+
+ spin_lock(&led->lock);
+ led->brightness = brightness;
+ schedule_work(&led->work);
+ spin_unlock(&led->lock);
+}
+
+static int dac124s085_probe(struct spi_device *spi)
+{
+ struct dac124s085 *dac;
+ struct dac124s085_led *led;
+ int i, ret;
+
+ dac = kzalloc(sizeof(*dac), GFP_KERNEL);
+ if (!dac)
+ return -ENOMEM;
+
+ spi->bits_per_word = 16;
+
+ for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+ led = dac->leds + i;
+ led->id = i;
+ led->brightness = LED_OFF;
+ led->spi = spi;
+ snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
+ spin_lock_init(&led->lock);
+ INIT_WORK(&led->work, dac124s085_led_work);
+ mutex_init(&led->mutex);
+ led->ldev.name = led->name;
+ led->ldev.brightness = LED_OFF;
+ led->ldev.max_brightness = 0xfff;
+ led->ldev.brightness_set = dac124s085_set_brightness;
+ ret = led_classdev_register(&spi->dev, &led->ldev);
+ if (ret < 0)
+ goto eledcr;
+ }
+
+ spi_set_drvdata(spi, dac);
+
+ return 0;
+
+eledcr:
+ while (i--)
+ led_classdev_unregister(&dac->leds[i].ldev);
+
+ spi_set_drvdata(spi, NULL);
+ kfree(dac);
+ return ret;
+}
+
+static int dac124s085_remove(struct spi_device *spi)
+{
+ struct dac124s085 *dac = spi_get_drvdata(spi);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+ led_classdev_unregister(&dac->leds[i].ldev);
+ cancel_work_sync(&dac->leds[i].work);
+ }
+
+ spi_set_drvdata(spi, NULL);
+ kfree(dac);
+
+ return 0;
+}
+
+static struct spi_driver dac124s085_driver = {
+ .probe = dac124s085_probe,
+ .remove = dac124s085_remove,
+ .driver = {
+ .name = "dac124s085",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init dac124s085_leds_init(void)
+{
+ return spi_register_driver(&dac124s085_driver);
+}
+
+static void __exit dac124s085_leds_exit(void)
+{
+ spi_unregister_driver(&dac124s085_driver);
+}
+
+module_init(dac124s085_leds_init);
+module_exit(dac124s085_leds_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_DESCRIPTION("DAC124S085 LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 2e3df08b649..102ef4a14c5 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2007 8D Technologies inc.
* Raphael Assenat <raph@8d.com>
+ * Copyright (C) 2008 Freescale Semiconductor, Inc.
*
* 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
@@ -71,11 +72,67 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
}
+static int __devinit create_gpio_led(const struct gpio_led *template,
+ struct gpio_led_data *led_dat, struct device *parent,
+ int (*blink_set)(unsigned, unsigned long *, unsigned long *))
+{
+ int ret;
+
+ /* skip leds that aren't available */
+ if (!gpio_is_valid(template->gpio)) {
+ printk(KERN_INFO "Skipping unavilable LED gpio %d (%s)\n",
+ template->gpio, template->name);
+ return;
+ }
+
+ ret = gpio_request(template->gpio, template->name);
+ if (ret < 0)
+ return ret;
+
+ led_dat->cdev.name = template->name;
+ led_dat->cdev.default_trigger = template->default_trigger;
+ led_dat->gpio = template->gpio;
+ led_dat->can_sleep = gpio_cansleep(template->gpio);
+ led_dat->active_low = template->active_low;
+ if (blink_set) {
+ led_dat->platform_gpio_blink_set = blink_set;
+ led_dat->cdev.blink_set = gpio_blink_set;
+ }
+ led_dat->cdev.brightness_set = gpio_led_set;
+ led_dat->cdev.brightness = LED_OFF;
+ if (!template->retain_state_suspended)
+ led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = gpio_direction_output(led_dat->gpio, led_dat->active_low);
+ if (ret < 0)
+ goto err;
+
+ INIT_WORK(&led_dat->work, gpio_led_work);
+
+ ret = led_classdev_register(parent, &led_dat->cdev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ gpio_free(led_dat->gpio);
+ return ret;
+}
+
+static void delete_gpio_led(struct gpio_led_data *led)
+{
+ if (!gpio_is_valid(led->gpio))
+ return;
+ led_classdev_unregister(&led->cdev);
+ cancel_work_sync(&led->work);
+ gpio_free(led->gpio);
+}
+
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
static int gpio_led_probe(struct platform_device *pdev)
{
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
- struct gpio_led *cur_led;
- struct gpio_led_data *leds_data, *led_dat;
+ struct gpio_led_data *leds_data;
int i, ret = 0;
if (!pdata)
@@ -87,35 +144,10 @@ static int gpio_led_probe(struct platform_device *pdev)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
- cur_led = &pdata->leds[i];
- led_dat = &leds_data[i];
-
- ret = gpio_request(cur_led->gpio, cur_led->name);
+ ret = create_gpio_led(&pdata->leds[i], &leds_data[i],
+ &pdev->dev, pdata->gpio_blink_set);
if (ret < 0)
goto err;
-
- led_dat->cdev.name = cur_led->name;
- led_dat->cdev.default_trigger = cur_led->default_trigger;
- led_dat->gpio = cur_led->gpio;
- led_dat->can_sleep = gpio_cansleep(cur_led->gpio);
- led_dat->active_low = cur_led->active_low;
- if (pdata->gpio_blink_set) {
- led_dat->platform_gpio_blink_set = pdata->gpio_blink_set;
- led_dat->cdev.blink_set = gpio_blink_set;
- }
- led_dat->cdev.brightness_set = gpio_led_set;
- led_dat->cdev.brightness = LED_OFF;
- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
-
- gpio_direction_output(led_dat->gpio, led_dat->active_low);
-
- INIT_WORK(&led_dat->work, gpio_led_work);
-
- ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
- if (ret < 0) {
- gpio_free(led_dat->gpio);
- goto err;
- }
}
platform_set_drvdata(pdev, leds_data);
@@ -123,13 +155,8 @@ static int gpio_led_probe(struct platform_device *pdev)
return 0;
err:
- if (i > 0) {
- for (i = i - 1; i >= 0; i--) {
- led_classdev_unregister(&leds_data[i].cdev);
- cancel_work_sync(&leds_data[i].work);
- gpio_free(leds_data[i].gpio);
- }
- }
+ for (i = i - 1; i >= 0; i--)
+ delete_gpio_led(&leds_data[i]);
kfree(leds_data);
@@ -144,11 +171,8 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
leds_data = platform_get_drvdata(pdev);
- for (i = 0; i < pdata->num_leds; i++) {
- led_classdev_unregister(&leds_data[i].cdev);
- cancel_work_sync(&leds_data[i].work);
- gpio_free(leds_data[i].gpio);
- }
+ for (i = 0; i < pdata->num_leds; i++)
+ delete_gpio_led(&leds_data[i]);
kfree(leds_data);
@@ -164,20 +188,133 @@ static struct platform_driver gpio_led_driver = {
},
};
+MODULE_ALIAS("platform:leds-gpio");
+#endif /* CONFIG_LEDS_GPIO_PLATFORM */
+
+/* Code to create from OpenFirmware platform devices */
+#ifdef CONFIG_LEDS_GPIO_OF
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+struct gpio_led_of_platform_data {
+ int num_leds;
+ struct gpio_led_data led_data[];
+};
+
+static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct device_node *np = ofdev->node, *child;
+ struct gpio_led led;
+ struct gpio_led_of_platform_data *pdata;
+ int count = 0, ret;
+
+ /* count LEDs defined by this device, so we know how much to allocate */
+ for_each_child_of_node(np, child)
+ count++;
+ if (!count)
+ return 0; /* or ENODEV? */
+
+ pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count,
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ memset(&led, 0, sizeof(led));
+ for_each_child_of_node(np, child) {
+ enum of_gpio_flags flags;
+
+ led.gpio = of_get_gpio_flags(child, 0, &flags);
+ led.active_low = flags & OF_GPIO_ACTIVE_LOW;
+ led.name = of_get_property(child, "label", NULL) ? : child->name;
+ led.default_trigger =
+ of_get_property(child, "linux,default-trigger", NULL);
+
+ ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++],
+ &ofdev->dev, NULL);
+ if (ret < 0) {
+ of_node_put(child);
+ goto err;
+ }
+ }
+
+ dev_set_drvdata(&ofdev->dev, pdata);
+
+ return 0;
+
+err:
+ for (count = pdata->num_leds - 2; count >= 0; count--)
+ delete_gpio_led(&pdata->led_data[count]);
+
+ kfree(pdata);
+
+ return ret;
+}
+
+static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
+{
+ struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
+ int i;
+
+ for (i = 0; i < pdata->num_leds; i++)
+ delete_gpio_led(&pdata->led_data[i]);
+
+ kfree(pdata);
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id of_gpio_leds_match[] = {
+ { .compatible = "gpio-leds", },
+ {},
+};
+
+static struct of_platform_driver of_gpio_leds_driver = {
+ .driver = {
+ .name = "of_gpio_leds",
+ .owner = THIS_MODULE,
+ },
+ .match_table = of_gpio_leds_match,
+ .probe = of_gpio_leds_probe,
+ .remove = __devexit_p(of_gpio_leds_remove),
+};
+#endif
+
static int __init gpio_led_init(void)
{
- return platform_driver_register(&gpio_led_driver);
+ int ret;
+
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
+ ret = platform_driver_register(&gpio_led_driver);
+ if (ret)
+ return ret;
+#endif
+#ifdef CONFIG_LEDS_GPIO_OF
+ ret = of_register_platform_driver(&of_gpio_leds_driver);
+#endif
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
+ if (ret)
+ platform_driver_unregister(&gpio_led_driver);
+#endif
+
+ return ret;
}
static void __exit gpio_led_exit(void)
{
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
platform_driver_unregister(&gpio_led_driver);
+#endif
+#ifdef CONFIG_LEDS_GPIO_OF
+ of_unregister_platform_driver(&of_gpio_leds_driver);
+#endif
}
module_init(gpio_led_init);
module_exit(gpio_led_exit);
-MODULE_AUTHOR("Raphael Assenat <raph@8d.com>");
+MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:leds-gpio");
diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c
index 11b77a70bbc..1aa46a390a0 100644
--- a/drivers/leds/leds-h1940.c
+++ b/drivers/leds/leds-h1940.c
@@ -104,7 +104,7 @@ static struct led_classdev h1940_blueled = {
.default_trigger = "h1940-bluetooth",
};
-static int __init h1940leds_probe(struct platform_device *pdev)
+static int __devinit h1940leds_probe(struct platform_device *pdev)
{
int ret;
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index bd3b431c971..3937244fdca 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -169,7 +169,7 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,
{
struct pca9532_data *data = input_get_drvdata(dev);
- if (type != EV_SND && (code != SND_BELL || code != SND_TONE))
+ if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE)))
return -1;
/* XXX: allow different kind of beeps with psc/pwm modifications */
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
new file mode 100644
index 00000000000..cdfdc8714e1
--- /dev/null
+++ b/drivers/leds/leds-pwm.c
@@ -0,0 +1,153 @@
+/*
+ * linux/drivers/leds-pwm.c
+ *
+ * simple PWM based LED control
+ *
+ * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
+ *
+ * based on leds-gpio.c by Raphael Assenat <raph@8d.com>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/leds_pwm.h>
+
+struct led_pwm_data {
+ struct led_classdev cdev;
+ struct pwm_device *pwm;
+ unsigned int active_low;
+ unsigned int period;
+ unsigned int max_brightness;
+};
+
+static void led_pwm_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_pwm_data *led_dat =
+ container_of(led_cdev, struct led_pwm_data, cdev);
+ unsigned int max = led_dat->max_brightness;
+ unsigned int period = led_dat->period;
+
+ if (brightness == 0) {
+ pwm_config(led_dat->pwm, 0, period);
+ pwm_disable(led_dat->pwm);
+ } else {
+ pwm_config(led_dat->pwm, brightness * period / max, period);
+ pwm_enable(led_dat->pwm);
+ }
+}
+
+static int led_pwm_probe(struct platform_device *pdev)
+{
+ struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm *cur_led;
+ struct led_pwm_data *leds_data, *led_dat;
+ int i, ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
+ GFP_KERNEL);
+ if (!leds_data)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ cur_led = &pdata->leds[i];
+ led_dat = &leds_data[i];
+
+ led_dat->pwm = pwm_request(cur_led->pwm_id,
+ cur_led->name);
+ if (IS_ERR(led_dat->pwm)) {
+ dev_err(&pdev->dev, "unable to request PWM %d\n",
+ cur_led->pwm_id);
+ goto err;
+ }
+
+ led_dat->cdev.name = cur_led->name;
+ led_dat->cdev.default_trigger = cur_led->default_trigger;
+ led_dat->active_low = cur_led->active_low;
+ led_dat->max_brightness = cur_led->max_brightness;
+ led_dat->period = cur_led->pwm_period_ns;
+ led_dat->cdev.brightness_set = led_pwm_set;
+ led_dat->cdev.brightness = LED_OFF;
+ led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
+ if (ret < 0) {
+ pwm_free(led_dat->pwm);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, leds_data);
+
+ return 0;
+
+err:
+ if (i > 0) {
+ for (i = i - 1; i >= 0; i--) {
+ led_classdev_unregister(&leds_data[i].cdev);
+ pwm_free(leds_data[i].pwm);
+ }
+ }
+
+ kfree(leds_data);
+
+ return ret;
+}
+
+static int __devexit led_pwm_remove(struct platform_device *pdev)
+{
+ int i;
+ struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm_data *leds_data;
+
+ leds_data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ led_classdev_unregister(&leds_data[i].cdev);
+ pwm_free(leds_data[i].pwm);
+ }
+
+ kfree(leds_data);
+
+ return 0;
+}
+
+static struct platform_driver led_pwm_driver = {
+ .probe = led_pwm_probe,
+ .remove = __devexit_p(led_pwm_remove),
+ .driver = {
+ .name = "leds_pwm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init led_pwm_init(void)
+{
+ return platform_driver_register(&led_pwm_driver);
+}
+
+static void __exit led_pwm_exit(void)
+{
+ platform_driver_unregister(&led_pwm_driver);
+}
+
+module_init(led_pwm_init);
+module_exit(led_pwm_exit);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("PWM LED driver for PXA");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-pwm");
diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c
new file mode 100644
index 00000000000..c3525f37f73
--- /dev/null
+++ b/drivers/leds/leds-rb532.c
@@ -0,0 +1,77 @@
+/*
+ * LEDs driver for the "User LED" on Routerboard532
+ *
+ * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
+ *
+ * Based on leds-cobalt-qube.c by Florian Fainelly and
+ * rb-diag.c (my own standalone driver for both LED and
+ * button of Routerboard532).
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-rc32434/gpio.h>
+#include <asm/mach-rc32434/rb.h>
+
+static void rb532_led_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ if (brightness)
+ set_latch_u5(LO_ULED, 0);
+
+ else
+ set_latch_u5(0, LO_ULED);
+}
+
+static enum led_brightness rb532_led_get(struct led_classdev *cdev)
+{
+ return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF;
+}
+
+static struct led_classdev rb532_uled = {
+ .name = "uled",
+ .brightness_set = rb532_led_set,
+ .brightness_get = rb532_led_get,
+ .default_trigger = "nand-disk",
+};
+
+static int __devinit rb532_led_probe(struct platform_device *pdev)
+{
+ return led_classdev_register(&pdev->dev, &rb532_uled);
+}
+
+static int __devexit rb532_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&rb532_uled);
+ return 0;
+}
+
+static struct platform_driver rb532_led_driver = {
+ .probe = rb532_led_probe,
+ .remove = __devexit_p(rb532_led_remove),
+ .driver = {
+ .name = "rb532-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rb532_led_init(void)
+{
+ return platform_driver_register(&rb532_led_driver);
+}
+
+static void __exit rb532_led_exit(void)
+{
+ platform_driver_unregister(&rb532_led_driver);
+}
+
+module_init(rb532_led_init);
+module_exit(rb532_led_exit);
+
+MODULE_ALIAS("platform:rb532-led");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("User LED support for Routerboard532");
+MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index 4d81131542a..aa2e7ae0cda 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -102,14 +102,11 @@ static int s3c24xx_led_probe(struct platform_device *dev)
ret = led_classdev_register(&dev->dev, &led->cdev);
if (ret < 0) {
dev_err(&dev->dev, "led_classdev_register failed\n");
- goto exit_err1;
+ kfree(led);
+ return ret;
}
return 0;
-
- exit_err1:
- kfree(led);
- return ret;
}
static struct platform_driver s3c24xx_led_driver = {
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index 5edbf52c4fa..2dd8ecbfdc3 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -20,8 +20,8 @@
static inline void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
- if (value > LED_FULL)
- value = LED_FULL;
+ 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);
diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/ledtrig-default-on.c
index 92995e40cfa..a4ef54b9d50 100644
--- a/drivers/leds/ledtrig-default-on.c
+++ b/drivers/leds/ledtrig-default-on.c
@@ -19,7 +19,7 @@
static void defon_trig_activate(struct led_classdev *led_cdev)
{
- led_set_brightness(led_cdev, LED_FULL);
+ led_set_brightness(led_cdev, led_cdev->max_brightness);
}
static struct led_trigger defon_led_trigger = {
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c
new file mode 100644
index 00000000000..a247ae63374
--- /dev/null
+++ b/drivers/leds/ledtrig-gpio.c
@@ -0,0 +1,239 @@
+/*
+ * ledtrig-gio.c - LED Trigger Based on GPIO events
+ *
+ * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct gpio_trig_data {
+ struct led_classdev *led;
+ struct work_struct work;
+
+ unsigned desired_brightness; /* desired brightness when led is on */
+ unsigned inverted; /* true when gpio is inverted */
+ unsigned gpio; /* gpio that triggers the leds */
+};
+
+static irqreturn_t gpio_trig_irq(int irq, void *_led)
+{
+ struct led_classdev *led = _led;
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ /* just schedule_work since gpio_get_value can sleep */
+ schedule_work(&gpio_data->work);
+
+ return IRQ_HANDLED;
+};
+
+static void gpio_trig_work(struct work_struct *work)
+{
+ struct gpio_trig_data *gpio_data = container_of(work,
+ struct gpio_trig_data, work);
+ int tmp;
+
+ if (!gpio_data->gpio)
+ return;
+
+ tmp = gpio_get_value(gpio_data->gpio);
+ if (gpio_data->inverted)
+ tmp = !tmp;
+
+ if (tmp) {
+ if (gpio_data->desired_brightness)
+ led_set_brightness(gpio_data->led,
+ gpio_data->desired_brightness);
+ else
+ led_set_brightness(gpio_data->led, LED_FULL);
+ } else {
+ led_set_brightness(gpio_data->led, LED_OFF);
+ }
+}
+
+static ssize_t gpio_trig_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ return sprintf(buf, "%u\n", gpio_data->desired_brightness);
+}
+
+static ssize_t gpio_trig_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+ unsigned desired_brightness;
+ int ret;
+
+ ret = sscanf(buf, "%u", &desired_brightness);
+ if (ret < 1 || desired_brightness > 255) {
+ dev_err(dev, "invalid value\n");
+ return -EINVAL;
+ }
+
+ gpio_data->desired_brightness = desired_brightness;
+
+ return n;
+}
+static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
+ gpio_trig_brightness_store);
+
+static ssize_t gpio_trig_inverted_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no");
+}
+
+static ssize_t gpio_trig_inverted_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+ unsigned inverted;
+ int ret;
+
+ ret = sscanf(buf, "%u", &inverted);
+ if (ret < 1) {
+ dev_err(dev, "invalid value\n");
+ return -EINVAL;
+ }
+
+ gpio_data->inverted = !!inverted;
+
+ return n;
+}
+static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
+ gpio_trig_inverted_store);
+
+static ssize_t gpio_trig_gpio_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ return sprintf(buf, "%u\n", gpio_data->gpio);
+}
+
+static ssize_t gpio_trig_gpio_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+ unsigned gpio;
+ int ret;
+
+ ret = sscanf(buf, "%u", &gpio);
+ if (ret < 1) {
+ dev_err(dev, "couldn't read gpio number\n");
+ flush_work(&gpio_data->work);
+ return -EINVAL;
+ }
+
+ if (!gpio) {
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+ return n;
+ }
+
+ if (gpio_data->gpio > 0 && gpio_data->gpio != gpio)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+
+ gpio_data->gpio = gpio;
+ ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
+ IRQF_SHARED | IRQF_TRIGGER_RISING
+ | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
+ if (ret)
+ dev_err(dev, "request_irq failed with error %d\n", ret);
+
+ return ret ? ret : n;
+}
+static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
+
+static void gpio_trig_activate(struct led_classdev *led)
+{
+ struct gpio_trig_data *gpio_data;
+ int ret;
+
+ gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
+ if (!gpio_data)
+ return;
+
+ ret = device_create_file(led->dev, &dev_attr_gpio);
+ if (ret)
+ goto err_gpio;
+
+ ret = device_create_file(led->dev, &dev_attr_inverted);
+ if (ret)
+ goto err_inverted;
+
+ ret = device_create_file(led->dev, &dev_attr_desired_brightness);
+ if (ret)
+ goto err_brightness;
+
+ gpio_data->led = led;
+ led->trigger_data = gpio_data;
+ INIT_WORK(&gpio_data->work, gpio_trig_work);
+
+ return;
+
+err_brightness:
+ device_remove_file(led->dev, &dev_attr_inverted);
+
+err_inverted:
+ device_remove_file(led->dev, &dev_attr_gpio);
+
+err_gpio:
+ kfree(gpio_data);
+}
+
+static void gpio_trig_deactivate(struct led_classdev *led)
+{
+ struct gpio_trig_data *gpio_data = led->trigger_data;
+
+ if (gpio_data) {
+ device_remove_file(led->dev, &dev_attr_gpio);
+ device_remove_file(led->dev, &dev_attr_inverted);
+ device_remove_file(led->dev, &dev_attr_desired_brightness);
+ flush_work(&gpio_data->work);
+ free_irq(gpio_to_irq(gpio_data->gpio),led);
+ kfree(gpio_data);
+ }
+}
+
+static struct led_trigger gpio_led_trigger = {
+ .name = "gpio",
+ .activate = gpio_trig_activate,
+ .deactivate = gpio_trig_deactivate,
+};
+
+static int __init gpio_trig_init(void)
+{
+ return led_trigger_register(&gpio_led_trigger);
+}
+module_init(gpio_trig_init);
+
+static void __exit gpio_trig_exit(void)
+{
+ led_trigger_unregister(&gpio_led_trigger);
+}
+module_exit(gpio_trig_exit);
+
+MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
+MODULE_DESCRIPTION("GPIO LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c
index 4bf8cec8b8c..c1c1ea6f817 100644
--- a/drivers/leds/ledtrig-heartbeat.c
+++ b/drivers/leds/ledtrig-heartbeat.c
@@ -47,7 +47,7 @@ static void led_heartbeat_function(unsigned long data)
msecs_to_jiffies(heartbeat_data->period);
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
- brightness = LED_FULL;
+ brightness = led_cdev->max_brightness;
break;
case 1:
delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
@@ -56,7 +56,7 @@ static void led_heartbeat_function(unsigned long data)
case 2:
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
- brightness = LED_FULL;
+ brightness = led_cdev->max_brightness;
break;
default:
delay = heartbeat_data->period - heartbeat_data->period / 4 -
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c
index 883a577b1b9..ec099fcbcb0 100644
--- a/drivers/leds/ledtrig-ide-disk.c
+++ b/drivers/leds/ledtrig-ide-disk.c
@@ -37,7 +37,8 @@ static void ledtrig_ide_timerfunc(unsigned long data)
{
if (ide_lastactivity != ide_activity) {
ide_lastactivity = ide_activity;
- led_trigger_event(ledtrig_ide, LED_FULL);
+ /* INT_MAX will set each LED to its maximum brightness */
+ led_trigger_event(ledtrig_ide, INT_MAX);
mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
} else {
led_trigger_event(ledtrig_ide, LED_OFF);
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
index 3d6531396dd..3b83406de75 100644
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/ledtrig-timer.c
@@ -166,7 +166,7 @@ static void timer_trig_activate(struct led_classdev *led_cdev)
timer_data->brightness_on = led_get_brightness(led_cdev);
if (timer_data->brightness_on == LED_OFF)
- timer_data->brightness_on = LED_FULL;
+ timer_data->brightness_on = led_cdev->max_brightness;
led_cdev->trigger_data = timer_data;
init_timer(&timer_data->timer);
diff --git a/include/linux/leds-bd2802.h b/include/linux/leds-bd2802.h
new file mode 100644
index 00000000000..42f854a1a19
--- /dev/null
+++ b/include/linux/leds-bd2802.h
@@ -0,0 +1,26 @@
+/*
+ * leds-bd2802.h - RGB LED Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.com>
+ *
+ * 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.
+ *
+ * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
+ *
+ */
+#ifndef _LEDS_BD2802_H_
+#define _LEDS_BD2802_H_
+
+struct bd2802_led_platform_data{
+ int reset_gpio;
+ u8 rgb_time;
+};
+
+#define RGB_TIME(slopedown, slopeup, waveform) \
+ ((slopedown) << 6 | (slopeup) << 4 | (waveform))
+
+#endif /* _LEDS_BD2802_H_ */
+
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 24489da701e..376fe07732e 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -30,6 +30,7 @@ enum led_brightness {
struct led_classdev {
const char *name;
int brightness;
+ int max_brightness;
int flags;
/* Lower 16 bits reflect status */
@@ -140,7 +141,8 @@ struct gpio_led {
const char *name;
const char *default_trigger;
unsigned gpio;
- u8 active_low;
+ u8 active_low : 1;
+ u8 retain_state_suspended : 1;
};
struct gpio_led_platform_data {
diff --git a/include/linux/leds_pwm.h b/include/linux/leds_pwm.h
new file mode 100644
index 00000000000..33a07116748
--- /dev/null
+++ b/include/linux/leds_pwm.h
@@ -0,0 +1,21 @@
+/*
+ * PWM LED driver data - see drivers/leds/leds-pwm.c
+ */
+#ifndef __LINUX_LEDS_PWM_H
+#define __LINUX_LEDS_PWM_H
+
+struct led_pwm {
+ const char *name;
+ const char *default_trigger;
+ unsigned pwm_id;
+ u8 active_low;
+ unsigned max_brightness;
+ unsigned pwm_period_ns;
+};
+
+struct led_pwm_platform_data {
+ int num_leds;
+ struct led_pwm *leds;
+};
+
+#endif