diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 19 | ||||
-rw-r--r-- | drivers/leds/Makefile | 2 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 70 | ||||
-rw-r--r-- | drivers/leds/led-core.c | 70 | ||||
-rw-r--r-- | drivers/leds/leds-88pm860x.c | 23 | ||||
-rw-r--r-- | drivers/leds/leds-ams-delta.c | 126 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 3 | ||||
-rw-r--r-- | drivers/leds/leds-lm3530.c | 135 | ||||
-rw-r--r-- | drivers/leds/leds-lp5521.c | 147 | ||||
-rw-r--r-- | drivers/leds/leds-lp5523.c | 6 | ||||
-rw-r--r-- | drivers/leds/leds-pca9633.c | 193 | ||||
-rw-r--r-- | drivers/leds/leds-tca6507.c | 9 |
12 files changed, 502 insertions, 301 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 8c7a75d53101..ff4b8cfda585 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 @@ -234,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 6bcf4f695515..890481cb09f6 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -12,7 +12,6 @@ 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_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o @@ -30,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 0c8739c448b1..5bff8439dc68 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 016d19f5486f..d6860043f6f9 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 4ca00624bd18..5b61aaf7ac0f 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 07428357c83f..000000000000 --- 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 7df74cb97e70..f4c470a3bc8d 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 e59c166a0ce2..968fd5fef4fc 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 d62a7982a5e6..410a723b8691 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 73e791ae7259..857a3e15f2dd 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-pca9633.c b/drivers/leds/leds-pca9633.c new file mode 100644 index 000000000000..d8926fd031aa --- /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 133f89fb7071..6c1c14f31635 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; |