aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds2013-09-12 11:35:33 -0700
committerLinus Torvalds2013-09-12 11:35:33 -0700
commit5223161dc0f5e44fbf3d5e42d23697b6796cdf4e (patch)
tree10837ec58d96e751469d78d347f76c0d49238d72 /drivers
parente5d0c874391a500be7643d3eef9fb07171eee129 (diff)
parent61abeba5222895d6900b13115f5d8eba7988d7d6 (diff)
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull led updates from Bryan Wu: "Sorry for the late pull request, since I'm just back from vacation. LED subsystem updates for 3.12: - pca9633 driver DT supporting and pca9634 chip supporting - restore legacy device attributes for lp5521 - other fixing and updates" * 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (28 commits) leds: wm831x-status: Request a REG resource leds: trigger: ledtrig-backlight: Fix invalid memory access in fb_event notification callback leds-pca963x: Fix device tree parsing leds-pca9633: Rename to leds-pca963x leds-pca9633: Add mutex to the ledout register leds-pca9633: Unique naming of the LEDs leds-pca9633: Add support for PCA9634 leds: lp5562: use LP55xx common macros for device attributes Documentation: leds-lp5521,lp5523: update device attribute information leds: lp5523: remove unnecessary writing commands leds: lp5523: restore legacy device attributes leds: lp5523: LED MUX configuration on initializing leds: lp5523: make separate API for loading engine leds: lp5521: remove unnecessary writing commands leds: lp5521: restore legacy device attributes leds: lp55xx: add common macros for device attributes leds: lp55xx: add common data structure for program Documentation: leds: Fix a typo leds: ss4200: Fix incorrect placement of __initdata leds: clevo-mail: Fix incorrect placement of __initdata ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/leds/Kconfig34
-rw-r--r--drivers/leds/Makefile3
-rw-r--r--drivers/leds/leds-88pm860x.c2
-rw-r--r--drivers/leds/leds-adp5520.c6
-rw-r--r--drivers/leds/leds-asic3.c4
-rw-r--r--drivers/leds/leds-atmel-pwm.c4
-rw-r--r--drivers/leds/leds-bd2802.c2
-rw-r--r--drivers/leds/leds-clevo-mail.c2
-rw-r--r--drivers/leds/leds-da903x.c2
-rw-r--r--drivers/leds/leds-da9052.c4
-rw-r--r--drivers/leds/leds-gpio.c2
-rw-r--r--drivers/leds/leds-lm3530.c2
-rw-r--r--drivers/leds/leds-lm3533.c2
-rw-r--r--drivers/leds/leds-lm355x.c2
-rw-r--r--drivers/leds/leds-lm3642.c2
-rw-r--r--drivers/leds/leds-lp3944.c7
-rw-r--r--drivers/leds/leds-lp5521.c118
-rw-r--r--drivers/leds/leds-lp5523.c325
-rw-r--r--drivers/leds/leds-lp5562.c8
-rw-r--r--drivers/leds/leds-lp55xx-common.c3
-rw-r--r--drivers/leds/leds-lp55xx-common.h66
-rw-r--r--drivers/leds/leds-lp8501.c410
-rw-r--r--drivers/leds/leds-lt3593.c4
-rw-r--r--drivers/leds/leds-netxbig.c6
-rw-r--r--drivers/leds/leds-ns2.c2
-rw-r--r--drivers/leds/leds-pca9532.c3
-rw-r--r--drivers/leds/leds-pca955x.c2
-rw-r--r--drivers/leds/leds-pca9633.c194
-rw-r--r--drivers/leds/leds-pca963x.c461
-rw-r--r--drivers/leds/leds-pwm.c2
-rw-r--r--drivers/leds/leds-regulator.c3
-rw-r--r--drivers/leds/leds-s3c24xx.c2
-rw-r--r--drivers/leds/leds-ss4200.c4
-rw-r--r--drivers/leds/leds-tca6507.c2
-rw-r--r--drivers/leds/leds-wm831x-status.c8
-rw-r--r--drivers/leds/leds-wm8350.c2
-rw-r--r--drivers/leds/trigger/ledtrig-backlight.c30
37 files changed, 1435 insertions, 300 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 074bcb3892b5..875bbe4c962e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -194,11 +194,11 @@ config LEDS_LP3944
module will be called leds-lp3944.
config LEDS_LP55XX_COMMON
- tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
- depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
+ tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
+ depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
select FW_LOADER
help
- This option supports common operations for LP5521 and LP5523/55231
+ This option supports common operations for LP5521/5523/55231/5562/8501
devices.
config LEDS_LP5521
@@ -232,6 +232,18 @@ config LEDS_LP5562
Driver provides direct control via LED class and interface for
programming the engines.
+config LEDS_LP8501
+ tristate "LED Support for TI LP8501 LED driver chip"
+ depends on LEDS_CLASS && I2C
+ select LEDS_LP55XX_COMMON
+ help
+ If you say yes here you get support for TI LP8501 LED driver.
+ It is 9 channel chip with programmable engines.
+ Driver provides direct control via LED class and interface for
+ programming the engines.
+ It is similar as LP5523, but output power selection is available.
+ And register layout and engine program schemes are different.
+
config LEDS_LP8788
tristate "LED support for the TI LP8788 PMIC"
depends on LEDS_CLASS
@@ -279,13 +291,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"
+config LEDS_PCA963X
+ tristate "LED support for PCA963x 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.
+ This option enables support for LEDs connected to the PCA963x
+ LED driver chip accessed via the I2C bus. Supported
+ devices include PCA9633 and PCA9634
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
@@ -398,10 +411,7 @@ config LEDS_MC13783
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
depends on LEDS_CLASS
- depends on MACH_NETSPACE_V2 || MACH_INETSPACE_V2 || \
- MACH_NETSPACE_MAX_V2 || MACH_D2NET_V2 || \
- MACH_NETSPACE_V2_DT || MACH_INETSPACE_V2_DT || \
- MACH_NETSPACE_MAX_V2_DT || MACH_NETSPACE_MINI_V2_DT
+ depends on ARCH_KIRKWOOD
default y
help
This option enable support for the dual-GPIO LED found on the
@@ -410,8 +420,8 @@ config LEDS_NS2
config LEDS_NETXBIG
tristate "LED support for Big Network series LEDs"
- depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
depends on LEDS_CLASS
+ depends on ARCH_KIRKWOOD
default y
help
This option enable support for LEDs found on the LaCie 2Big
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ae4b6135f665..8979b0b2c85e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
+obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
@@ -34,7 +35,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_PCA963X) += leds-pca963x.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 232b3ce902e5..5f588c0a376e 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -157,7 +157,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
static int pm860x_led_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
- struct pm860x_led_pdata *pdata = pdev->dev.platform_data;
+ struct pm860x_led_pdata *pdata = dev_get_platdata(&pdev->dev);
struct pm860x_led *data;
struct resource *res;
int ret = 0;
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
index e8072abe76e5..7e311a120b11 100644
--- a/drivers/leds/leds-adp5520.c
+++ b/drivers/leds/leds-adp5520.c
@@ -87,7 +87,7 @@ static int adp5520_led_setup(struct adp5520_led *led)
static int adp5520_led_prepare(struct platform_device *pdev)
{
- struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = pdev->dev.parent;
int ret = 0;
@@ -103,7 +103,7 @@ static int adp5520_led_prepare(struct platform_device *pdev)
static int adp5520_led_probe(struct platform_device *pdev)
{
- struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct adp5520_led *led, *led_dat;
struct led_info *cur_led;
int ret, i;
@@ -185,7 +185,7 @@ err:
static int adp5520_led_remove(struct platform_device *pdev)
{
- struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct adp5520_led *led;
int i;
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
index cf9efe421c2b..6de216a89a0c 100644
--- a/drivers/leds/leds-asic3.c
+++ b/drivers/leds/leds-asic3.c
@@ -94,7 +94,7 @@ static int blink_set(struct led_classdev *cdev,
static int asic3_led_probe(struct platform_device *pdev)
{
- struct asic3_led *led = pdev->dev.platform_data;
+ struct asic3_led *led = dev_get_platdata(&pdev->dev);
int ret;
ret = mfd_cell_enable(pdev);
@@ -127,7 +127,7 @@ out:
static int asic3_led_remove(struct platform_device *pdev)
{
- struct asic3_led *led = pdev->dev.platform_data;
+ struct asic3_led *led = dev_get_platdata(&pdev->dev);
led_classdev_unregister(led->cdev);
diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c
index 90518f84b9c0..56cec8d6a2ac 100644
--- a/drivers/leds/leds-atmel-pwm.c
+++ b/drivers/leds/leds-atmel-pwm.c
@@ -42,7 +42,7 @@ static int pwmled_probe(struct platform_device *pdev)
int i;
int status;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata || pdata->num_leds < 1)
return -ENODEV;
@@ -119,7 +119,7 @@ static int pwmled_remove(struct platform_device *pdev)
struct pwmled *leds;
unsigned i;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
leds = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++) {
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 2db04231a792..fb5a3472d614 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -684,7 +684,7 @@ static int bd2802_probe(struct i2c_client *client,
}
led->client = client;
- pdata = led->pdata = client->dev.platform_data;
+ pdata = led->pdata = dev_get_platdata(&client->dev);
i2c_set_clientdata(client, led);
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
index 6a8405df76a3..d93e2455da5c 100644
--- a/drivers/leds/leds-clevo-mail.c
+++ b/drivers/leds/leds-clevo-mail.c
@@ -40,7 +40,7 @@ static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
* detected as working, but in reality it is not) as low as
* possible.
*/
-static struct dmi_system_id __initdata clevo_mail_led_dmi_table[] = {
+static struct dmi_system_id clevo_mail_led_dmi_table[] __initdata = {
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Clevo D410J",
diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c
index c263a21db829..2a4b87f8091a 100644
--- a/drivers/leds/leds-da903x.c
+++ b/drivers/leds/leds-da903x.c
@@ -93,7 +93,7 @@ static void da903x_led_set(struct led_classdev *led_cdev,
static int da903x_led_probe(struct platform_device *pdev)
{
- struct led_info *pdata = pdev->dev.platform_data;
+ struct led_info *pdata = dev_get_platdata(&pdev->dev);
struct da903x_led *led;
int id, ret;
diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c
index efec43344e9f..865d4faf874a 100644
--- a/drivers/leds/leds-da9052.c
+++ b/drivers/leds/leds-da9052.c
@@ -112,7 +112,7 @@ static int da9052_led_probe(struct platform_device *pdev)
int i;
da9052 = dev_get_drvdata(pdev->dev.parent);
- pdata = da9052->dev->platform_data;
+ pdata = dev_get_platdata(da9052->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data\n");
goto err;
@@ -185,7 +185,7 @@ static int da9052_led_remove(struct platform_device *pdev)
int i;
da9052 = dev_get_drvdata(pdev->dev.parent);
- pdata = da9052->dev->platform_data;
+ pdata = dev_get_platdata(da9052->dev);
pled = pdata->pled;
for (i = 0; i < pled->num_leds; i++) {
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 84d74c373cae..e8b01e57348d 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -233,7 +233,7 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
static int gpio_led_probe(struct platform_device *pdev)
{
- struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct gpio_leds_priv *priv;
int i, ret = 0;
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index a036a19040fe..652368c2ea9a 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -403,7 +403,7 @@ static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
static int lm3530_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lm3530_platform_data *pdata = client->dev.platform_data;
+ struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev);
struct lm3530_data *drvdata;
int err = 0;
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
index bbf24d038a7f..027ede73b80d 100644
--- a/drivers/leds/leds-lm3533.c
+++ b/drivers/leds/leds-lm3533.c
@@ -671,7 +671,7 @@ static int lm3533_led_probe(struct platform_device *pdev)
if (!lm3533)
return -EINVAL;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index d81a8e7afd6c..591eb5e58ae3 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -423,7 +423,7 @@ static const struct regmap_config lm355x_regmap = {
static int lm355x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lm355x_platform_data *pdata = client->dev.platform_data;
+ struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev);
struct lm355x_chip_data *chip;
int err;
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index f361bbef2dec..ceb6b3cde6fe 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -316,7 +316,7 @@ static const struct regmap_config lm3642_regmap = {
static int lm3642_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lm3642_platform_data *pdata = client->dev.platform_data;
+ struct lm3642_platform_data *pdata = dev_get_platdata(&client->dev);
struct lm3642_chip_data *chip;
int err;
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c
index 0c4386e656c1..8e1abdcd4c9d 100644
--- a/drivers/leds/leds-lp3944.c
+++ b/drivers/leds/leds-lp3944.c
@@ -289,7 +289,7 @@ static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
dev_dbg(&led->client->dev, "%s: %s, %d\n",
__func__, led_cdev->name, brightness);
- led->status = brightness;
+ led->status = !!brightness;
schedule_work(&led->work);
}
@@ -377,7 +377,8 @@ exit:
static int lp3944_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data;
+ struct lp3944_platform_data *lp3944_pdata =
+ dev_get_platdata(&client->dev);
struct lp3944_data *data;
int err;
@@ -413,7 +414,7 @@ static int lp3944_probe(struct i2c_client *client,
static int lp3944_remove(struct i2c_client *client)
{
- struct lp3944_platform_data *pdata = client->dev.platform_data;
+ struct lp3944_platform_data *pdata = dev_get_platdata(&client->dev);
struct lp3944_data *data = i2c_get_clientdata(client);
int i;
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 1392feb1bcf7..05188351711d 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -220,17 +220,11 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
};
unsigned cmd;
char c[3];
- int program_size;
int nrchars;
- int offset = 0;
int ret;
- int i;
-
- /* clear program memory before updating */
- for (i = 0; i < LP5521_PROGRAM_LENGTH; i++)
- lp55xx_write(chip, addr[idx] + i, 0);
+ int offset = 0;
+ int i = 0;
- i = 0;
while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) {
/* separate sscanfs because length is working only for %s */
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
@@ -250,11 +244,19 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
if (i % 2)
goto err;
- program_size = i;
- for (i = 0; i < program_size; i++)
- lp55xx_write(chip, addr[idx] + i, pattern[i]);
+ mutex_lock(&chip->lock);
- return 0;
+ for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) {
+ ret = lp55xx_write(chip, addr[idx] + i, pattern[i]);
+ if (ret) {
+ mutex_unlock(&chip->lock);
+ return -EINVAL;
+ }
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return size;
err:
dev_err(&chip->cl->dev, "wrong pattern format\n");
@@ -365,6 +367,80 @@ static void lp5521_led_brightness_work(struct work_struct *work)
mutex_unlock(&chip->lock);
}
+static ssize_t show_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
+
+ switch (mode) {
+ case LP55XX_ENGINE_RUN:
+ return sprintf(buf, "run\n");
+ case LP55XX_ENGINE_LOAD:
+ return sprintf(buf, "load\n");
+ case LP55XX_ENGINE_DISABLED:
+ default:
+ return sprintf(buf, "disabled\n");
+ }
+}
+show_mode(1)
+show_mode(2)
+show_mode(3)
+
+static ssize_t store_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+
+ if (!strncmp(buf, "run", 3)) {
+ lp5521_run_engine(chip, true);
+ engine->mode = LP55XX_ENGINE_RUN;
+ } else if (!strncmp(buf, "load", 4)) {
+ lp5521_stop_engine(chip);
+ lp5521_load_engine(chip);
+ engine->mode = LP55XX_ENGINE_LOAD;
+ } else if (!strncmp(buf, "disabled", 8)) {
+ lp5521_stop_engine(chip);
+ engine->mode = LP55XX_ENGINE_DISABLED;
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+store_mode(1)
+store_mode(2)
+store_mode(3)
+
+static ssize_t store_engine_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+ lp5521_load_engine(chip);
+
+ mutex_unlock(&chip->lock);
+
+ return lp5521_update_program_memory(chip, buf, len);
+}
+store_load(1)
+store_load(2)
+store_load(3)
+
static ssize_t lp5521_selftest(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -381,9 +457,21 @@ static ssize_t lp5521_selftest(struct device *dev,
}
/* device attributes */
-static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
+static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
+static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
+static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
+static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
+static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
+static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
+static LP55XX_DEV_ATTR_RO(selftest, lp5521_selftest);
static struct attribute *lp5521_attributes[] = {
+ &dev_attr_engine1_mode.attr,
+ &dev_attr_engine2_mode.attr,
+ &dev_attr_engine3_mode.attr,
+ &dev_attr_engine1_load.attr,
+ &dev_attr_engine2_load.attr,
+ &dev_attr_engine3_load.attr,
&dev_attr_selftest.attr,
NULL
};
@@ -420,7 +508,7 @@ static int lp5521_probe(struct i2c_client *client,
struct lp55xx_platform_data *pdata;
struct device_node *np = client->dev.of_node;
- if (!client->dev.platform_data) {
+ if (!dev_get_platdata(&client->dev)) {
if (np) {
ret = lp55xx_of_populate_pdata(&client->dev, np);
if (ret < 0)
@@ -430,7 +518,7 @@ static int lp5521_probe(struct i2c_client *client,
return -EINVAL;
}
}
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 3979428f3100..fe3bcbb5747f 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -49,6 +49,9 @@
#define LP5523_REG_RESET 0x3D
#define LP5523_REG_LED_TEST_CTRL 0x41
#define LP5523_REG_LED_TEST_ADC 0x42
+#define LP5523_REG_CH1_PROG_START 0x4C
+#define LP5523_REG_CH2_PROG_START 0x4D
+#define LP5523_REG_CH3_PROG_START 0x4E
#define LP5523_REG_PROG_PAGE_SEL 0x4F
#define LP5523_REG_PROG_MEM 0x50
@@ -65,11 +68,15 @@
#define LP5523_RESET 0xFF
#define LP5523_ADC_SHORTCIRC_LIM 80
#define LP5523_EXT_CLK_USED 0x08
+#define LP5523_ENG_STATUS_MASK 0x07
/* Memory Page Selection */
#define LP5523_PAGE_ENG1 0
#define LP5523_PAGE_ENG2 1
#define LP5523_PAGE_ENG3 2
+#define LP5523_PAGE_MUX1 3
+#define LP5523_PAGE_MUX2 4
+#define LP5523_PAGE_MUX3 5
/* Program Memory Operations */
#define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */
@@ -94,11 +101,15 @@
#define LP5523_RUN_ENG2 0x08
#define LP5523_RUN_ENG3 0x02
+#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led)))
+
enum lp5523_chip_id {
LP5523,
LP55231,
};
+static int lp5523_init_program_engine(struct lp55xx_chip *chip);
+
static inline void lp5523_wait_opmode_done(void)
{
usleep_range(1000, 2000);
@@ -134,7 +145,11 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip)
if (ret)
return ret;
- return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
+ ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
+ if (ret)
+ return ret;
+
+ return lp5523_init_program_engine(chip);
}
static void lp5523_load_engine(struct lp55xx_chip *chip)
@@ -152,15 +167,21 @@ static void lp5523_load_engine(struct lp55xx_chip *chip)
[LP55XX_ENGINE_3] = LP5523_LOAD_ENG3,
};
+ lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]);
+
+ lp5523_wait_opmode_done();
+}
+
+static void lp5523_load_engine_and_select_page(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
u8 page_sel[] = {
[LP55XX_ENGINE_1] = LP5523_PAGE_ENG1,
[LP55XX_ENGINE_2] = LP5523_PAGE_ENG2,
[LP55XX_ENGINE_3] = LP5523_PAGE_ENG3,
};
- lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]);
-
- lp5523_wait_opmode_done();
+ lp5523_load_engine(chip);
lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]);
}
@@ -227,23 +248,75 @@ static void lp5523_run_engine(struct lp55xx_chip *chip, bool start)
lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec);
}
+static int lp5523_init_program_engine(struct lp55xx_chip *chip)
+{
+ int i;
+ int j;
+ int ret;
+ u8 status;
+ /* one pattern per engine setting LED MUX start and stop addresses */
+ 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},
+ };
+
+ /* hardcode 32 bytes of memory for each engine from program memory */
+ ret = lp55xx_write(chip, LP5523_REG_CH1_PROG_START, 0x00);
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_CH2_PROG_START, 0x10);
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_CH3_PROG_START, 0x20);
+ if (ret)
+ return ret;
+
+ /* write LED MUX address space for each engine */
+ for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
+ chip->engine_idx = i;
+ lp5523_load_engine_and_select_page(chip);
+
+ for (j = 0; j < LP5523_PROGRAM_LENGTH; j++) {
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + j,
+ pattern[i - 1][j]);
+ if (ret)
+ goto out;
+ }
+ }
+
+ lp5523_run_engine(chip, true);
+
+ /* Let the programs run for couple of ms and check the engine status */
+ usleep_range(3000, 6000);
+ lp55xx_read(chip, LP5523_REG_STATUS, &status);
+ status &= LP5523_ENG_STATUS_MASK;
+
+ if (status != LP5523_ENG_STATUS_MASK) {
+ dev_err(&chip->cl->dev,
+ "cound not configure LED engine, status = 0x%.2x\n",
+ status);
+ ret = -1;
+ }
+
+out:
+ lp5523_stop_engine(chip);
+ return ret;
+}
+
static int lp5523_update_program_memory(struct lp55xx_chip *chip,
const u8 *data, size_t size)
{
u8 pattern[LP5523_PROGRAM_LENGTH] = {0};
unsigned cmd;
char c[3];
- int update_size;
int nrchars;
- int offset = 0;
int ret;
- int i;
-
- /* clear program memory before updating */
- for (i = 0; i < LP5523_PROGRAM_LENGTH; i++)
- lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0);
+ int offset = 0;
+ int i = 0;
- i = 0;
while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) {
/* separate sscanfs because length is working only for %s */
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
@@ -263,11 +336,19 @@ static int lp5523_update_program_memory(struct lp55xx_chip *chip,
if (i % 2)
goto err;
- update_size = i;
- for (i = 0; i < update_size; i++)
- lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]);
+ mutex_lock(&chip->lock);
- return 0;
+ for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) {
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]);
+ if (ret) {
+ mutex_unlock(&chip->lock);
+ return -EINVAL;
+ }
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return size;
err:
dev_err(&chip->cl->dev, "wrong pattern format\n");
@@ -290,10 +371,196 @@ static void lp5523_firmware_loaded(struct lp55xx_chip *chip)
* 2) write firmware data into program memory
*/
- lp5523_load_engine(chip);
+ lp5523_load_engine_and_select_page(chip);
lp5523_update_program_memory(chip, fw->data, fw->size);
}
+static ssize_t show_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
+
+ switch (mode) {
+ case LP55XX_ENGINE_RUN:
+ return sprintf(buf, "run\n");
+ case LP55XX_ENGINE_LOAD:
+ return sprintf(buf, "load\n");
+ case LP55XX_ENGINE_DISABLED:
+ default:
+ return sprintf(buf, "disabled\n");
+ }
+}
+show_mode(1)
+show_mode(2)
+show_mode(3)
+
+static ssize_t store_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+
+ if (!strncmp(buf, "run", 3)) {
+ lp5523_run_engine(chip, true);
+ engine->mode = LP55XX_ENGINE_RUN;
+ } else if (!strncmp(buf, "load", 4)) {
+ lp5523_stop_engine(chip);
+ lp5523_load_engine(chip);
+ engine->mode = LP55XX_ENGINE_LOAD;
+ } else if (!strncmp(buf, "disabled", 8)) {
+ lp5523_stop_engine(chip);
+ engine->mode = LP55XX_ENGINE_DISABLED;
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+store_mode(1)
+store_mode(2)
+store_mode(3)
+
+static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len)
+{
+ u16 tmp_mux = 0;
+ int i;
+
+ len = min_t(int, len, LP5523_MAX_LEDS);
+
+ for (i = 0; i < len; i++) {
+ switch (buf[i]) {
+ case '1':
+ tmp_mux |= (1 << i);
+ break;
+ case '0':
+ break;
+ case '\n':
+ i = len;
+ break;
+ default:
+ return -1;
+ }
+ }
+ *mux = tmp_mux;
+
+ return 0;
+}
+
+static void lp5523_mux_to_array(u16 led_mux, char *array)
+{
+ int i, pos = 0;
+ for (i = 0; i < LP5523_MAX_LEDS; i++)
+ pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i));
+
+ array[pos] = '\0';
+}
+
+static ssize_t show_engine_leds(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ char mux[LP5523_MAX_LEDS + 1];
+
+ lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux);
+
+ return sprintf(buf, "%s\n", mux);
+}
+show_leds(1)
+show_leds(2)
+show_leds(3)
+
+static int lp5523_load_mux(struct lp55xx_chip *chip, u16 mux, int nr)
+{
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+ int ret;
+ u8 mux_page[] = {
+ [LP55XX_ENGINE_1] = LP5523_PAGE_MUX1,
+ [LP55XX_ENGINE_2] = LP5523_PAGE_MUX2,
+ [LP55XX_ENGINE_3] = LP5523_PAGE_MUX3,
+ };
+
+ lp5523_load_engine(chip);
+
+ ret = lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, mux_page[nr]);
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM , (u8)(mux >> 8));
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + 1, (u8)(mux));
+ if (ret)
+ return ret;
+
+ engine->led_mux = mux;
+ return 0;
+}
+
+static ssize_t store_engine_leds(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+ u16 mux = 0;
+ ssize_t ret;
+
+ if (lp5523_mux_parse(buf, &mux, len))
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+ ret = -EINVAL;
+
+ if (engine->mode != LP55XX_ENGINE_LOAD)
+ goto leave;
+
+ if (lp5523_load_mux(chip, mux, nr))
+ goto leave;
+
+ ret = len;
+leave:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+store_leds(1)
+store_leds(2)
+store_leds(3)
+
+static ssize_t store_engine_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+ lp5523_load_engine_and_select_page(chip);
+
+ mutex_unlock(&chip->lock);
+
+ return lp5523_update_program_memory(chip, buf, len);
+}
+store_load(1)
+store_load(2)
+store_load(3)
+
static ssize_t lp5523_selftest(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -393,9 +660,27 @@ static void lp5523_led_brightness_work(struct work_struct *work)
mutex_unlock(&chip->lock);
}
-static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL);
+static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
+static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
+static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
+static LP55XX_DEV_ATTR_RW(engine1_leds, show_engine1_leds, store_engine1_leds);
+static LP55XX_DEV_ATTR_RW(engine2_leds, show_engine2_leds, store_engine2_leds);
+static LP55XX_DEV_ATTR_RW(engine3_leds, show_engine3_leds, store_engine3_leds);
+static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
+static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
+static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
+static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);
static struct attribute *lp5523_attributes[] = {
+ &dev_attr_engine1_mode.attr,
+ &dev_attr_engine2_mode.attr,
+ &dev_attr_engine3_mode.attr,
+ &dev_attr_engine1_load.attr,
+ &dev_attr_engine2_load.attr,
+ &dev_attr_engine3_load.attr,
+ &dev_attr_engine1_leds.attr,
+ &dev_attr_engine2_leds.attr,
+ &dev_attr_engine3_leds.attr,
&dev_attr_selftest.attr,
NULL,
};
@@ -432,7 +717,7 @@ static int lp5523_probe(struct i2c_client *client,
struct lp55xx_platform_data *pdata;
struct device_node *np = client->dev.of_node;
- if (!client->dev.platform_data) {
+ if (!dev_get_platdata(&client->dev)) {
if (np) {
ret = lp55xx_of_populate_pdata(&client->dev, np);
if (ret < 0)
@@ -442,7 +727,7 @@ static int lp5523_probe(struct i2c_client *client,
return -EINVAL;
}
}
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index cbd856dac150..2585cfd57711 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -477,8 +477,8 @@ static ssize_t lp5562_store_engine_mux(struct device *dev,
return len;
}
-static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
-static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
+static LP55XX_DEV_ATTR_WO(led_pattern, lp5562_store_pattern);
+static LP55XX_DEV_ATTR_WO(engine_mux, lp5562_store_engine_mux);
static struct attribute *lp5562_attributes[] = {
&dev_attr_led_pattern.attr,
@@ -518,7 +518,7 @@ static int lp5562_probe(struct i2c_client *client,
struct lp55xx_platform_data *pdata;
struct device_node *np = client->dev.of_node;
- if (!client->dev.platform_data) {
+ if (!dev_get_platdata(&client->dev)) {
if (np) {
ret = lp55xx_of_populate_pdata(&client->dev, np);
if (ret < 0)
@@ -528,7 +528,7 @@ static int lp5562_probe(struct i2c_client *client,
return -EINVAL;
}
}
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index c2fecd4d391c..351825b96f16 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -593,6 +593,9 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
of_property_read_string(np, "label", &pdata->label);
of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
+ /* LP8501 specific */
+ of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
+
dev->platform_data = pdata;
return 0;
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h
index dbbf86df0f1f..cceab483edd0 100644
--- a/drivers/leds/leds-lp55xx-common.h
+++ b/drivers/leds/leds-lp55xx-common.h
@@ -20,8 +20,62 @@ enum lp55xx_engine_index {
LP55XX_ENGINE_1,
LP55XX_ENGINE_2,
LP55XX_ENGINE_3,
+ LP55XX_ENGINE_MAX = LP55XX_ENGINE_3,
};
+enum lp55xx_engine_mode {
+ LP55XX_ENGINE_DISABLED,
+ LP55XX_ENGINE_LOAD,
+ LP55XX_ENGINE_RUN,
+};
+
+#define LP55XX_DEV_ATTR_RW(name, show, store) \
+ DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show, store)
+#define LP55XX_DEV_ATTR_RO(name, show) \
+ DEVICE_ATTR(name, S_IRUGO, show, NULL)
+#define LP55XX_DEV_ATTR_WO(name, store) \
+ DEVICE_ATTR(name, S_IWUSR, NULL, store)
+
+#define show_mode(nr) \
+static ssize_t show_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_mode(dev, attr, buf, nr); \
+}
+
+#define store_mode(nr) \
+static ssize_t store_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_mode(dev, attr, buf, len, nr); \
+}
+
+#define show_leds(nr) \
+static ssize_t show_engine##nr##_leds(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_leds(dev, attr, buf, nr); \
+}
+
+#define store_leds(nr) \
+static ssize_t store_engine##nr##_leds(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_leds(dev, attr, buf, len, nr); \
+}
+
+#define store_load(nr) \
+static ssize_t store_engine##nr##_load(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_load(dev, attr, buf, len, nr); \
+}
+
struct lp55xx_led;
struct lp55xx_chip;
@@ -72,6 +126,16 @@ struct lp55xx_device_config {
};
/*
+ * struct lp55xx_engine
+ * @mode : Engine mode
+ * @led_mux : Mux bits for LED selection. Only used in LP5523
+ */
+struct lp55xx_engine {
+ enum lp55xx_engine_mode mode;
+ u16 led_mux;
+};
+
+/*
* struct lp55xx_chip
* @cl : I2C communication for access registers
* @pdata : Platform specific data
@@ -79,6 +143,7 @@ struct lp55xx_device_config {
* @num_leds : Number of registered LEDs
* @cfg : Device specific configuration data
* @engine_idx : Selected engine number
+ * @engines : Engine structure for the device attribute R/W interface
* @fw : Firmware data for running a LED pattern
*/
struct lp55xx_chip {
@@ -89,6 +154,7 @@ struct lp55xx_chip {
int num_leds;
struct lp55xx_device_config *cfg;
enum lp55xx_engine_index engine_idx;
+ struct lp55xx_engine engines[LP55XX_ENGINE_MAX];
const struct firmware *fw;
};
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
new file mode 100644
index 000000000000..8d55a780ca46
--- /dev/null
+++ b/drivers/leds/leds-lp8501.c
@@ -0,0 +1,410 @@
+/*
+ * TI LP8501 9 channel LED Driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.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/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/leds-lp55xx.h>
+#include <linux/slab.h>
+
+#include "leds-lp55xx-common.h"
+
+#define LP8501_PROGRAM_LENGTH 32
+#define LP8501_MAX_LEDS 9
+
+/* Registers */
+#define LP8501_REG_ENABLE 0x00
+#define LP8501_ENABLE BIT(6)
+#define LP8501_EXEC_M 0x3F
+#define LP8501_EXEC_ENG1_M 0x30
+#define LP8501_EXEC_ENG2_M 0x0C
+#define LP8501_EXEC_ENG3_M 0x03
+#define LP8501_RUN_ENG1 0x20
+#define LP8501_RUN_ENG2 0x08
+#define LP8501_RUN_ENG3 0x02
+
+#define LP8501_REG_OP_MODE 0x01
+#define LP8501_MODE_ENG1_M 0x30
+#define LP8501_MODE_ENG2_M 0x0C
+#define LP8501_MODE_ENG3_M 0x03
+#define LP8501_LOAD_ENG1 0x10
+#define LP8501_LOAD_ENG2 0x04
+#define LP8501_LOAD_ENG3 0x01
+
+#define LP8501_REG_PWR_CONFIG 0x05
+#define LP8501_PWR_CONFIG_M 0x03
+
+#define LP8501_REG_LED_PWM_BASE 0x16
+
+#define LP8501_REG_LED_CURRENT_BASE 0x26
+
+#define LP8501_REG_CONFIG 0x36
+#define LP8501_PWM_PSAVE BIT(7)
+#define LP8501_AUTO_INC BIT(6)
+#define LP8501_PWR_SAVE BIT(5)
+#define LP8501_CP_AUTO 0x18
+#define LP8501_INT_CLK BIT(0)
+#define LP8501_DEFAULT_CFG \
+ (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO)
+
+#define LP8501_REG_RESET 0x3D
+#define LP8501_RESET 0xFF
+
+#define LP8501_REG_PROG_PAGE_SEL 0x4F
+#define LP8501_PAGE_ENG1 0
+#define LP8501_PAGE_ENG2 1
+#define LP8501_PAGE_ENG3 2
+
+#define LP8501_REG_PROG_MEM 0x50
+
+#define LP8501_ENG1_IS_LOADING(mode) \
+ ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1)
+#define LP8501_ENG2_IS_LOADING(mode) \
+ ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2)
+#define LP8501_ENG3_IS_LOADING(mode) \
+ ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3)
+
+static inline void lp8501_wait_opmode_done(void)
+{
+ usleep_range(1000, 2000);
+}
+
+static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current)
+{
+ led->led_current = led_current;
+ lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr,
+ led_current);
+}
+
+static int lp8501_post_init_device(struct lp55xx_chip *chip)
+{
+ int ret;
+ u8 val = LP8501_DEFAULT_CFG;
+
+ ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE);
+ if (ret)
+ return ret;
+
+ /* Chip startup time is 500 us, 1 - 2 ms gives some margin */
+ usleep_range(1000, 2000);
+
+ if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
+ val |= LP8501_INT_CLK;
+
+ ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
+ if (ret)
+ return ret;
+
+ /* Power selection for each output */
+ return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG,
+ LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel);
+}
+
+static void lp8501_load_engine(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
+ u8 mask[] = {
+ [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
+ [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
+ [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
+ };
+
+ u8 val[] = {
+ [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
+ [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
+ [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
+ };
+
+ u8 page_sel[] = {
+ [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
+ [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
+ [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
+ };
+
+ lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]);
+
+ lp8501_wait_opmode_done();
+
+ lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]);
+}
+
+static void lp8501_stop_engine(struct lp55xx_chip *chip)
+{
+ lp55xx_write(chip, LP8501_REG_OP_MODE, 0);
+ lp8501_wait_opmode_done();
+}
+
+static void lp8501_turn_off_channels(struct lp55xx_chip *chip)
+{
+ int i;
+
+ for (i = 0; i < LP8501_MAX_LEDS; i++)
+ lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0);
+}
+
+static void lp8501_run_engine(struct lp55xx_chip *chip, bool start)
+{
+ int ret;
+ u8 mode;
+ u8 exec;
+
+ /* stop engine */
+ if (!start) {
+ lp8501_stop_engine(chip);
+ lp8501_turn_off_channels(chip);
+ return;
+ }
+
+ /*
+ * To run the engine,
+ * operation mode and enable register should updated at the same time
+ */
+
+ ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode);
+ if (ret)
+ return;
+
+ ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec);
+ if (ret)
+ return;
+
+ /* change operation mode to RUN only when each engine is loading */
+ if (LP8501_ENG1_IS_LOADING(mode)) {
+ mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1;
+ exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1;
+ }
+
+ if (LP8501_ENG2_IS_LOADING(mode)) {
+ mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2;
+ exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2;
+ }
+
+ if (LP8501_ENG3_IS_LOADING(mode)) {
+ mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3;
+ exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3;
+ }
+
+ lp55xx_write(chip, LP8501_REG_OP_MODE, mode);
+ lp8501_wait_opmode_done();
+
+ lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec);
+}
+
+static int lp8501_update_program_memory(struct lp55xx_chip *chip,
+ const u8 *data, size_t size)
+{
+ u8 pattern[LP8501_PROGRAM_LENGTH] = {0};
+ unsigned cmd;
+ char c[3];
+ int update_size;
+ int nrchars;
+ int offset = 0;
+ int ret;
+ int i;
+
+ /* clear program memory before updating */
+ for (i = 0; i < LP8501_PROGRAM_LENGTH; i++)
+ lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0);
+
+ i = 0;
+ while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) {
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
+ if (ret != 1)
+ goto err;
+
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto err;
+
+ pattern[i] = (u8)cmd;
+ offset += nrchars;
+ i++;
+ }
+
+ /* Each instruction is 16bit long. Check that length is even */
+ if (i % 2)
+ goto err;
+
+ update_size = i;
+ for (i = 0; i < update_size; i++)
+ lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]);
+
+ return 0;
+
+err:
+ dev_err(&chip->cl->dev, "wrong pattern format\n");
+ return -EINVAL;
+}
+
+static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
+{
+ const struct firmware *fw = chip->fw;
+
+ if (fw->size > LP8501_PROGRAM_LENGTH) {
+ dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
+ fw->size);
+ return;
+ }
+
+ /*
+ * Program momery sequence
+ * 1) set engine mode to "LOAD"
+ * 2) write firmware data into program memory
+ */
+
+ lp8501_load_engine(chip);
+ lp8501_update_program_memory(chip, fw->data, fw->size);
+}
+
+static void lp8501_led_brightness_work(struct work_struct *work)
+{
+ struct lp55xx_led *led = container_of(work, struct lp55xx_led,
+ brightness_work);
+ struct lp55xx_chip *chip = led->chip;
+
+ mutex_lock(&chip->lock);
+ lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
+ led->brightness);
+ mutex_unlock(&chip->lock);
+}
+
+/* Chip specific configurations */
+static struct lp55xx_device_config lp8501_cfg = {
+ .reset = {
+ .addr = LP8501_REG_RESET,
+ .val = LP8501_RESET,
+ },
+ .enable = {
+ .addr = LP8501_REG_ENABLE,
+ .val = LP8501_ENABLE,
+ },
+ .max_channel = LP8501_MAX_LEDS,
+ .post_init_device = lp8501_post_init_device,
+ .brightness_work_fn = lp8501_led_brightness_work,
+ .set_led_current = lp8501_set_led_current,
+ .firmware_cb = lp8501_firmware_loaded,
+ .run_engine = lp8501_run_engine,
+};
+
+static int lp8501_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lp55xx_chip *chip;
+ struct lp55xx_led *led;
+ struct lp55xx_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+
+ if (!dev_get_platdata(&client->dev)) {
+ if (np) {
+ ret = lp55xx_of_populate_pdata(&client->dev, np);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+ }
+ pdata = dev_get_platdata(&client->dev);
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ led = devm_kzalloc(&client->dev,
+ sizeof(*led) * pdata->num_channels, GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ chip->cl = client;
+ chip->pdata = pdata;
+ chip->cfg = &lp8501_cfg;
+
+ mutex_init(&chip->lock);
+
+ i2c_set_clientdata(client, led);
+
+ ret = lp55xx_init_device(chip);
+ if (ret)
+ goto err_init;
+
+ dev_info(&client->dev, "%s Programmable led chip found\n", id->name);
+
+ ret = lp55xx_register_leds(led, chip);
+ if (ret)
+ goto err_register_leds;
+
+ ret = lp55xx_register_sysfs(chip);
+ if (ret) {
+ dev_err(&client->dev, "registering sysfs failed\n");
+ goto err_register_sysfs;
+ }
+
+ return 0;
+
+err_register_sysfs:
+ lp55xx_unregister_leds(led, chip);
+err_register_leds:
+ lp55xx_deinit_device(chip);
+err_init:
+ return ret;
+}
+
+static int lp8501_remove(struct i2c_client *client)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(client);
+ struct lp55xx_chip *chip = led->chip;
+
+ lp8501_stop_engine(chip);
+ lp55xx_unregister_sysfs(chip);
+ lp55xx_unregister_leds(led, chip);
+ lp55xx_deinit_device(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id lp8501_id[] = {
+ { "lp8501", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp8501_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_lp8501_leds_match[] = {
+ { .compatible = "ti,lp8501", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
+#endif
+
+static struct i2c_driver lp8501_driver = {
+ .driver = {
+ .name = "lp8501",
+ .of_match_table = of_match_ptr(of_lp8501_leds_match),
+ },
+ .probe = lp8501_probe,
+ .remove = lp8501_remove,
+ .id_table = lp8501_id,
+};
+
+module_i2c_driver(lp8501_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index ca48a7d5502d..3417e5be7b57 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -135,7 +135,7 @@ static void delete_lt3593_led(struct lt3593_led_data *led)
static int lt3593_led_probe(struct platform_device *pdev)
{
- struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct lt3593_led_data *leds_data;
int i, ret = 0;
@@ -169,7 +169,7 @@ err:
static int lt3593_led_remove(struct platform_device *pdev)
{
int i;
- struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct lt3593_led_data *leds_data;
leds_data = platform_get_drvdata(pdev);
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c
index c61c5ebcc08e..2f9f141084ba 100644
--- a/drivers/leds/leds-netxbig.c
+++ b/drivers/leds/leds-netxbig.c
@@ -306,7 +306,7 @@ create_netxbig_led(struct platform_device *pdev,
struct netxbig_led_data *led_dat,
const struct netxbig_led *template)
{
- struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
+ struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret;
spin_lock_init(&led_dat->lock);
@@ -354,7 +354,7 @@ create_netxbig_led(struct platform_device *pdev,
static int netxbig_led_probe(struct platform_device *pdev)
{
- struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
+ struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct netxbig_led_data *leds_data;
int i;
int ret;
@@ -391,7 +391,7 @@ err_free_leds:
static int netxbig_led_remove(struct platform_device *pdev)
{
- struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
+ struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct netxbig_led_data *leds_data;
int i;
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index e7df9875c400..141f13438e80 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -321,7 +321,7 @@ static inline int sizeof_ns2_led_priv(int num_leds)
static int ns2_led_probe(struct platform_device *pdev)
{
- struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
+ struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct ns2_led_priv *priv;
int i;
int ret;
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 0c597bdd23f9..4a0e786b7832 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -446,7 +446,8 @@ static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pca9532_data *data = i2c_get_clientdata(client);
- struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
+ struct pca9532_platform_data *pca9532_pdata =
+ dev_get_platdata(&client->dev);
if (!pca9532_pdata)
return -EIO;
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index edf485b773c8..c3a08b60535b 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -267,7 +267,7 @@ static int pca955x_probe(struct i2c_client *client,
chip = &pca955x_chipdefs[id->driver_data];
adapter = to_i2c_adapter(client->dev.parent);
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
/* Make sure the slave address / chip type combo given is possible */
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c
deleted file mode 100644
index 9aae5679ffb2..000000000000
--- a/drivers/leds/leds-pca9633.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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>
-#include <linux/platform_data/leds-pca9633.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 pca9633_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct pca9633_led *pca9633;
- struct pca9633_platform_data *pdata;
- int i, err;
-
- pdata = client->dev.platform_data;
-
- if (pdata) {
- if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) {
- dev_err(&client->dev, "board info must claim at most 4 LEDs");
- return -EINVAL;
- }
- }
-
- pca9633 = devm_kzalloc(&client->dev, 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->leds.num_leds) {
- if (pdata->leds.leds[i].name)
- snprintf(pca9633[i].name,
- sizeof(pca9633[i].name), "pca9633:%s",
- pdata->leds.leds[i].name);
- if (pdata->leds.leds[i].default_trigger)
- pca9633[i].led_cdev.default_trigger =
- pdata->leds.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);
-
- /* Configure output: open-drain or totem pole (push-pull) */
- if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN)
- i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01);
-
- /* 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);
- }
-
- return err;
-}
-
-static int 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);
- }
-
- return 0;
-}
-
-static struct i2c_driver pca9633_driver = {
- .driver = {
- .name = "leds-pca9633",
- .owner = THIS_MODULE,
- },
- .probe = pca9633_probe,
- .remove = 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-pca963x.c b/drivers/leds/leds-pca963x.c
new file mode 100644
index 000000000000..82589c0a5689
--- /dev/null
+++ b/drivers/leds/leds-pca963x.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2011 bct electronic GmbH
+ * Copyright 2013 Qtechnology/AS
+ *
+ * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
+ * Author: Ricardo Ribalda <ricardo.ribalda@gmail.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)
+ * LED driver for the PCA9634 I2C LED driver (7-bit slave address set by hw.)
+ *
+ * Note that hardware blinking violates the leds infrastructure driver
+ * interface since the hardware only supports blinking all LEDs with the
+ * same delay_on/delay_off rates. That is, only the LEDs that are set to
+ * blink will actually blink but all LEDs that are set to blink will blink
+ * in identical fashion. The delay_on/delay_off values of the last LED
+ * that is set to blink will be used for all of the blinking LEDs.
+ * Hardware blinking is disabled by default but can be enabled by setting
+ * the 'blink_type' member in the platform_data struct to 'PCA963X_HW_BLINK'
+ * or by adding the 'nxp,hw-blink' property to the DTS.
+ */
+
+#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>
+#include <linux/of.h>
+#include <linux/platform_data/leds-pca963x.h>
+
+/* LED select registers determine the source that drives LED outputs */
+#define PCA963X_LED_OFF 0x0 /* LED driver off */
+#define PCA963X_LED_ON 0x1 /* LED driver on */
+#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */
+#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
+
+#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */
+
+#define PCA963X_MODE1 0x00
+#define PCA963X_MODE2 0x01
+#define PCA963X_PWM_BASE 0x02
+
+enum pca963x_type {
+ pca9633,
+ pca9634,
+};
+
+struct pca963x_chipdef {
+ u8 grppwm;
+ u8 grpfreq;
+ u8 ledout_base;
+ int n_leds;
+};
+
+static struct pca963x_chipdef pca963x_chipdefs[] = {
+ [pca9633] = {
+ .grppwm = 0x6,
+ .grpfreq = 0x7,
+ .ledout_base = 0x8,
+ .n_leds = 4,
+ },
+ [pca9634] = {
+ .grppwm = 0xa,
+ .grpfreq = 0xb,
+ .ledout_base = 0xc,
+ .n_leds = 8,
+ },
+};
+
+/* Total blink period in milliseconds */
+#define PCA963X_BLINK_PERIOD_MIN 42
+#define PCA963X_BLINK_PERIOD_MAX 10667
+
+static const struct i2c_device_id pca963x_id[] = {
+ { "pca9632", pca9633 },
+ { "pca9633", pca9633 },
+ { "pca9634", pca9634 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca963x_id);
+
+enum pca963x_cmd {
+ BRIGHTNESS_SET,
+ BLINK_SET,
+};
+
+struct pca963x_led;
+
+struct pca963x {
+ struct pca963x_chipdef *chipdef;
+ struct mutex mutex;
+ struct i2c_client *client;
+ struct pca963x_led *leds;
+};
+
+struct pca963x_led {
+ struct pca963x *chip;
+ struct work_struct work;
+ enum led_brightness brightness;
+ struct led_classdev led_cdev;
+ int led_num; /* 0 .. 7 potentially */
+ enum pca963x_cmd cmd;
+ char name[32];
+ u8 gdc;
+ u8 gfrq;
+};
+
+static void pca963x_brightness_work(struct pca963x_led *pca963x)
+{
+ u8 ledout_addr = pca963x->chip->chipdef->ledout_base
+ + (pca963x->led_num / 4);
+ u8 ledout;
+ int shift = 2 * (pca963x->led_num % 4);
+ u8 mask = 0x3 << shift;
+
+ mutex_lock(&pca963x->chip->mutex);
+ ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
+ switch (pca963x->brightness) {
+ case LED_FULL:
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ (ledout & ~mask) | (PCA963X_LED_ON << shift));
+ break;
+ case LED_OFF:
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ ledout & ~mask);
+ break;
+ default:
+ i2c_smbus_write_byte_data(pca963x->chip->client,
+ PCA963X_PWM_BASE + pca963x->led_num,
+ pca963x->brightness);
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ (ledout & ~mask) | (PCA963X_LED_PWM << shift));
+ break;
+ }
+ mutex_unlock(&pca963x->chip->mutex);
+}
+
+static void pca963x_blink_work(struct pca963x_led *pca963x)
+{
+ u8 ledout_addr = pca963x->chip->chipdef->ledout_base +
+ (pca963x->led_num / 4);
+ u8 ledout;
+ u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client,
+ PCA963X_MODE2);
+ int shift = 2 * (pca963x->led_num % 4);
+ u8 mask = 0x3 << shift;
+
+ i2c_smbus_write_byte_data(pca963x->chip->client,
+ pca963x->chip->chipdef->grppwm, pca963x->gdc);
+
+ i2c_smbus_write_byte_data(pca963x->chip->client,
+ pca963x->chip->chipdef->grpfreq, pca963x->gfrq);
+
+ if (!(mode2 & PCA963X_MODE2_DMBLNK))
+ i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2,
+ mode2 | PCA963X_MODE2_DMBLNK);
+
+ mutex_lock(&pca963x->chip->mutex);
+ ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
+ if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift))
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift));
+ mutex_unlock(&pca963x->chip->mutex);
+}
+
+static void pca963x_work(struct work_struct *work)
+{
+ struct pca963x_led *pca963x = container_of(work,
+ struct pca963x_led, work);
+
+ switch (pca963x->cmd) {
+ case BRIGHTNESS_SET:
+ pca963x_brightness_work(pca963x);
+ break;
+ case BLINK_SET:
+ pca963x_blink_work(pca963x);
+ break;
+ }
+}
+
+static void pca963x_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pca963x_led *pca963x;
+
+ pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
+
+ pca963x->cmd = BRIGHTNESS_SET;
+ pca963x->brightness = value;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca963x->work);
+}
+
+static int pca963x_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct pca963x_led *pca963x;
+ unsigned long time_on, time_off, period;
+ u8 gdc, gfrq;
+
+ pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
+
+ time_on = *delay_on;
+ time_off = *delay_off;
+
+ /* If both zero, pick reasonable defaults of 500ms each */
+ if (!time_on && !time_off) {
+ time_on = 500;
+ time_off = 500;
+ }
+
+ period = time_on + time_off;
+
+ /* If period not supported by hardware, default to someting sane. */
+ if ((period < PCA963X_BLINK_PERIOD_MIN) ||
+ (period > PCA963X_BLINK_PERIOD_MAX)) {
+ time_on = 500;
+ time_off = 500;
+ period = time_on + time_off;
+ }
+
+ /*
+ * From manual: duty cycle = (GDC / 256) ->
+ * (time_on / period) = (GDC / 256) ->
+ * GDC = ((time_on * 256) / period)
+ */
+ gdc = (time_on * 256) / period;
+
+ /*
+ * From manual: period = ((GFRQ + 1) / 24) in seconds.
+ * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) ->
+ * GFRQ = ((period * 24 / 1000) - 1)
+ */
+ gfrq = (period * 24 / 1000) - 1;
+
+ pca963x->cmd = BLINK_SET;
+ pca963x->gdc = gdc;
+ pca963x->gfrq = gfrq;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca963x->work);
+
+ *delay_on = time_on;
+ *delay_off = time_off;
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static struct pca963x_platform_data *
+pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
+{
+ struct device_node *np = client->dev.of_node, *child;
+ struct pca963x_platform_data *pdata;
+ struct led_info *pca963x_leds;
+ int count;
+
+ count = of_get_child_count(np);
+ if (!count || count > chip->n_leds)
+ return ERR_PTR(-ENODEV);
+
+ pca963x_leds = devm_kzalloc(&client->dev,
+ sizeof(struct led_info) * chip->n_leds, GFP_KERNEL);
+ if (!pca963x_leds)
+ return ERR_PTR(-ENOMEM);
+
+ for_each_child_of_node(np, child) {
+ struct led_info led;
+ u32 reg;
+ int res;
+
+ res = of_property_read_u32(child, "reg", &reg);
+ if ((res != 0) || (reg >= chip->n_leds))
+ continue;
+ led.name =
+ of_get_property(child, "label", NULL) ? : child->name;
+ led.default_trigger =
+ of_get_property(child, "linux,default-trigger", NULL);
+ pca963x_leds[reg] = led;
+ }
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct pca963x_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->leds.leds = pca963x_leds;
+ pdata->leds.num_leds = chip->n_leds;
+
+ /* default to open-drain unless totem pole (push-pull) is specified */
+ if (of_property_read_bool(np, "nxp,totem-pole"))
+ pdata->outdrv = PCA963X_TOTEM_POLE;
+ else
+ pdata->outdrv = PCA963X_OPEN_DRAIN;
+
+ /* default to software blinking unless hardware blinking is specified */
+ if (of_property_read_bool(np, "nxp,hw-blink"))
+ pdata->blink_type = PCA963X_HW_BLINK;
+ else
+ pdata->blink_type = PCA963X_SW_BLINK;
+
+ return pdata;
+}
+
+static const struct of_device_id of_pca963x_match[] = {
+ { .compatible = "nxp,pca9632", },
+ { .compatible = "nxp,pca9633", },
+ { .compatible = "nxp,pca9634", },
+ {},
+};
+#else
+static struct pca963x_platform_data *
+pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
+static int pca963x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca963x *pca963x_chip;
+ struct pca963x_led *pca963x;
+ struct pca963x_platform_data *pdata;
+ struct pca963x_chipdef *chip;
+ int i, err;
+
+ chip = &pca963x_chipdefs[id->driver_data];
+ pdata = dev_get_platdata(&client->dev);
+
+ if (!pdata) {
+ pdata = pca963x_dt_init(client, chip);
+ if (IS_ERR(pdata)) {
+ dev_warn(&client->dev, "could not parse configuration\n");
+ pdata = NULL;
+ }
+ }
+
+ if (pdata && (pdata->leds.num_leds < 1 ||
+ pdata->leds.num_leds > chip->n_leds)) {
+ dev_err(&client->dev, "board info must claim 1-%d LEDs",
+ chip->n_leds);
+ return -EINVAL;
+ }
+
+ pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip),
+ GFP_KERNEL);
+ if (!pca963x_chip)
+ return -ENOMEM;
+ pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x),
+ GFP_KERNEL);
+ if (!pca963x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pca963x_chip);
+
+ mutex_init(&pca963x_chip->mutex);
+ pca963x_chip->chipdef = chip;
+ pca963x_chip->client = client;
+ pca963x_chip->leds = pca963x;
+
+ /* Turn off LEDs by default*/
+ i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00);
+ if (chip->n_leds > 4)
+ i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00);
+
+ for (i = 0; i < chip->n_leds; i++) {
+ pca963x[i].led_num = i;
+ pca963x[i].chip = pca963x_chip;
+
+ /* Platform data can specify LED names and default triggers */
+ if (pdata && i < pdata->leds.num_leds) {
+ if (pdata->leds.leds[i].name)
+ snprintf(pca963x[i].name,
+ sizeof(pca963x[i].name), "pca963x:%s",
+ pdata->leds.leds[i].name);
+ if (pdata->leds.leds[i].default_trigger)
+ pca963x[i].led_cdev.default_trigger =
+ pdata->leds.leds[i].default_trigger;
+ }
+ if (!pdata || i >= pdata->leds.num_leds ||
+ !pdata->leds.leds[i].name)
+ snprintf(pca963x[i].name, sizeof(pca963x[i].name),
+ "pca963x:%d:%.2x:%d", client->adapter->nr,
+ client->addr, i);
+
+ pca963x[i].led_cdev.name = pca963x[i].name;
+ pca963x[i].led_cdev.brightness_set = pca963x_led_set;
+
+ if (pdata && pdata->blink_type == PCA963X_HW_BLINK)
+ pca963x[i].led_cdev.blink_set = pca963x_blink_set;
+
+ INIT_WORK(&pca963x[i].work, pca963x_work);
+
+ err = led_classdev_register(&client->dev, &pca963x[i].led_cdev);
+ if (err < 0)
+ goto exit;
+ }
+
+ /* Disable LED all-call address and set normal mode */
+ i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00);
+
+ /* Configure output: open-drain or totem pole (push-pull) */
+ if (pdata && pdata->outdrv == PCA963X_OPEN_DRAIN)
+ i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x01);
+
+ return 0;
+
+exit:
+ while (i--) {
+ led_classdev_unregister(&pca963x[i].led_cdev);
+ cancel_work_sync(&pca963x[i].work);
+ }
+
+ return err;
+}
+
+static int pca963x_remove(struct i2c_client *client)
+{
+ struct pca963x *pca963x = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < pca963x->chipdef->n_leds; i++) {
+ led_classdev_unregister(&pca963x->leds[i].led_cdev);
+ cancel_work_sync(&pca963x->leds[i].work);
+ }
+
+ return 0;
+}
+
+static struct i2c_driver pca963x_driver = {
+ .driver = {
+ .name = "leds-pca963x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_pca963x_match),
+ },
+ .probe = pca963x_probe,
+ .remove = pca963x_remove,
+ .id_table = pca963x_id,
+};
+
+module_i2c_driver(pca963x_driver);
+
+MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
+MODULE_DESCRIPTION("PCA963X LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index faf52c005e8c..bb6f94898541 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -147,7 +147,7 @@ err:
static int led_pwm_probe(struct platform_device *pdev)
{
- struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct led_pwm_priv *priv;
int i, ret = 0;
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
index 4253a9b03dbf..358430db6e66 100644
--- a/drivers/leds/leds-regulator.c
+++ b/drivers/leds/leds-regulator.c
@@ -142,7 +142,8 @@ static void regulator_led_brightness_set(struct led_classdev *led_cdev,
static int regulator_led_probe(struct platform_device *pdev)
{
- struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
+ struct led_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
struct regulator_led *led;
struct regulator *vcc;
int ret = 0;
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index e1a0df63a37f..76483fb5ee45 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -71,7 +71,7 @@ static int s3c24xx_led_remove(struct platform_device *dev)
static int s3c24xx_led_probe(struct platform_device *dev)
{
- struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;
+ struct s3c24xx_led_platdata *pdata = dev_get_platdata(&dev->dev);
struct s3c24xx_gpio_led *led;
int ret;
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
index 64e204e714f6..5b8f938a8d73 100644
--- a/drivers/leds/leds-ss4200.c
+++ b/drivers/leds/leds-ss4200.c
@@ -91,7 +91,7 @@ MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
* detected as working, but in reality it is not) as low as
* possible.
*/
-static struct dmi_system_id __initdata nas_led_whitelist[] = {
+static struct dmi_system_id nas_led_whitelist[] __initdata = {
{
.callback = ss4200_led_dmi_callback,
.ident = "Intel SS4200-E",
@@ -197,7 +197,7 @@ static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
spin_unlock(&nasgpio_gpio_lock);
}
-u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
+static u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
{
struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
u32 gpio_in;
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 98fe021ba276..8cc304f36728 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -737,7 +737,7 @@ static int tca6507_probe(struct i2c_client *client,
int i = 0;
adapter = to_i2c_adapter(client->dev.parent);
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
return -EIO;
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
index 120815a42701..0a1a13f3a6a5 100644
--- a/drivers/leds/leds-wm831x-status.c
+++ b/drivers/leds/leds-wm831x-status.c
@@ -230,9 +230,9 @@ static int wm831x_status_probe(struct platform_device *pdev)
int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
int ret;
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ res = platform_get_resource(pdev, IORESOURCE_REG, 0);
if (res == NULL) {
- dev_err(&pdev->dev, "No I/O resource\n");
+ dev_err(&pdev->dev, "No register resource\n");
ret = -EINVAL;
goto err;
}
@@ -246,8 +246,8 @@ static int wm831x_status_probe(struct platform_device *pdev)
drvdata->wm831x = wm831x;
drvdata->reg = res->start;
- if (wm831x->dev->platform_data)
- chip_pdata = wm831x->dev->platform_data;
+ if (dev_get_platdata(wm831x->dev))
+ chip_pdata = dev_get_platdata(wm831x->dev);
else
chip_pdata = NULL;
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index 8a181d56602d..3f75fd22fd49 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -203,7 +203,7 @@ static int wm8350_led_probe(struct platform_device *pdev)
{
struct regulator *isink, *dcdc;
struct wm8350_led *led;
- struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
+ struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
int i;
if (pdata == NULL) {
diff --git a/drivers/leds/trigger/ledtrig-backlight.c b/drivers/leds/trigger/ledtrig-backlight.c
index 3c9c88a07eb8..47e55aa9eefa 100644
--- a/drivers/leds/trigger/ledtrig-backlight.c
+++ b/drivers/leds/trigger/ledtrig-backlight.c
@@ -36,26 +36,28 @@ static int fb_notifier_callback(struct notifier_block *p,
struct bl_trig_notifier, notifier);
struct led_classdev *led = n->led;
struct fb_event *fb_event = data;
- int *blank = fb_event->data;
- int new_status = *blank ? BLANK : UNBLANK;
+ int *blank;
+ int new_status;
- switch (event) {
- case FB_EVENT_BLANK:
- if (new_status == n->old_status)
- break;
+ /* If we aren't interested in this event, skip it immediately ... */
+ if (event != FB_EVENT_BLANK)
+ return 0;
- if ((n->old_status == UNBLANK) ^ n->invert) {
- n->brightness = led->brightness;
- __led_set_brightness(led, LED_OFF);
- } else {
- __led_set_brightness(led, n->brightness);
- }
+ blank = fb_event->data;
+ new_status = *blank ? BLANK : UNBLANK;
- n->old_status = new_status;
+ if (new_status == n->old_status)
+ return 0;
- break;
+ if ((n->old_status == UNBLANK) ^ n->invert) {
+ n->brightness = led->brightness;
+ __led_set_brightness(led, LED_OFF);
+ } else {
+ __led_set_brightness(led, n->brightness);
}
+ n->old_status = new_status;
+
return 0;
}