浏览代码

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
Linus Torvalds 16 年之前
父节点
当前提交
3cd69271f8

+ 37 - 9
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>;
+	};
+}

+ 72 - 3
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

+ 7 - 0
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

+ 20 - 1
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);

+ 9 - 1
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);
 

+ 765 - 0
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");

+ 150 - 0
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");

+ 181 - 44
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");

+ 1 - 1
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;
 

+ 1 - 1
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 */

+ 153 - 0
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");

+ 77 - 0
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>");

+ 2 - 5
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 = {

+ 2 - 2
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);

+ 1 - 1
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 = {

+ 239 - 0
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");

+ 2 - 2
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 -

+ 2 - 1
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);

+ 1 - 1
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);

+ 26 - 0
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_ */
+

+ 3 - 1
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 {

+ 21 - 0
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