summaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig29
-rw-r--r--drivers/leds/Makefile3
-rw-r--r--drivers/leds/led-class.c70
-rw-r--r--drivers/leds/led-core.c70
-rw-r--r--drivers/leds/leds-88pm860x.c23
-rw-r--r--drivers/leds/leds-ams-delta.c126
-rw-r--r--drivers/leds/leds-gpio.c3
-rw-r--r--drivers/leds/leds-lm3530.c135
-rw-r--r--drivers/leds/leds-lp5521.c147
-rw-r--r--drivers/leds/leds-lp5523.c6
-rw-r--r--drivers/leds/leds-net5501.c97
-rw-r--r--drivers/leds/leds-pca9633.c193
-rw-r--r--drivers/leds/leds-tca6507.c9
13 files changed, 502 insertions, 409 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 9ca28fced2b..ff4b8cfda58 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -17,7 +17,7 @@ menuconfig NEW_LEDS
if NEW_LEDS
config LEDS_CLASS
- bool "LED Class Support"
+ tristate "LED Class Support"
help
This option enables the led sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
@@ -69,18 +69,11 @@ config LEDS_MIKROTIK_RB532
config LEDS_S3C24XX
tristate "LED Support for Samsung S3C24XX GPIO LEDs"
depends on LEDS_CLASS
- depends on ARCH_S3C2410
+ depends on ARCH_S3C24XX
help
This option enables support for LEDs connected to GPIO lines
on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440.
-config LEDS_AMS_DELTA
- tristate "LED Support for the Amstrad Delta (E3)"
- depends on LEDS_CLASS
- depends on MACH_AMS_DELTA
- help
- This option enables support for the LEDs on Amstrad Delta (E3).
-
config LEDS_NET48XX
tristate "LED Support for Soekris net48xx series Error LED"
depends on LEDS_CLASS
@@ -89,16 +82,6 @@ config LEDS_NET48XX
This option enables support for the Soekris net4801 and net4826 error
LED.
-config LEDS_NET5501
- tristate "LED Support for Soekris net5501 series Error LED"
- depends on LEDS_TRIGGERS
- depends on X86 && GPIO_CS5535
- select LEDS_TRIGGER_DEFAULT_ON
- default n
- help
- Add support for the Soekris net5501 board (detection, error led
- and GPIO).
-
config LEDS_FSG
tristate "LED Support for the Freecom FSG-3"
depends on LEDS_CLASS
@@ -244,6 +227,14 @@ config LEDS_PCA955X
LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
+config LEDS_PCA9633
+ tristate "LED support for PCA9633 I2C chip"
+ depends on LEDS_CLASS
+ depends on I2C
+ help
+ This option enables support for LEDs connected to the PCA9633
+ LED driver chip accessed via the I2C bus.
+
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 1fc6875a8b2..890481cb09f 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -12,9 +12,7 @@ obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.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
-obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
@@ -31,6 +29,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_OT200) += leds-ot200.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
+obj-$(CONFIG_LEDS_PCA9633) += leds-pca9633.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 0c8739c448b..5bff8439dc6 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -110,50 +110,6 @@ static void led_timer_function(unsigned long data)
mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
}
-static void led_stop_software_blink(struct led_classdev *led_cdev)
-{
- /* deactivate previous settings */
- del_timer_sync(&led_cdev->blink_timer);
- led_cdev->blink_delay_on = 0;
- led_cdev->blink_delay_off = 0;
-}
-
-static void led_set_software_blink(struct led_classdev *led_cdev,
- unsigned long delay_on,
- unsigned long delay_off)
-{
- int current_brightness;
-
- current_brightness = led_get_brightness(led_cdev);
- if (current_brightness)
- led_cdev->blink_brightness = current_brightness;
- if (!led_cdev->blink_brightness)
- led_cdev->blink_brightness = led_cdev->max_brightness;
-
- if (led_get_trigger_data(led_cdev) &&
- delay_on == led_cdev->blink_delay_on &&
- delay_off == led_cdev->blink_delay_off)
- return;
-
- led_stop_software_blink(led_cdev);
-
- led_cdev->blink_delay_on = delay_on;
- led_cdev->blink_delay_off = delay_off;
-
- /* never on - don't blink */
- if (!delay_on)
- return;
-
- /* never off - just set to brightness */
- if (!delay_off) {
- led_set_brightness(led_cdev, led_cdev->blink_brightness);
- return;
- }
-
- mod_timer(&led_cdev->blink_timer, jiffies + 1);
-}
-
-
/**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
@@ -262,32 +218,6 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);
-void led_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on,
- unsigned long *delay_off)
-{
- del_timer_sync(&led_cdev->blink_timer);
-
- if (led_cdev->blink_set &&
- !led_cdev->blink_set(led_cdev, delay_on, delay_off))
- return;
-
- /* blink with 1 Hz as default if nothing specified */
- if (!*delay_on && !*delay_off)
- *delay_on = *delay_off = 500;
-
- led_set_software_blink(led_cdev, *delay_on, *delay_off);
-}
-EXPORT_SYMBOL(led_blink_set);
-
-void led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- led_stop_software_blink(led_cdev);
- led_cdev->brightness_set(led_cdev, brightness);
-}
-EXPORT_SYMBOL(led_brightness_set);
-
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 016d19f5486..d6860043f6f 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -23,3 +23,73 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);
+
+static void led_stop_software_blink(struct led_classdev *led_cdev)
+{
+ /* deactivate previous settings */
+ del_timer_sync(&led_cdev->blink_timer);
+ led_cdev->blink_delay_on = 0;
+ led_cdev->blink_delay_off = 0;
+}
+
+static void led_set_software_blink(struct led_classdev *led_cdev,
+ unsigned long delay_on,
+ unsigned long delay_off)
+{
+ int current_brightness;
+
+ current_brightness = led_get_brightness(led_cdev);
+ if (current_brightness)
+ led_cdev->blink_brightness = current_brightness;
+ if (!led_cdev->blink_brightness)
+ led_cdev->blink_brightness = led_cdev->max_brightness;
+
+ if (led_get_trigger_data(led_cdev) &&
+ delay_on == led_cdev->blink_delay_on &&
+ delay_off == led_cdev->blink_delay_off)
+ return;
+
+ led_stop_software_blink(led_cdev);
+
+ led_cdev->blink_delay_on = delay_on;
+ led_cdev->blink_delay_off = delay_off;
+
+ /* never on - don't blink */
+ if (!delay_on)
+ return;
+
+ /* never off - just set to brightness */
+ if (!delay_off) {
+ led_set_brightness(led_cdev, led_cdev->blink_brightness);
+ return;
+ }
+
+ mod_timer(&led_cdev->blink_timer, jiffies + 1);
+}
+
+
+void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ del_timer_sync(&led_cdev->blink_timer);
+
+ if (led_cdev->blink_set &&
+ !led_cdev->blink_set(led_cdev, delay_on, delay_off))
+ return;
+
+ /* blink with 1 Hz as default if nothing specified */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 500;
+
+ led_set_software_blink(led_cdev, *delay_on, *delay_off);
+}
+EXPORT_SYMBOL(led_blink_set);
+
+void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ led_stop_software_blink(led_cdev);
+ led_cdev->brightness_set(led_cdev, brightness);
+}
+EXPORT_SYMBOL(led_brightness_set);
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 4ca00624bd1..5b61aaf7ac0 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -114,6 +114,27 @@ static inline int __blink_ctl_mask(int port)
return ret;
}
+static int led_power_set(struct pm860x_chip *chip, int port, int on)
+{
+ int ret = -EINVAL;
+
+ switch (port) {
+ case PM8606_LED1_RED:
+ case PM8606_LED1_GREEN:
+ case PM8606_LED1_BLUE:
+ ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) :
+ pm8606_osc_disable(chip, RGB1_ENABLE);
+ break;
+ case PM8606_LED2_RED:
+ case PM8606_LED2_GREEN:
+ case PM8606_LED2_BLUE:
+ ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) :
+ pm8606_osc_disable(chip, RGB2_ENABLE);
+ break;
+ }
+ return ret;
+}
+
static void pm860x_led_work(struct work_struct *work)
{
@@ -126,6 +147,7 @@ static void pm860x_led_work(struct work_struct *work)
chip = led->chip;
mutex_lock(&led->lock);
if ((led->current_brightness == 0) && led->brightness) {
+ led_power_set(chip, led->port, 1);
if (led->iset) {
pm860x_set_bits(led->i2c, __led_off(led->port),
LED_CURRENT_MASK, led->iset);
@@ -149,6 +171,7 @@ static void pm860x_led_work(struct work_struct *work)
LED_CURRENT_MASK, 0);
mask = __blink_ctl_mask(led->port);
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
+ led_power_set(chip, led->port, 0);
}
}
led->current_brightness = led->brightness;
diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c
deleted file mode 100644
index 07428357c83..00000000000
--- a/drivers/leds/leds-ams-delta.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * LEDs driver for Amstrad Delta (E3)
- *
- * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
- *
- * 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/leds.h>
-#include <plat/board-ams-delta.h>
-
-/*
- * Our context
- */
-struct ams_delta_led {
- struct led_classdev cdev;
- u8 bitmask;
-};
-
-static void ams_delta_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct ams_delta_led *led_dev =
- container_of(led_cdev, struct ams_delta_led, cdev);
-
- if (value)
- ams_delta_latch1_write(led_dev->bitmask, led_dev->bitmask);
- else
- ams_delta_latch1_write(led_dev->bitmask, 0);
-}
-
-static struct ams_delta_led ams_delta_leds[] = {
- {
- .cdev = {
- .name = "ams-delta::camera",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_CAMERA,
- },
- {
- .cdev = {
- .name = "ams-delta::advert",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_ADVERT,
- },
- {
- .cdev = {
- .name = "ams-delta::email",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_EMAIL,
- },
- {
- .cdev = {
- .name = "ams-delta::handsfree",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_HANDSFREE,
- },
- {
- .cdev = {
- .name = "ams-delta::voicemail",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_VOICEMAIL,
- },
- {
- .cdev = {
- .name = "ams-delta::voice",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_VOICE,
- },
-};
-
-static int ams_delta_led_probe(struct platform_device *pdev)
-{
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
- ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = led_classdev_register(&pdev->dev,
- &ams_delta_leds[i].cdev);
- if (ret < 0)
- goto fail;
- }
-
- return 0;
-fail:
- while (--i >= 0)
- led_classdev_unregister(&ams_delta_leds[i].cdev);
- return ret;
-}
-
-static int ams_delta_led_remove(struct platform_device *pdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
- led_classdev_unregister(&ams_delta_leds[i].cdev);
-
- return 0;
-}
-
-static struct platform_driver ams_delta_led_driver = {
- .probe = ams_delta_led_probe,
- .remove = ams_delta_led_remove,
- .driver = {
- .name = "ams-delta-led",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(ams_delta_led_driver);
-
-MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
-MODULE_DESCRIPTION("Amstrad Delta LED driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ams-delta-led");
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 7df74cb97e7..f4c470a3bc8 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
@@ -20,8 +21,6 @@
#include <linux/workqueue.h>
#include <linux/module.h>
-#include <asm/gpio.h>
-
struct gpio_led_data {
struct led_classdev cdev;
unsigned gpio;
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index e59c166a0ce..968fd5fef4f 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -26,7 +26,6 @@
#define LM3530_GEN_CONFIG 0x10
#define LM3530_ALS_CONFIG 0x20
#define LM3530_BRT_RAMP_RATE 0x30
-#define LM3530_ALS_ZONE_REG 0x40
#define LM3530_ALS_IMP_SELECT 0x41
#define LM3530_BRT_CTRL_REG 0xA0
#define LM3530_ALS_ZB0_REG 0x60
@@ -38,7 +37,7 @@
#define LM3530_ALS_Z2T_REG 0x72
#define LM3530_ALS_Z3T_REG 0x73
#define LM3530_ALS_Z4T_REG 0x74
-#define LM3530_REG_MAX 15
+#define LM3530_REG_MAX 14
/* General Control Register */
#define LM3530_EN_I2C_SHIFT (0)
@@ -80,6 +79,9 @@
#define LM3530_DEF_ZT_3 (0x33)
#define LM3530_DEF_ZT_4 (0x19)
+/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */
+#define MAX_BRIGHTNESS (127)
+
struct lm3530_mode_map {
const char *mode;
enum lm3530_mode mode_val;
@@ -115,7 +117,6 @@ static const u8 lm3530_reg[LM3530_REG_MAX] = {
LM3530_GEN_CONFIG,
LM3530_ALS_CONFIG,
LM3530_BRT_RAMP_RATE,
- LM3530_ALS_ZONE_REG,
LM3530_ALS_IMP_SELECT,
LM3530_BRT_CTRL_REG,
LM3530_ALS_ZB0_REG,
@@ -152,27 +153,35 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
u8 reg_val[LM3530_REG_MAX];
u8 zones[LM3530_ALS_ZB_MAX];
u32 als_vmin, als_vmax, als_vstep;
- struct lm3530_platform_data *pltfm = drvdata->pdata;
+ struct lm3530_platform_data *pdata = drvdata->pdata;
struct i2c_client *client = drvdata->client;
+ struct lm3530_pwm_data *pwm = &pdata->pwm_data;
- gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
- ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT);
+ gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
+ ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);
- if (drvdata->mode == LM3530_BL_MODE_MANUAL ||
- drvdata->mode == LM3530_BL_MODE_ALS)
- gen_config |= (LM3530_ENABLE_I2C);
+ switch (drvdata->mode) {
+ case LM3530_BL_MODE_MANUAL:
+ case LM3530_BL_MODE_ALS:
+ gen_config |= LM3530_ENABLE_I2C;
+ break;
+ case LM3530_BL_MODE_PWM:
+ gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
+ (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT);
+ break;
+ }
if (drvdata->mode == LM3530_BL_MODE_ALS) {
- if (pltfm->als_vmax == 0) {
- pltfm->als_vmin = 0;
- pltfm->als_vmax = LM3530_ALS_WINDOW_mV;
+ if (pdata->als_vmax == 0) {
+ pdata->als_vmin = 0;
+ pdata->als_vmax = LM3530_ALS_WINDOW_mV;
}
- als_vmin = pltfm->als_vmin;
- als_vmax = pltfm->als_vmax;
+ als_vmin = pdata->als_vmin;
+ als_vmax = pdata->als_vmax;
if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
- pltfm->als_vmax = als_vmax =
+ pdata->als_vmax = als_vmax =
als_vmin + LM3530_ALS_WINDOW_mV;
/* n zone boundary makes n+1 zones */
@@ -184,44 +193,41 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
/ 1000;
als_config =
- (pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
+ (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
(LM3530_ENABLE_ALS) |
- (pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT);
+ (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
als_imp_sel =
- (pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
- (pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
+ (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
+ (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
}
- if (drvdata->mode == LM3530_BL_MODE_PWM)
- gen_config |= (LM3530_ENABLE_PWM) |
- (pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) |
- (LM3530_ENABLE_PWM_SIMPLE);
-
- brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
- (pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
+ brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
+ (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
if (drvdata->brightness)
brightness = drvdata->brightness;
else
- brightness = drvdata->brightness = pltfm->brt_val;
+ brightness = drvdata->brightness = pdata->brt_val;
+
+ if (brightness > drvdata->led_dev.max_brightness)
+ brightness = drvdata->led_dev.max_brightness;
reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */
reg_val[1] = als_config; /* LM3530_ALS_CONFIG */
reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */
- reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
- reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
- reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
- reg_val[6] = zones[0]; /* LM3530_ALS_ZB0_REG */
- reg_val[7] = zones[1]; /* LM3530_ALS_ZB1_REG */
- reg_val[8] = zones[2]; /* LM3530_ALS_ZB2_REG */
- reg_val[9] = zones[3]; /* LM3530_ALS_ZB3_REG */
- reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
- reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
- reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
- reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
- reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
+ reg_val[3] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
+ reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */
+ reg_val[5] = zones[0]; /* LM3530_ALS_ZB0_REG */
+ reg_val[6] = zones[1]; /* LM3530_ALS_ZB1_REG */
+ reg_val[7] = zones[2]; /* LM3530_ALS_ZB2_REG */
+ reg_val[8] = zones[3]; /* LM3530_ALS_ZB3_REG */
+ reg_val[9] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
+ reg_val[10] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
+ reg_val[11] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
+ reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
+ reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
if (!drvdata->enable) {
ret = regulator_enable(drvdata->regulator);
@@ -234,6 +240,15 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
}
for (i = 0; i < LM3530_REG_MAX; i++) {
+ /* do not update brightness register when pwm mode */
+ if (lm3530_reg[i] == LM3530_BRT_CTRL_REG &&
+ drvdata->mode == LM3530_BL_MODE_PWM) {
+ if (pwm->pwm_set_intensity)
+ pwm->pwm_set_intensity(reg_val[i],
+ drvdata->led_dev.max_brightness);
+ continue;
+ }
+
ret = i2c_smbus_write_byte_data(client,
lm3530_reg[i], reg_val[i]);
if (ret)
@@ -249,6 +264,9 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
int err;
struct lm3530_data *drvdata =
container_of(led_cdev, struct lm3530_data, led_dev);
+ struct lm3530_platform_data *pdata = drvdata->pdata;
+ struct lm3530_pwm_data *pwm = &pdata->pwm_data;
+ u8 max_brightness = led_cdev->max_brightness;
switch (drvdata->mode) {
case LM3530_BL_MODE_MANUAL:
@@ -264,12 +282,12 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
/* set the brightness in brightness control register*/
err = i2c_smbus_write_byte_data(drvdata->client,
- LM3530_BRT_CTRL_REG, brt_val / 2);
+ LM3530_BRT_CTRL_REG, brt_val);
if (err)
dev_err(&drvdata->client->dev,
"Unable to set brightness: %d\n", err);
else
- drvdata->brightness = brt_val / 2;
+ drvdata->brightness = brt_val;
if (brt_val == 0) {
err = regulator_disable(drvdata->regulator);
@@ -282,6 +300,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
case LM3530_BL_MODE_ALS:
break;
case LM3530_BL_MODE_PWM:
+ if (pwm->pwm_set_intensity)
+ pwm->pwm_set_intensity(brt_val, max_brightness);
break;
default:
break;
@@ -291,11 +311,11 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
static ssize_t lm3530_mode_get(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = container_of(
- dev->parent, struct i2c_client, dev);
- struct lm3530_data *drvdata = i2c_get_clientdata(client);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lm3530_data *drvdata;
int i, len = 0;
+ drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
for (i = 0; i < ARRAY_SIZE(mode_map); i++)
if (drvdata->mode == mode_map[i].mode_val)
len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
@@ -310,26 +330,26 @@ static ssize_t lm3530_mode_get(struct device *dev,
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
- int err;
- struct i2c_client *client = container_of(
- dev->parent, struct i2c_client, dev);
- struct lm3530_data *drvdata = i2c_get_clientdata(client);
- int mode;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lm3530_data *drvdata;
+ struct lm3530_pwm_data *pwm;
+ u8 max_brightness;
+ int mode, err;
+ drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
+ pwm = &drvdata->pdata->pwm_data;
+ max_brightness = led_cdev->max_brightness;
mode = lm3530_get_mode_from_str(buf);
if (mode < 0) {
dev_err(dev, "Invalid mode\n");
return -EINVAL;
}
- if (mode == LM3530_BL_MODE_MANUAL)
- drvdata->mode = LM3530_BL_MODE_MANUAL;
- else if (mode == LM3530_BL_MODE_ALS)
- drvdata->mode = LM3530_BL_MODE_ALS;
- else if (mode == LM3530_BL_MODE_PWM) {
- dev_err(dev, "PWM mode not supported\n");
- return -EINVAL;
- }
+ drvdata->mode = mode;
+
+ /* set pwm to low if unnecessary */
+ if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity)
+ pwm->pwm_set_intensity(0, max_brightness);
err = lm3530_init_registers(drvdata);
if (err) {
@@ -380,6 +400,7 @@ static int __devinit lm3530_probe(struct i2c_client *client,
drvdata->enable = false;
drvdata->led_dev.name = LM3530_LED_DEV;
drvdata->led_dev.brightness_set = lm3530_brightness_set;
+ drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
i2c_set_clientdata(client, drvdata);
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index d62a7982a5e..410a723b869 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -81,18 +81,10 @@
#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */
#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
#define LP5521_EXEC_RUN 0x2A
-
-/* Bits in CONFIG register */
-#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
-#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
-#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
-#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
-#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
-#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
-#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
-#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
-#define LP5521_CLK_INT 1 /* Internal clock */
-#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
+#define LP5521_ENABLE_DEFAULT \
+ (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
+#define LP5521_ENABLE_RUN_PROGRAM \
+ (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
/* Status */
#define LP5521_EXT_CLK_USED 0x08
@@ -100,6 +92,9 @@
/* default R channel current register value */
#define LP5521_REG_R_CURR_DEFAULT 0xAF
+/* Pattern Mode */
+#define PATTERN_OFF 0
+
struct lp5521_engine {
int id;
u8 mode;
@@ -241,15 +236,16 @@ static int lp5521_configure(struct i2c_client *client)
{
struct lp5521_chip *chip = i2c_get_clientdata(client);
int ret;
+ u8 cfg;
lp5521_init_engine(chip);
/* Set all PWMs to direct control mode */
- ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+ ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
- /* Enable auto-powersave, set charge pump to auto, red to battery */
- ret |= lp5521_write(client, LP5521_REG_CONFIG,
- LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+ cfg = chip->pdata->update_config ?
+ : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+ ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg);
/* Initialize all channels PWM to zero -> leds off */
ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
@@ -258,8 +254,7 @@ static int lp5521_configure(struct i2c_client *client)
/* Set engines are set to run state when OP_MODE enables engines */
ret |= lp5521_write(client, LP5521_REG_ENABLE,
- LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM |
- LP5521_EXEC_RUN);
+ LP5521_ENABLE_RUN_PROGRAM);
/* enable takes 500us. 1 - 2 ms leaves some margin */
usleep_range(1000, 2000);
@@ -310,8 +305,7 @@ static int lp5521_detect(struct i2c_client *client)
int ret;
u8 buf;
- ret = lp5521_write(client, LP5521_REG_ENABLE,
- LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM);
+ ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
if (ret)
return ret;
/* enable takes 500us. 1 - 2 ms leaves some margin */
@@ -319,7 +313,7 @@ static int lp5521_detect(struct i2c_client *client)
ret = lp5521_read(client, LP5521_REG_ENABLE, &buf);
if (ret)
return ret;
- if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM))
+ if (buf != LP5521_ENABLE_DEFAULT)
return -ENODEV;
return 0;
@@ -504,7 +498,7 @@ static ssize_t store_current(struct device *dev,
ssize_t ret;
unsigned long curr;
- if (strict_strtoul(buf, 0, &curr))
+ if (kstrtoul(buf, 0, &curr))
return -EINVAL;
if (curr > led->max_current)
@@ -536,6 +530,97 @@ static ssize_t lp5521_selftest(struct device *dev,
return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
}
+static void lp5521_clear_program_memory(struct i2c_client *cl)
+{
+ int i;
+ u8 rgb_mem[] = {
+ LP5521_REG_R_PROG_MEM,
+ LP5521_REG_G_PROG_MEM,
+ LP5521_REG_B_PROG_MEM,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) {
+ lp5521_write(cl, rgb_mem[i], 0);
+ lp5521_write(cl, rgb_mem[i] + 1, 0);
+ }
+}
+
+static void lp5521_write_program_memory(struct i2c_client *cl,
+ u8 base, u8 *rgb, int size)
+{
+ int i;
+
+ if (!rgb || size <= 0)
+ return;
+
+ for (i = 0; i < size; i++)
+ lp5521_write(cl, base + i, *(rgb + i));
+
+ lp5521_write(cl, base + i, 0);
+ lp5521_write(cl, base + i + 1, 0);
+}
+
+static inline struct lp5521_led_pattern *lp5521_get_pattern
+ (struct lp5521_chip *chip, u8 offset)
+{
+ struct lp5521_led_pattern *ptn;
+ ptn = chip->pdata->patterns + (offset - 1);
+ return ptn;
+}
+
+static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip)
+{
+ struct lp5521_led_pattern *ptn;
+ struct i2c_client *cl = chip->client;
+ int num_patterns = chip->pdata->num_patterns;
+
+ if (mode > num_patterns || !(chip->pdata->patterns))
+ return;
+
+ if (mode == PATTERN_OFF) {
+ lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
+ usleep_range(1000, 2000);
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
+ } else {
+ ptn = lp5521_get_pattern(chip, mode);
+ if (!ptn)
+ return;
+
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD);
+ usleep_range(1000, 2000);
+
+ lp5521_clear_program_memory(cl);
+
+ lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM,
+ ptn->r, ptn->size_r);
+ lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM,
+ ptn->g, ptn->size_g);
+ lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM,
+ ptn->b, ptn->size_b);
+
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN);
+ usleep_range(1000, 2000);
+ lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
+ }
+}
+
+static ssize_t store_led_pattern(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 16, &val);
+ if (ret)
+ return ret;
+
+ lp5521_run_led_pattern(val, chip);
+
+ return len;
+}
+
/* led class device attributes */
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current);
static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL);
@@ -561,6 +646,7 @@ static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load);
static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load);
static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load);
static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
+static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern);
static struct attribute *lp5521_attributes[] = {
&dev_attr_engine1_mode.attr,
@@ -570,6 +656,7 @@ static struct attribute *lp5521_attributes[] = {
&dev_attr_engine1_load.attr,
&dev_attr_engine2_load.attr,
&dev_attr_engine3_load.attr,
+ &dev_attr_led_pattern.attr,
NULL
};
@@ -620,10 +707,15 @@ static int __devinit lp5521_init_led(struct lp5521_led *led,
return -EINVAL;
}
- snprintf(name, sizeof(name), "%s:channel%d",
- pdata->label ?: client->name, chan);
led->cdev.brightness_set = lp5521_set_brightness;
- led->cdev.name = name;
+ if (pdata->led_config[chan].name) {
+ led->cdev.name = pdata->led_config[chan].name;
+ } else {
+ snprintf(name, sizeof(name), "%s:channel%d",
+ pdata->label ?: client->name, chan);
+ led->cdev.name = name;
+ }
+
res = led_classdev_register(dev, &led->cdev);
if (res < 0) {
dev_err(dev, "couldn't register led on channel %d\n", chan);
@@ -692,9 +784,9 @@ static int __devinit lp5521_probe(struct i2c_client *client,
* otherwise further access to the R G B channels in the
* LP5521_REG_ENABLE register will not have any effect - strange!
*/
- lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
+ ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
if (buf != LP5521_REG_R_CURR_DEFAULT) {
- dev_err(&client->dev, "error in reseting chip\n");
+ dev_err(&client->dev, "error in resetting chip\n");
goto fail2;
}
usleep_range(10000, 20000);
@@ -767,6 +859,7 @@ static int __devexit lp5521_remove(struct i2c_client *client)
struct lp5521_chip *chip = i2c_get_clientdata(client);
int i;
+ lp5521_run_led_pattern(PATTERN_OFF, chip);
lp5521_unregister_sysfs(client);
for (i = 0; i < chip->num_leds; i++) {
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 73e791ae725..857a3e15f2d 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -152,7 +152,7 @@ static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led)
static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode);
static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode);
-static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern);
+static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern);
static void lp5523_led_brightness_work(struct work_struct *work);
@@ -196,7 +196,7 @@ static int lp5523_configure(struct i2c_client *client)
u8 status;
/* one pattern per engine setting led mux start and stop addresses */
- u8 pattern[][LP5523_PROGRAM_LENGTH] = {
+ static const u8 pattern[][LP5523_PROGRAM_LENGTH] = {
{ 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},
{ 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},
{ 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},
@@ -301,7 +301,7 @@ static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux)
return ret;
}
-static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern)
+static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern)
{
struct lp5523_chip *chip = engine_to_lp5523(engine);
struct i2c_client *client = chip->client;
diff --git a/drivers/leds/leds-net5501.c b/drivers/leds/leds-net5501.c
deleted file mode 100644
index 0555d4709a7..00000000000
--- a/drivers/leds/leds-net5501.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Soekris board support code
- *
- * Copyright (C) 2008-2009 Tower Technologies
- * Written by Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/string.h>
-#include <linux/leds.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-
-#include <asm/geode.h>
-
-static const struct gpio_led net5501_leds[] = {
- {
- .name = "error",
- .gpio = 6,
- .default_trigger = "default-on",
- },
-};
-
-static struct gpio_led_platform_data net5501_leds_data = {
- .num_leds = ARRAY_SIZE(net5501_leds),
- .leds = net5501_leds,
-};
-
-static struct platform_device net5501_leds_dev = {
- .name = "leds-gpio",
- .id = -1,
- .dev.platform_data = &net5501_leds_data,
-};
-
-static void __init init_net5501(void)
-{
- platform_device_register(&net5501_leds_dev);
-}
-
-struct soekris_board {
- u16 offset;
- char *sig;
- u8 len;
- void (*init)(void);
-};
-
-static struct soekris_board __initdata boards[] = {
- { 0xb7b, "net5501", 7, init_net5501 }, /* net5501 v1.33/1.33c */
- { 0xb1f, "net5501", 7, init_net5501 }, /* net5501 v1.32i */
-};
-
-static int __init soekris_init(void)
-{
- int i;
- unsigned char *rombase, *bios;
-
- if (!is_geode())
- return 0;
-
- rombase = ioremap(0xffff0000, 0xffff);
- if (!rombase) {
- printk(KERN_INFO "Soekris net5501 LED driver failed to get rombase");
- return 0;
- }
-
- bios = rombase + 0x20; /* null terminated */
-
- if (strncmp(bios, "comBIOS", 7))
- goto unmap;
-
- for (i = 0; i < ARRAY_SIZE(boards); i++) {
- unsigned char *model = rombase + boards[i].offset;
-
- if (strncmp(model, boards[i].sig, boards[i].len) == 0) {
- printk(KERN_INFO "Soekris %s: %s\n", model, bios);
-
- if (boards[i].init)
- boards[i].init();
- break;
- }
- }
-
-unmap:
- iounmap(rombase);
- return 0;
-}
-
-arch_initcall(soekris_init);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c
new file mode 100644
index 00000000000..d8926fd031a
--- /dev/null
+++ b/drivers/leds/leds-pca9633.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2011 bct electronic GmbH
+ *
+ * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
+ *
+ * Based on leds-pca955x.c
+ *
+ * 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 PCA9633 I2C LED driver (7-bit slave address 0x62)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+/* LED select registers determine the source that drives LED outputs */
+#define PCA9633_LED_OFF 0x0 /* LED driver off */
+#define PCA9633_LED_ON 0x1 /* LED driver on */
+#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */
+#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
+
+#define PCA9633_MODE1 0x00
+#define PCA9633_MODE2 0x01
+#define PCA9633_PWM_BASE 0x02
+#define PCA9633_LEDOUT 0x08
+
+static const struct i2c_device_id pca9633_id[] = {
+ { "pca9633", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca9633_id);
+
+struct pca9633_led {
+ struct i2c_client *client;
+ struct work_struct work;
+ enum led_brightness brightness;
+ struct led_classdev led_cdev;
+ int led_num; /* 0 .. 3 potentially */
+ char name[32];
+};
+
+static void pca9633_led_work(struct work_struct *work)
+{
+ struct pca9633_led *pca9633 = container_of(work,
+ struct pca9633_led, work);
+ u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
+ int shift = 2 * pca9633->led_num;
+ u8 mask = 0x3 << shift;
+
+ switch (pca9633->brightness) {
+ case LED_FULL:
+ i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
+ (ledout & ~mask) | (PCA9633_LED_ON << shift));
+ break;
+ case LED_OFF:
+ i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
+ ledout & ~mask);
+ break;
+ default:
+ i2c_smbus_write_byte_data(pca9633->client,
+ PCA9633_PWM_BASE + pca9633->led_num,
+ pca9633->brightness);
+ i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
+ (ledout & ~mask) | (PCA9633_LED_PWM << shift));
+ break;
+ }
+}
+
+static void pca9633_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pca9633_led *pca9633;
+
+ pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
+
+ pca9633->brightness = value;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca9633->work);
+}
+
+static int __devinit pca9633_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca9633_led *pca9633;
+ struct led_platform_data *pdata;
+ int i, err;
+
+ pdata = client->dev.platform_data;
+
+ if (pdata) {
+ if (pdata->num_leds <= 0 || pdata->num_leds > 4) {
+ dev_err(&client->dev, "board info must claim at most 4 LEDs");
+ return -EINVAL;
+ }
+ }
+
+ pca9633 = kcalloc(4, sizeof(*pca9633), GFP_KERNEL);
+ if (!pca9633)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pca9633);
+
+ for (i = 0; i < 4; i++) {
+ pca9633[i].client = client;
+ pca9633[i].led_num = i;
+
+ /* Platform data can specify LED names and default triggers */
+ if (pdata && i < pdata->num_leds) {
+ if (pdata->leds[i].name)
+ snprintf(pca9633[i].name,
+ sizeof(pca9633[i].name), "pca9633:%s",
+ pdata->leds[i].name);
+ if (pdata->leds[i].default_trigger)
+ pca9633[i].led_cdev.default_trigger =
+ pdata->leds[i].default_trigger;
+ } else {
+ snprintf(pca9633[i].name, sizeof(pca9633[i].name),
+ "pca9633:%d", i);
+ }
+
+ pca9633[i].led_cdev.name = pca9633[i].name;
+ pca9633[i].led_cdev.brightness_set = pca9633_led_set;
+
+ INIT_WORK(&pca9633[i].work, pca9633_led_work);
+
+ err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
+ if (err < 0)
+ goto exit;
+ }
+
+ /* Disable LED all-call address and set normal mode */
+ i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);
+
+ /* Turn off LEDs */
+ i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);
+
+ return 0;
+
+exit:
+ while (i--) {
+ led_classdev_unregister(&pca9633[i].led_cdev);
+ cancel_work_sync(&pca9633[i].work);
+ }
+
+ kfree(pca9633);
+
+ return err;
+}
+
+static int __devexit pca9633_remove(struct i2c_client *client)
+{
+ struct pca9633_led *pca9633 = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ led_classdev_unregister(&pca9633[i].led_cdev);
+ cancel_work_sync(&pca9633[i].work);
+ }
+
+ kfree(pca9633);
+
+ return 0;
+}
+
+static struct i2c_driver pca9633_driver = {
+ .driver = {
+ .name = "leds-pca9633",
+ .owner = THIS_MODULE,
+ },
+ .probe = pca9633_probe,
+ .remove = __devexit_p(pca9633_remove),
+ .id_table = pca9633_id,
+};
+
+module_i2c_driver(pca9633_driver);
+
+MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
+MODULE_DESCRIPTION("PCA9633 LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 133f89fb707..6c1c14f3163 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -687,10 +687,9 @@ static int __devinit tca6507_probe(struct i2c_client *client,
NUM_LEDS);
return -ENODEV;
}
- err = -ENOMEM;
tca = kzalloc(sizeof(*tca), GFP_KERNEL);
if (!tca)
- goto exit;
+ return -ENOMEM;
tca->client = client;
INIT_WORK(&tca->work, tca6507_work);
@@ -724,11 +723,10 @@ static int __devinit tca6507_probe(struct i2c_client *client,
return 0;
exit:
- while (i--)
+ while (i--) {
if (tca->leds[i].led_cdev.name)
led_classdev_unregister(&tca->leds[i].led_cdev);
- cancel_work_sync(&tca->work);
- i2c_set_clientdata(client, NULL);
+ }
kfree(tca);
return err;
}
@@ -745,7 +743,6 @@ static int __devexit tca6507_remove(struct i2c_client *client)
}
tca6507_remove_gpio(tca);
cancel_work_sync(&tca->work);
- i2c_set_clientdata(client, NULL);
kfree(tca);
return 0;