diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-10 20:14:07 +0900 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-10 20:14:07 +0900 |
commit | c7a6ced9d8e8411bdafe83998474d185a79badc3 (patch) | |
tree | bf9a230bfc99165b9d8fa9ed9fc453619bd4a9fb | |
parent | a188e7e93a36627fb3f0013f41857ab54f076d04 (diff) | |
parent | 2f73c3927318abe3d7fcaecadfbad89e2d12f881 (diff) | |
download | blackbird-op-linux-c7a6ced9d8e8411bdafe83998474d185a79badc3.tar.gz blackbird-op-linux-c7a6ced9d8e8411bdafe83998474d185a79badc3.zip |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull LED subsystem update from Bryan Wu.
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (24 commits)
leds: add output driver configuration for pca9633 led driver
leds: lm3642: Use regmap_update_bits() in lm3642_chip_init()
leds: Add new LED driver for lm3642 chips
leds-lp5523: Fix riskiness of the page fault
leds-lp5523: turn off the LED engines on unloading the driver
leds-lm3530: Fix smatch warnings
leds-lm3530: Use devm_regulator_get function
leds: leds-gpio: adopt pinctrl support
leds: Add new LED driver for lm355x chips
leds-lp5523: use the i2c device id rather than fixed name
leds-lp5523: add new device id for LP55231
leds-lp5523: support new LP55231 device
leds: triggers: send uevent when changing triggers
leds-lp5523: minor code style fixes
leds-lp5523: change the return type of lp5523_set_mode()
leds-lp5523: set the brightness to 0 forcely on removing the driver
leds-lp5523: add channel name in the platform data
leds: leds-gpio: Use of_get_child_count() helper
leds: leds-gpio: Use platform_{get,set}_drvdata
leds: leds-gpio: use of_match_ptr()
...
-rw-r--r-- | Documentation/leds/leds-lp5523.txt | 21 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 26 | ||||
-rw-r--r-- | drivers/leds/Makefile | 3 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 15 | ||||
-rw-r--r-- | drivers/leds/led-core.c | 16 | ||||
-rw-r--r-- | drivers/leds/led-triggers.c | 17 | ||||
-rw-r--r-- | drivers/leds/leds-clevo-mail.c | 10 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 19 | ||||
-rw-r--r-- | drivers/leds/leds-lm3530.c | 16 | ||||
-rw-r--r-- | drivers/leds/leds-lm3556.c | 512 | ||||
-rw-r--r-- | drivers/leds/leds-lm355x.c | 572 | ||||
-rw-r--r-- | drivers/leds/leds-lm3642.c | 462 | ||||
-rw-r--r-- | drivers/leds/leds-lp5523.c | 75 | ||||
-rw-r--r-- | drivers/leds/leds-pca9633.c | 19 | ||||
-rw-r--r-- | drivers/leds/leds-wm8350.c | 29 | ||||
-rw-r--r-- | drivers/leds/leds.h | 2 | ||||
-rw-r--r-- | include/linux/leds-lp5523.h | 1 | ||||
-rw-r--r-- | include/linux/leds.h | 4 | ||||
-rw-r--r-- | include/linux/platform_data/leds-lm3556.h | 50 | ||||
-rw-r--r-- | include/linux/platform_data/leds-lm355x.h | 66 | ||||
-rw-r--r-- | include/linux/platform_data/leds-lm3642.h | 38 | ||||
-rw-r--r-- | include/linux/platform_data/leds-pca9633.h | 35 |
22 files changed, 1350 insertions, 658 deletions
diff --git a/Documentation/leds/leds-lp5523.txt b/Documentation/leds/leds-lp5523.txt index fad2feb8b7ce..c2743f59f9ac 100644 --- a/Documentation/leds/leds-lp5523.txt +++ b/Documentation/leds/leds-lp5523.txt @@ -10,8 +10,22 @@ Contact: Samu Onkalo (samu.p.onkalo-at-nokia.com) Description ----------- LP5523 can drive up to 9 channels. Leds can be controlled directly via -the led class control interface. Channels have generic names: -lp5523:channelx where x is 0...8 +the led class control interface. +The name of each channel is configurable in the platform data - name and label. +There are three options to make the channel name. + +a) Define the 'name' in the platform data +To make specific channel name, then use 'name' platform data. +/sys/class/leds/R1 (name: 'R1') +/sys/class/leds/B1 (name: 'B1') + +b) Use the 'label' with no 'name' field +For one device name with channel number, then use 'label'. +/sys/class/leds/RGB:channelN (label: 'RGB', N: 0 ~ 8) + +c) Default +If both fields are NULL, 'lp5523' is used by default. +/sys/class/leds/lp5523:channelN (N: 0 ~ 8) The chip provides 3 engines. Each engine can control channels without interaction from the main CPU. Details of the micro engine code can be found @@ -46,12 +60,13 @@ Note - chan_nr can have values between 0 and 8. static struct lp5523_led_config lp5523_led_config[] = { { + .name = "D1", .chan_nr = 0, .led_current = 50, .max_current = 130, }, ... - }, { + { .chan_nr = 8, .led_current = 50, .max_current = 130, diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 16578d3b52bb..f508defc0d96 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -63,6 +63,17 @@ config LEDS_LM3533 hardware-accelerated blinking with maximum on and off periods of 9.8 and 77 seconds respectively. +config LEDS_LM3642 + tristate "LED support for LM3642 Chip" + depends on LEDS_CLASS && I2C + select REGMAP_I2C + help + This option enables support for LEDs connected to LM3642. + The LM3642 is a 4MHz fixed-frequency synchronous boost + converter plus 1.5A constant current driver for a high-current + white LED. + + config LEDS_LOCOMO tristate "LED Support for Locomo device" depends on LEDS_CLASS @@ -192,11 +203,12 @@ config LEDS_LP5521 programming the engines. config LEDS_LP5523 - tristate "LED Support for N.S. LP5523 LED driver chip" + tristate "LED Support for TI/National LP5523/55231 LED driver chip" depends on LEDS_CLASS && I2C help - If you say yes here you get support for the National Semiconductor - LP5523 LED driver. It is 9 channel chip with programmable engines. + If you say yes here you get support for TI/National Semiconductor + LP5523/55231 LED driver. + It is 9 channel chip with programmable engines. Driver provides direct control via LED class and interface for programming the engines. @@ -422,13 +434,13 @@ config LEDS_MAX8997 This option enables support for on-chip LED drivers on MAXIM MAX8997 PMIC. -config LEDS_LM3556 - tristate "LED support for LM3556 Chip" +config LEDS_LM355x + tristate "LED support for LM355x Chips, LM3554 and LM3556" depends on LEDS_CLASS && I2C select REGMAP_I2C help - This option enables support for LEDs connected to LM3556. - LM3556 includes Torch, Flash and Indicator functions. + This option enables support for LEDs connected to LM355x. + LM355x includes Torch, Flash and Indicator functions. config LEDS_OT200 tristate "LED support for the Bachmann OT200" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a9b627c4f8ba..3fb9641b6194 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o +obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o @@ -48,7 +49,7 @@ obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o -obj-$(CONFIG_LEDS_LM3556) += leds-lm3556.o +obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o # LED SPI Drivers diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index c599095bc005..48cce18e9d6d 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -124,6 +124,16 @@ static void led_timer_function(unsigned long data) mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); } +static void set_brightness_delayed(struct work_struct *ws) +{ + struct led_classdev *led_cdev = + container_of(ws, struct led_classdev, set_brightness_work); + + led_stop_software_blink(led_cdev); + + __led_set_brightness(led_cdev, led_cdev->delayed_set_value); +} + /** * led_classdev_suspend - suspend an led_classdev. * @led_cdev: the led_classdev to suspend. @@ -191,6 +201,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_update_brightness(led_cdev); + INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); + init_timer(&led_cdev->blink_timer); led_cdev->blink_timer.function = led_timer_function; led_cdev->blink_timer.data = (unsigned long)led_cdev; @@ -221,7 +233,10 @@ void led_classdev_unregister(struct led_classdev *led_cdev) up_write(&led_cdev->trigger_lock); #endif + cancel_work_sync(&led_cdev->set_brightness_work); + /* Stop blinking */ + led_stop_software_blink(led_cdev); led_set_brightness(led_cdev, LED_OFF); device_unregister(led_cdev->dev); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 2ab05af3de31..ce8921a753a3 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -103,13 +103,23 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, } EXPORT_SYMBOL(led_blink_set_oneshot); -void led_set_brightness(struct led_classdev *led_cdev, - enum led_brightness brightness) +void led_stop_software_blink(struct led_classdev *led_cdev) { - /* stop and clear soft-blink timer */ del_timer_sync(&led_cdev->blink_timer); led_cdev->blink_delay_on = 0; led_cdev->blink_delay_off = 0; +} +EXPORT_SYMBOL_GPL(led_stop_software_blink); + +void led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + /* delay brightness setting if need to stop soft-blink timer */ + if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { + led_cdev->delayed_set_value = brightness; + schedule_work(&led_cdev->set_brightness_work); + return; + } __led_set_brightness(led_cdev, brightness); } diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 363975b3c925..262eb4193710 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -102,6 +102,12 @@ EXPORT_SYMBOL_GPL(led_trigger_show); void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) { unsigned long flags; + char *event = NULL; + char *envp[2]; + const char *name; + + name = trig ? trig->name : "none"; + event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name); /* Remove any existing trigger */ if (led_cdev->trigger) { @@ -109,6 +115,8 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) list_del(&led_cdev->trig_list); write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags); + cancel_work_sync(&led_cdev->set_brightness_work); + led_stop_software_blink(led_cdev); if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); led_cdev->trigger = NULL; @@ -122,6 +130,13 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) if (trig->activate) trig->activate(led_cdev); } + + if (event) { + envp[0] = event; + envp[1] = NULL; + kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp); + kfree(event); + } } EXPORT_SYMBOL_GPL(led_trigger_set); @@ -224,7 +239,7 @@ void led_trigger_event(struct led_trigger *trig, struct led_classdev *led_cdev; led_cdev = list_entry(entry, struct led_classdev, trig_list); - __led_set_brightness(led_cdev, brightness); + led_set_brightness(led_cdev, brightness); } read_unlock(&trig->leddev_list_lock); } diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 1ed1677c916f..e024b0b1c3b1 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -31,7 +31,7 @@ static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id) } /* - * struct mail_led_whitelist - List of known good models + * struct clevo_mail_led_dmi_table - List of known good models * * Contains the known good models this driver is compatible with. * When adding a new model try to be as strict as possible. This @@ -39,7 +39,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 mail_led_whitelist[] = { +static struct dmi_system_id __initdata clevo_mail_led_dmi_table[] = { { .callback = clevo_mail_led_dmi_callback, .ident = "Clevo D410J", @@ -59,11 +59,10 @@ static struct dmi_system_id __initdata mail_led_whitelist[] = { }, { .callback = clevo_mail_led_dmi_callback, - .ident = "Positivo Mobile", + .ident = "Clevo M5x0V", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "), DMI_MATCH(DMI_BOARD_NAME, "M5X0V "), - DMI_MATCH(DMI_PRODUCT_NAME, "Positivo Mobile"), DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198") } }, @@ -89,6 +88,7 @@ static struct dmi_system_id __initdata mail_led_whitelist[] = { }, { } }; +MODULE_DEVICE_TABLE(dmi, clevo_mail_led_dmi_table); static void clevo_mail_led_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -180,7 +180,7 @@ static int __init clevo_mail_led_init(void) /* Check with the help of DMI if we are running on supported hardware */ if (!nodetect) { - count = dmi_check_system(mail_led_whitelist); + count = dmi_check_system(clevo_mail_led_dmi_table); } else { count = 1; printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. " diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index c032b2180340..087d1e66f4f7 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -20,6 +20,7 @@ #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/module.h> +#include <linux/pinctrl/consumer.h> struct gpio_led_data { struct led_classdev cdev; @@ -170,11 +171,10 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev { struct device_node *np = pdev->dev.of_node, *child; struct gpio_leds_priv *priv; - int count = 0, ret; + int count, ret; /* count LEDs in this device, so we know how much to allocate */ - for_each_child_of_node(np, child) - count++; + count = of_get_child_count(np); if (!count) return NULL; @@ -228,7 +228,6 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev { return NULL; } -#define of_gpio_leds_match NULL #endif /* CONFIG_OF_GPIO */ @@ -236,8 +235,14 @@ static int __devinit gpio_led_probe(struct platform_device *pdev) { struct gpio_led_platform_data *pdata = pdev->dev.platform_data; struct gpio_leds_priv *priv; + struct pinctrl *pinctrl; int i, ret = 0; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + if (pdata && pdata->num_leds) { priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(pdata->num_leds), @@ -270,13 +275,13 @@ static int __devinit gpio_led_probe(struct platform_device *pdev) static int __devexit gpio_led_remove(struct platform_device *pdev) { - struct gpio_leds_priv *priv = dev_get_drvdata(&pdev->dev); + struct gpio_leds_priv *priv = platform_get_drvdata(pdev); int i; for (i = 0; i < priv->num_leds; i++) delete_gpio_led(&priv->leds[i]); - dev_set_drvdata(&pdev->dev, NULL); + platform_set_drvdata(pdev, NULL); return 0; } @@ -287,7 +292,7 @@ static struct platform_driver gpio_led_driver = { .driver = { .name = "leds-gpio", .owner = THIS_MODULE, - .of_match_table = of_gpio_leds_match, + .of_match_table = of_match_ptr(of_gpio_leds_match), }, }; diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 23637bdb275d..b26306f6724d 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -150,7 +150,7 @@ static int lm3530_get_mode_from_str(const char *str) if (sysfs_streq(str, mode_map[i].mode)) return mode_map[i].mode_val; - return -1; + return -EINVAL; } static void lm3530_als_configure(struct lm3530_platform_data *pdata, @@ -358,7 +358,7 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute mode = lm3530_get_mode_from_str(buf); if (mode < 0) { dev_err(dev, "Invalid mode\n"); - return -EINVAL; + return mode; } drvdata->mode = mode; @@ -416,7 +416,7 @@ static int __devinit lm3530_probe(struct i2c_client *client, i2c_set_clientdata(client, drvdata); - drvdata->regulator = regulator_get(&client->dev, "vin"); + drvdata->regulator = devm_regulator_get(&client->dev, "vin"); if (IS_ERR(drvdata->regulator)) { dev_err(&client->dev, "regulator get failed\n"); err = PTR_ERR(drvdata->regulator); @@ -429,15 +429,13 @@ static int __devinit lm3530_probe(struct i2c_client *client, if (err < 0) { dev_err(&client->dev, "Register Init failed: %d\n", err); - err = -ENODEV; - goto err_reg_init; + return err; } } err = led_classdev_register(&client->dev, &drvdata->led_dev); if (err < 0) { dev_err(&client->dev, "Register led class failed: %d\n", err); - err = -ENODEV; - goto err_class_register; + return err; } err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode); @@ -451,9 +449,6 @@ static int __devinit lm3530_probe(struct i2c_client *client, err_create_file: led_classdev_unregister(&drvdata->led_dev); -err_class_register: -err_reg_init: - regulator_put(drvdata->regulator); return err; } @@ -465,7 +460,6 @@ static int __devexit lm3530_remove(struct i2c_client *client) if (drvdata->enable) regulator_disable(drvdata->regulator); - regulator_put(drvdata->regulator); led_classdev_unregister(&drvdata->led_dev); return 0; } diff --git a/drivers/leds/leds-lm3556.c b/drivers/leds/leds-lm3556.c deleted file mode 100644 index 3062abd9a532..000000000000 --- a/drivers/leds/leds-lm3556.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03) - * Copyright (C) 2012 Texas Instruments - * - * 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. - * - * Please refer Documentation/leds/leds-lm3556.txt file. - */ -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/leds.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/fs.h> -#include <linux/regmap.h> -#include <linux/platform_data/leds-lm3556.h> - -#define REG_FILT_TIME (0x0) -#define REG_IVFM_MODE (0x1) -#define REG_NTC (0x2) -#define REG_INDIC_TIME (0x3) -#define REG_INDIC_BLINK (0x4) -#define REG_INDIC_PERIOD (0x5) -#define REG_TORCH_TIME (0x6) -#define REG_CONF (0x7) -#define REG_FLASH (0x8) -#define REG_I_CTRL (0x9) -#define REG_ENABLE (0xA) -#define REG_FLAG (0xB) -#define REG_MAX (0xB) - -#define IVFM_FILTER_TIME_SHIFT (3) -#define UVLO_EN_SHIFT (7) -#define HYSTERSIS_SHIFT (5) -#define IVM_D_TH_SHIFT (2) -#define IVFM_ADJ_MODE_SHIFT (0) -#define NTC_EVENT_LVL_SHIFT (5) -#define NTC_TRIP_TH_SHIFT (2) -#define NTC_BIAS_I_LVL_SHIFT (0) -#define INDIC_RAMP_UP_TIME_SHIFT (3) -#define INDIC_RAMP_DN_TIME_SHIFT (0) -#define INDIC_N_BLANK_SHIFT (4) -#define INDIC_PULSE_TIME_SHIFT (0) -#define INDIC_N_PERIOD_SHIFT (0) -#define TORCH_RAMP_UP_TIME_SHIFT (3) -#define TORCH_RAMP_DN_TIME_SHIFT (0) -#define STROBE_USUAGE_SHIFT (7) -#define STROBE_PIN_POLARITY_SHIFT (6) -#define TORCH_PIN_POLARITY_SHIFT (5) -#define TX_PIN_POLARITY_SHIFT (4) -#define TX_EVENT_LVL_SHIFT (3) -#define IVFM_EN_SHIFT (2) -#define NTC_MODE_SHIFT (1) -#define INDIC_MODE_SHIFT (0) -#define INDUCTOR_I_LIMIT_SHIFT (6) -#define FLASH_RAMP_TIME_SHIFT (3) -#define FLASH_TOUT_TIME_SHIFT (0) -#define TORCH_I_SHIFT (4) -#define FLASH_I_SHIFT (0) -#define NTC_EN_SHIFT (7) -#define TX_PIN_EN_SHIFT (6) -#define STROBE_PIN_EN_SHIFT (5) -#define TORCH_PIN_EN_SHIFT (4) -#define PRECHG_MODE_EN_SHIFT (3) -#define PASS_MODE_ONLY_EN_SHIFT (2) -#define MODE_BITS_SHIFT (0) - -#define IVFM_FILTER_TIME_MASK (0x3) -#define UVLO_EN_MASK (0x1) -#define HYSTERSIS_MASK (0x3) -#define IVM_D_TH_MASK (0x7) -#define IVFM_ADJ_MODE_MASK (0x3) -#define NTC_EVENT_LVL_MASK (0x1) -#define NTC_TRIP_TH_MASK (0x7) -#define NTC_BIAS_I_LVL_MASK (0x3) -#define INDIC_RAMP_UP_TIME_MASK (0x7) -#define INDIC_RAMP_DN_TIME_MASK (0x7) -#define INDIC_N_BLANK_MASK (0x7) -#define INDIC_PULSE_TIME_MASK (0x7) -#define INDIC_N_PERIOD_MASK (0x7) -#define TORCH_RAMP_UP_TIME_MASK (0x7) -#define TORCH_RAMP_DN_TIME_MASK (0x7) -#define STROBE_USUAGE_MASK (0x1) -#define STROBE_PIN_POLARITY_MASK (0x1) -#define TORCH_PIN_POLARITY_MASK (0x1) -#define TX_PIN_POLARITY_MASK (0x1) -#define TX_EVENT_LVL_MASK (0x1) -#define IVFM_EN_MASK (0x1) -#define NTC_MODE_MASK (0x1) -#define INDIC_MODE_MASK (0x1) -#define INDUCTOR_I_LIMIT_MASK (0x3) -#define FLASH_RAMP_TIME_MASK (0x7) -#define FLASH_TOUT_TIME_MASK (0x7) -#define TORCH_I_MASK (0x7) -#define FLASH_I_MASK (0xF) -#define NTC_EN_MASK (0x1) -#define TX_PIN_EN_MASK (0x1) -#define STROBE_PIN_EN_MASK (0x1) -#define TORCH_PIN_EN_MASK (0x1) -#define PRECHG_MODE_EN_MASK (0x1) -#define PASS_MODE_ONLY_EN_MASK (0x1) -#define MODE_BITS_MASK (0x13) -#define EX_PIN_CONTROL_MASK (0xF1) -#define EX_PIN_ENABLE_MASK (0x70) - -enum lm3556_indic_pulse_time { - PULSE_TIME_0_MS = 0, - PULSE_TIME_32_MS, - PULSE_TIME_64_MS, - PULSE_TIME_92_MS, - PULSE_TIME_128_MS, - PULSE_TIME_160_MS, - PULSE_TIME_196_MS, - PULSE_TIME_224_MS, - PULSE_TIME_256_MS, - PULSE_TIME_288_MS, - PULSE_TIME_320_MS, - PULSE_TIME_352_MS, - PULSE_TIME_384_MS, - PULSE_TIME_416_MS, - PULSE_TIME_448_MS, - PULSE_TIME_480_MS, -}; - -enum lm3556_indic_n_blank { - INDIC_N_BLANK_0 = 0, - INDIC_N_BLANK_1, - INDIC_N_BLANK_2, - INDIC_N_BLANK_3, - INDIC_N_BLANK_4, - INDIC_N_BLANK_5, - INDIC_N_BLANK_6, - INDIC_N_BLANK_7, - INDIC_N_BLANK_8, - INDIC_N_BLANK_9, - INDIC_N_BLANK_10, - INDIC_N_BLANK_11, - INDIC_N_BLANK_12, - INDIC_N_BLANK_13, - INDIC_N_BLANK_14, - INDIC_N_BLANK_15, -}; - -enum lm3556_indic_period { - INDIC_PERIOD_0 = 0, - INDIC_PERIOD_1, - INDIC_PERIOD_2, - INDIC_PERIOD_3, - INDIC_PERIOD_4, - INDIC_PERIOD_5, - INDIC_PERIOD_6, - INDIC_PERIOD_7, -}; - -enum lm3556_mode { - MODES_STASNDBY = 0, - MODES_INDIC, - MODES_TORCH, - MODES_FLASH -}; - -#define INDIC_PATTERN_SIZE 4 - -struct indicator { - u8 blinking; - u8 period_cnt; -}; - -struct lm3556_chip_data { - struct device *dev; - - struct led_classdev cdev_flash; - struct led_classdev cdev_torch; - struct led_classdev cdev_indicator; - - struct lm3556_platform_data *pdata; - struct regmap *regmap; - struct mutex lock; - - unsigned int last_flag; -}; - -/* indicator pattern */ -static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { - [0] = {(INDIC_N_BLANK_1 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_1}, - [1] = {(INDIC_N_BLANK_15 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_2}, - [2] = {(INDIC_N_BLANK_10 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_4}, - [3] = {(INDIC_N_BLANK_5 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_7}, -}; - -/* chip initialize */ -static int __devinit lm3556_chip_init(struct lm3556_chip_data *chip) -{ - unsigned int reg_val; - int ret; - struct lm3556_platform_data *pdata = chip->pdata; - - /* set config register */ - ret = regmap_read(chip->regmap, REG_CONF, ®_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to read REG_CONF Register\n"); - goto out; - } - - reg_val &= (~EX_PIN_CONTROL_MASK); - reg_val |= ((pdata->torch_pin_polarity & 0x01) - << TORCH_PIN_POLARITY_SHIFT); - reg_val |= ((pdata->strobe_usuage & 0x01) << STROBE_USUAGE_SHIFT); - reg_val |= ((pdata->strobe_pin_polarity & 0x01) - << STROBE_PIN_POLARITY_SHIFT); - reg_val |= ((pdata->tx_pin_polarity & 0x01) << TX_PIN_POLARITY_SHIFT); - reg_val |= ((pdata->indicator_mode & 0x01) << INDIC_MODE_SHIFT); - - ret = regmap_write(chip->regmap, REG_CONF, reg_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_CONF Regisgter\n"); - goto out; - } - - /* set enable register */ - ret = regmap_read(chip->regmap, REG_ENABLE, ®_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to read REG_ENABLE Register\n"); - goto out; - } - - reg_val &= (~EX_PIN_ENABLE_MASK); - reg_val |= ((pdata->torch_pin_en & 0x01) << TORCH_PIN_EN_SHIFT); - reg_val |= ((pdata->strobe_pin_en & 0x01) << STROBE_PIN_EN_SHIFT); - reg_val |= ((pdata->tx_pin_en & 0x01) << TX_PIN_EN_SHIFT); - - ret = regmap_write(chip->regmap, REG_ENABLE, reg_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n"); - goto out; - } - -out: - return ret; -} - -/* chip control */ -static int lm3556_control(struct lm3556_chip_data *chip, - u8 brightness, enum lm3556_mode opmode) -{ - int ret; - struct lm3556_platform_data *pdata = chip->pdata; - - ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); - if (ret < 0) { - dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); - goto out; - } - - if (chip->last_flag) - dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag); - - /* brightness 0 means off-state */ - if (!brightness) - opmode = MODES_STASNDBY; - - switch (opmode) { - case MODES_TORCH: - ret = regmap_update_bits(chip->regmap, REG_I_CTRL, - TORCH_I_MASK << TORCH_I_SHIFT, - (brightness - 1) << TORCH_I_SHIFT); - - if (pdata->torch_pin_en) - opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); - break; - - case MODES_FLASH: - ret = regmap_update_bits(chip->regmap, REG_I_CTRL, - FLASH_I_MASK << FLASH_I_SHIFT, - (brightness - 1) << FLASH_I_SHIFT); - break; - - case MODES_INDIC: - ret = regmap_update_bits(chip->regmap, REG_I_CTRL, - TORCH_I_MASK << TORCH_I_SHIFT, - (brightness - 1) << TORCH_I_SHIFT); - break; - - case MODES_STASNDBY: - if (pdata->torch_pin_en) - opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); - break; - - default: - return ret; - } - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); - goto out; - } - ret = regmap_update_bits(chip->regmap, REG_ENABLE, - MODE_BITS_MASK << MODE_BITS_SHIFT, - opmode << MODE_BITS_SHIFT); - -out: - return ret; -} - -/* torch */ -static void lm3556_torch_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lm3556_chip_data *chip = - container_of(cdev, struct lm3556_chip_data, cdev_torch); - - mutex_lock(&chip->lock); - lm3556_control(chip, brightness, MODES_TORCH); - mutex_unlock(&chip->lock); -} - -/* flash */ -static void lm3556_strobe_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lm3556_chip_data *chip = - container_of(cdev, struct lm3556_chip_data, cdev_flash); - - mutex_lock(&chip->lock); - lm3556_control(chip, brightness, MODES_FLASH); - mutex_unlock(&chip->lock); -} - -/* indicator */ -static void lm3556_indicator_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lm3556_chip_data *chip = - container_of(cdev, struct lm3556_chip_data, cdev_indicator); - - mutex_lock(&chip->lock); - lm3556_control(chip, brightness, MODES_INDIC); - mutex_unlock(&chip->lock); -} - -/* indicator pattern */ -static ssize_t lm3556_indicator_pattern_store(struct device *dev, - struct device_attribute *devAttr, - const char *buf, size_t size) -{ - ssize_t ret; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lm3556_chip_data *chip = - container_of(led_cdev, struct lm3556_chip_data, cdev_indicator); - unsigned int state; - - ret = kstrtouint(buf, 10, &state); - if (ret) - goto out; - if (state > INDIC_PATTERN_SIZE - 1) - state = INDIC_PATTERN_SIZE - 1; - - ret = regmap_write(chip->regmap, REG_INDIC_BLINK, - indicator_pattern[state].blinking); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n"); - goto out; - } - - ret = regmap_write(chip->regmap, REG_INDIC_PERIOD, - indicator_pattern[state].period_cnt); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n"); - goto out; - } - - return size; -out: - dev_err(chip->dev, "Indicator pattern doesn't saved\n"); - return size; -} - -static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store); - -static const struct regmap_config lm3556_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = REG_MAX, -}; - -/* module initialize */ -static int __devinit lm3556_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct lm3556_platform_data *pdata = client->dev.platform_data; - struct lm3556_chip_data *chip; - - int err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "i2c functionality check fail.\n"); - return -EOPNOTSUPP; - } - - if (pdata == NULL) { - dev_err(&client->dev, "Needs Platform Data.\n"); - return -ENODATA; - } - - chip = - devm_kzalloc(&client->dev, sizeof(struct lm3556_chip_data), - GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->dev = &client->dev; - chip->pdata = pdata; - - chip->regmap = devm_regmap_init_i2c(client, &lm3556_regmap); - if (IS_ERR(chip->regmap)) { - err = PTR_ERR(chip->regmap); - dev_err(&client->dev, "Failed to allocate register map: %d\n", - err); - return err; - } - - mutex_init(&chip->lock); - i2c_set_clientdata(client, chip); - - err = lm3556_chip_init(chip); - if (err < 0) - goto err_out; - - /* flash */ - chip->cdev_flash.name = "flash"; - chip->cdev_flash.max_brightness = 16; - chip->cdev_flash.brightness_set = lm3556_strobe_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_flash); - if (err < 0) - goto err_out; - /* torch */ - chip->cdev_torch.name = "torch"; - chip->cdev_torch.max_brightness = 8; - chip->cdev_torch.brightness_set = lm3556_torch_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_torch); - if (err < 0) - goto err_create_torch_file; - /* indicator */ - chip->cdev_indicator.name = "indicator"; - chip->cdev_indicator.max_brightness = 8; - chip->cdev_indicator.brightness_set = lm3556_indicator_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_indicator); - if (err < 0) - goto err_create_indicator_file; - - err = device_create_file(chip->cdev_indicator.dev, &dev_attr_pattern); - if (err < 0) - goto err_create_pattern_file; - - dev_info(&client->dev, "LM3556 is initialized\n"); - return 0; - -err_create_pattern_file: - led_classdev_unregister(&chip->cdev_indicator); -err_create_indicator_file: - led_classdev_unregister(&chip->cdev_torch); -err_create_torch_file: - led_classdev_unregister(&chip->cdev_flash); -err_out: - return err; -} - -static int __devexit lm3556_remove(struct i2c_client *client) -{ - struct lm3556_chip_data *chip = i2c_get_clientdata(client); - - device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); - led_classdev_unregister(&chip->cdev_indicator); - led_classdev_unregister(&chip->cdev_torch); - led_classdev_unregister(&chip->cdev_flash); - regmap_write(chip->regmap, REG_ENABLE, 0); - return 0; -} - -static const struct i2c_device_id lm3556_id[] = { - {LM3556_NAME, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, lm3556_id); - -static struct i2c_driver lm3556_i2c_driver = { - .driver = { - .name = LM3556_NAME, - .owner = THIS_MODULE, - .pm = NULL, - }, - .probe = lm3556_probe, - .remove = __devexit_p(lm3556_remove), - .id_table = lm3556_id, -}; - -module_i2c_driver(lm3556_i2c_driver); - -MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3556"); -MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); -MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c new file mode 100644 index 000000000000..065ec015d67a --- /dev/null +++ b/drivers/leds/leds-lm355x.c @@ -0,0 +1,572 @@ +/* +* Simple driver for Texas Instruments LM355x LED Flash driver chip +* Copyright (C) 2012 Texas Instruments +* +* 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/delay.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/leds.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/fs.h> +#include <linux/regmap.h> +#include <linux/workqueue.h> +#include <linux/platform_data/leds-lm355x.h> + +enum lm355x_type { + CHIP_LM3554 = 0, + CHIP_LM3556, +}; + +enum lm355x_regs { + REG_FLAG = 0, + REG_TORCH_CFG, + REG_TORCH_CTRL, + REG_STROBE_CFG, + REG_FLASH_CTRL, + REG_INDI_CFG, + REG_INDI_CTRL, + REG_OPMODE, + REG_MAX, +}; + +/* operation mode */ +enum lm355x_mode { + MODE_SHDN = 0, + MODE_INDIC, + MODE_TORCH, + MODE_FLASH +}; + +/* register map info. */ +struct lm355x_reg_data { + u8 regno; + u8 mask; + u8 shift; +}; + +struct lm355x_chip_data { + struct device *dev; + enum lm355x_type type; + + struct led_classdev cdev_flash; + struct led_classdev cdev_torch; + struct led_classdev cdev_indicator; + + struct work_struct work_flash; + struct work_struct work_torch; + struct work_struct work_indicator; + + u8 br_flash; + u8 br_torch; + u8 br_indicator; + + struct lm355x_platform_data *pdata; + struct regmap *regmap; + struct mutex lock; + + unsigned int last_flag; + struct lm355x_reg_data *regs; +}; + +/* specific indicator function for lm3556 */ +enum lm3556_indic_pulse_time { + PULSE_TIME_0_MS = 0, + PULSE_TIME_32_MS, + PULSE_TIME_64_MS, + PULSE_TIME_92_MS, + PULSE_TIME_128_MS, + PULSE_TIME_160_MS, + PULSE_TIME_196_MS, + PULSE_TIME_224_MS, + PULSE_TIME_256_MS, + PULSE_TIME_288_MS, + PULSE_TIME_320_MS, + PULSE_TIME_352_MS, + PULSE_TIME_384_MS, + PULSE_TIME_416_MS, + PULSE_TIME_448_MS, + PULSE_TIME_480_MS, +}; + +enum lm3556_indic_n_blank { + INDIC_N_BLANK_0 = 0, + INDIC_N_BLANK_1, + INDIC_N_BLANK_2, + INDIC_N_BLANK_3, + INDIC_N_BLANK_4, + INDIC_N_BLANK_5, + INDIC_N_BLANK_6, + INDIC_N_BLANK_7, + INDIC_N_BLANK_8, + INDIC_N_BLANK_9, + INDIC_N_BLANK_10, + INDIC_N_BLANK_11, + INDIC_N_BLANK_12, + INDIC_N_BLANK_13, + INDIC_N_BLANK_14, + INDIC_N_BLANK_15, +}; + +enum lm3556_indic_period { + INDIC_PERIOD_0 = 0, + INDIC_PERIOD_1, + INDIC_PERIOD_2, + INDIC_PERIOD_3, + INDIC_PERIOD_4, + INDIC_PERIOD_5, + INDIC_PERIOD_6, + INDIC_PERIOD_7, +}; + +#define INDIC_PATTERN_SIZE 4 + +struct indicator { + u8 blinking; + u8 period_cnt; +}; + +/* indicator pattern data only for lm3556 */ +static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { + [0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1}, + [1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2}, + [2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4}, + [3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7}, +}; + +static struct lm355x_reg_data lm3554_regs[REG_MAX] = { + [REG_FLAG] = {0xD0, 0xBF, 0}, + [REG_TORCH_CFG] = {0xE0, 0x80, 7}, + [REG_TORCH_CTRL] = {0xA0, 0x38, 3}, + [REG_STROBE_CFG] = {0xE0, 0x04, 2}, + [REG_FLASH_CTRL] = {0xB0, 0x78, 3}, + [REG_INDI_CFG] = {0xE0, 0x08, 3}, + [REG_INDI_CTRL] = {0xA0, 0xC0, 6}, + [REG_OPMODE] = {0xA0, 0x03, 0}, +}; + +static struct lm355x_reg_data lm3556_regs[REG_MAX] = { + [REG_FLAG] = {0x0B, 0xFF, 0}, + [REG_TORCH_CFG] = {0x0A, 0x10, 4}, + [REG_TORCH_CTRL] = {0x09, 0x70, 4}, + [REG_STROBE_CFG] = {0x0A, 0x20, 5}, + [REG_FLASH_CTRL] = {0x09, 0x0F, 0}, + [REG_INDI_CFG] = {0xFF, 0xFF, 0}, + [REG_INDI_CTRL] = {0x09, 0x70, 4}, + [REG_OPMODE] = {0x0A, 0x03, 0}, +}; + +static char lm355x_name[][I2C_NAME_SIZE] = { + [CHIP_LM3554] = LM3554_NAME, + [CHIP_LM3556] = LM3556_NAME, +}; + +/* chip initialize */ +static int __devinit lm355x_chip_init(struct lm355x_chip_data *chip) +{ + int ret; + unsigned int reg_val; + struct lm355x_platform_data *pdata = chip->pdata; + + /* input and output pins configuration */ + switch (chip->type) { + case CHIP_LM3554: + reg_val = pdata->pin_tx2 | pdata->ntc_pin; + ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); + if (ret < 0) + goto out; + reg_val = pdata->pass_mode; + ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); + if (ret < 0) + goto out; + break; + + case CHIP_LM3556: + reg_val = pdata->pin_tx2 | pdata->ntc_pin | pdata->pass_mode; + ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); + if (ret < 0) + goto out; + break; + default: + return -ENODATA; + } + + return ret; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return ret; +} + +/* chip control */ +static void lm355x_control(struct lm355x_chip_data *chip, + u8 brightness, enum lm355x_mode opmode) +{ + int ret; + unsigned int reg_val; + struct lm355x_platform_data *pdata = chip->pdata; + struct lm355x_reg_data *preg = chip->regs; + + ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag); + if (ret < 0) + goto out; + if (chip->last_flag & preg[REG_FLAG].mask) + dev_info(chip->dev, "%s Last FLAG is 0x%x\n", + lm355x_name[chip->type], + chip->last_flag & preg[REG_FLAG].mask); + /* brightness 0 means shutdown */ + if (!brightness) + opmode = MODE_SHDN; + + switch (opmode) { + case MODE_TORCH: + ret = + regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno, + preg[REG_TORCH_CTRL].mask, + (brightness - 1) + << preg[REG_TORCH_CTRL].shift); + if (ret < 0) + goto out; + + if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) { + ret = + regmap_update_bits(chip->regmap, + preg[REG_TORCH_CFG].regno, + preg[REG_TORCH_CFG].mask, + 0x01 << + preg[REG_TORCH_CFG].shift); + if (ret < 0) + goto out; + opmode = MODE_SHDN; + dev_info(chip->dev, + "torch brt is set - ext. torch pin mode\n"); + } + break; + + case MODE_FLASH: + + ret = + regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno, + preg[REG_FLASH_CTRL].mask, + (brightness - 1) + << preg[REG_FLASH_CTRL].shift); + if (ret < 0) + goto out; + + if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) { + if (chip->type == CHIP_LM3554) + reg_val = 0x00; + else + reg_val = 0x01; + ret = + regmap_update_bits(chip->regmap, + preg[REG_STROBE_CFG].regno, + preg[REG_STROBE_CFG].mask, + reg_val << + preg[REG_STROBE_CFG].shift); + if (ret < 0) + goto out; + opmode = MODE_SHDN; + dev_info(chip->dev, + "flash brt is set - ext. strobe pin mode\n"); + } + break; + + case MODE_INDIC: + ret = + regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno, + preg[REG_INDI_CTRL].mask, + (brightness - 1) + << preg[REG_INDI_CTRL].shift); + if (ret < 0) + goto out; + + if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) { + ret = + regmap_update_bits(chip->regmap, + preg[REG_INDI_CFG].regno, + preg[REG_INDI_CFG].mask, + 0x01 << + preg[REG_INDI_CFG].shift); + if (ret < 0) + goto out; + opmode = MODE_SHDN; + } + break; + case MODE_SHDN: + break; + default: + return; + } + /* operation mode control */ + ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, + preg[REG_OPMODE].mask, + opmode << preg[REG_OPMODE].shift); + if (ret < 0) + goto out; + return; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return; +} + +/* torch */ +static void lm355x_deferred_torch_brightness_set(struct work_struct *work) +{ + struct lm355x_chip_data *chip = + container_of(work, struct lm355x_chip_data, work_torch); + + mutex_lock(&chip->lock); + lm355x_control(chip, chip->br_torch, MODE_TORCH); + mutex_unlock(&chip->lock); +} + +static void lm355x_torch_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm355x_chip_data *chip = + container_of(cdev, struct lm355x_chip_data, cdev_torch); + + chip->br_torch = brightness; + schedule_work(&chip->work_torch); +} + +/* flash */ +static void lm355x_deferred_strobe_brightness_set(struct work_struct *work) +{ + struct lm355x_chip_data *chip = + container_of(work, struct lm355x_chip_data, work_flash); + + mutex_lock(&chip->lock); + lm355x_control(chip, chip->br_flash, MODE_FLASH); + mutex_unlock(&chip->lock); +} + +static void lm355x_strobe_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm355x_chip_data *chip = + container_of(cdev, struct lm355x_chip_data, cdev_flash); + + chip->br_flash = brightness; + schedule_work(&chip->work_flash); +} + +/* indicator */ +static void lm355x_deferred_indicator_brightness_set(struct work_struct *work) +{ + struct lm355x_chip_data *chip = + container_of(work, struct lm355x_chip_data, work_indicator); + + mutex_lock(&chip->lock); + lm355x_control(chip, chip->br_indicator, MODE_INDIC); + mutex_unlock(&chip->lock); +} + +static void lm355x_indicator_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm355x_chip_data *chip = + container_of(cdev, struct lm355x_chip_data, cdev_indicator); + + chip->br_indicator = brightness; + schedule_work(&chip->work_indicator); +} + +/* indicator pattern only for lm3556*/ +static ssize_t lm3556_indicator_pattern_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm355x_chip_data *chip = + container_of(led_cdev, struct lm355x_chip_data, cdev_indicator); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out; + if (state > INDIC_PATTERN_SIZE - 1) + state = INDIC_PATTERN_SIZE - 1; + + ret = regmap_write(chip->regmap, 0x04, + indicator_pattern[state].blinking); + if (ret < 0) + goto out; + + ret = regmap_write(chip->regmap, 0x05, + indicator_pattern[state].period_cnt); + if (ret < 0) + goto out; + + return size; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return size; +} + +static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store); + +static const struct regmap_config lm355x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, +}; + +/* module initialize */ +static int __devinit lm355x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm355x_platform_data *pdata = client->dev.platform_data; + struct lm355x_chip_data *chip; + + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c functionality check fail.\n"); + return -EOPNOTSUPP; + } + + if (pdata == NULL) { + dev_err(&client->dev, "needs Platform Data.\n"); + return -ENODATA; + } + + chip = devm_kzalloc(&client->dev, + sizeof(struct lm355x_chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + chip->type = id->driver_data; + switch (id->driver_data) { + case CHIP_LM3554: + chip->regs = lm3554_regs; + break; + case CHIP_LM3556: + chip->regs = lm3556_regs; + break; + default: + return -ENOSYS; + } + chip->pdata = pdata; + + chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap); + if (IS_ERR(chip->regmap)) { + err = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", err); + return err; + } + + mutex_init(&chip->lock); + i2c_set_clientdata(client, chip); + + err = lm355x_chip_init(chip); + if (err < 0) + goto err_out; + + /* flash */ + INIT_WORK(&chip->work_flash, lm355x_deferred_strobe_brightness_set); + chip->cdev_flash.name = "flash"; + chip->cdev_flash.max_brightness = 16; + chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_flash); + if (err < 0) + goto err_out; + /* torch */ + INIT_WORK(&chip->work_torch, lm355x_deferred_torch_brightness_set); + chip->cdev_torch.name = "torch"; + chip->cdev_torch.max_brightness = 8; + chip->cdev_torch.brightness_set = lm355x_torch_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_torch); + if (err < 0) + goto err_create_torch_file; + /* indicator */ + INIT_WORK(&chip->work_indicator, + lm355x_deferred_indicator_brightness_set); + chip->cdev_indicator.name = "indicator"; + if (id->driver_data == CHIP_LM3554) + chip->cdev_indicator.max_brightness = 4; + else + chip->cdev_indicator.max_brightness = 8; + chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_indicator); + if (err < 0) + goto err_create_indicator_file; + /* indicator pattern control only for LM3554 */ + if (id->driver_data == CHIP_LM3556) { + err = + device_create_file(chip->cdev_indicator.dev, + &dev_attr_pattern); + if (err < 0) + goto err_create_pattern_file; + } + + dev_info(&client->dev, "%s is initialized\n", + lm355x_name[id->driver_data]); + return 0; + +err_create_pattern_file: + led_classdev_unregister(&chip->cdev_indicator); +err_create_indicator_file: + led_classdev_unregister(&chip->cdev_torch); +err_create_torch_file: + led_classdev_unregister(&chip->cdev_flash); +err_out: + return err; +} + +static int __devexit lm355x_remove(struct i2c_client *client) +{ + struct lm355x_chip_data *chip = i2c_get_clientdata(client); + struct lm355x_reg_data *preg = chip->regs; + + regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); + if (chip->type == CHIP_LM3556) + device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); + led_classdev_unregister(&chip->cdev_indicator); + flush_work(&chip->work_indicator); + led_classdev_unregister(&chip->cdev_torch); + flush_work(&chip->work_torch); + led_classdev_unregister(&chip->cdev_flash); + flush_work(&chip->work_flash); + dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); + + return 0; +} + +static const struct i2c_device_id lm355x_id[] = { + {LM3554_NAME, CHIP_LM3554}, + {LM3556_NAME, CHIP_LM3556}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm355x_id); + +static struct i2c_driver lm355x_i2c_driver = { + .driver = { + .name = LM355x_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, + .probe = lm355x_probe, + .remove = __devexit_p(lm355x_remove), + .id_table = lm355x_id, +}; + +module_i2c_driver(lm355x_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x"); +MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); +MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c new file mode 100644 index 000000000000..3285006e9888 --- /dev/null +++ b/drivers/leds/leds-lm3642.c @@ -0,0 +1,462 @@ +/* +* Simple driver for Texas Instruments LM3642 LED Flash driver chip +* Copyright (C) 2012 Texas Instruments +* +* 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/delay.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/fs.h> +#include <linux/regmap.h> +#include <linux/workqueue.h> +#include <linux/platform_data/leds-lm3642.h> + +#define REG_FILT_TIME (0x0) +#define REG_IVFM_MODE (0x1) +#define REG_TORCH_TIME (0x6) +#define REG_FLASH (0x8) +#define REG_I_CTRL (0x9) +#define REG_ENABLE (0xA) +#define REG_FLAG (0xB) +#define REG_MAX (0xB) + +#define UVLO_EN_SHIFT (7) +#define IVM_D_TH_SHIFT (2) +#define TORCH_RAMP_UP_TIME_SHIFT (3) +#define TORCH_RAMP_DN_TIME_SHIFT (0) +#define INDUCTOR_I_LIMIT_SHIFT (6) +#define FLASH_RAMP_TIME_SHIFT (3) +#define FLASH_TOUT_TIME_SHIFT (0) +#define TORCH_I_SHIFT (4) +#define FLASH_I_SHIFT (0) +#define IVFM_SHIFT (7) +#define TX_PIN_EN_SHIFT (6) +#define STROBE_PIN_EN_SHIFT (5) +#define TORCH_PIN_EN_SHIFT (4) +#define MODE_BITS_SHIFT (0) + +#define UVLO_EN_MASK (0x1) +#define IVM_D_TH_MASK (0x7) +#define TORCH_RAMP_UP_TIME_MASK (0x7) +#define TORCH_RAMP_DN_TIME_MASK (0x7) +#define INDUCTOR_I_LIMIT_MASK (0x1) +#define FLASH_RAMP_TIME_MASK (0x7) +#define FLASH_TOUT_TIME_MASK (0x7) +#define TORCH_I_MASK (0x7) +#define FLASH_I_MASK (0xF) +#define IVFM_MASK (0x1) +#define TX_PIN_EN_MASK (0x1) +#define STROBE_PIN_EN_MASK (0x1) +#define TORCH_PIN_EN_MASK (0x1) +#define MODE_BITS_MASK (0x73) +#define EX_PIN_CONTROL_MASK (0x71) +#define EX_PIN_ENABLE_MASK (0x70) + +enum lm3642_mode { + MODES_STASNDBY = 0, + MODES_INDIC, + MODES_TORCH, + MODES_FLASH +}; + +struct lm3642_chip_data { + struct device *dev; + + struct led_classdev cdev_flash; + struct led_classdev cdev_torch; + struct led_classdev cdev_indicator; + + struct work_struct work_flash; + struct work_struct work_torch; + struct work_struct work_indicator; + + u8 br_flash; + u8 br_torch; + u8 br_indicator; + + enum lm3642_torch_pin_enable torch_pin; + enum lm3642_strobe_pin_enable strobe_pin; + enum lm3642_tx_pin_enable tx_pin; + + struct lm3642_platform_data *pdata; + struct regmap *regmap; + struct mutex lock; + + unsigned int last_flag; +}; + +/* chip initialize */ +static int __devinit lm3642_chip_init(struct lm3642_chip_data *chip) +{ + int ret; + struct lm3642_platform_data *pdata = chip->pdata; + + /* set enable register */ + ret = regmap_update_bits(chip->regmap, REG_ENABLE, EX_PIN_ENABLE_MASK, + pdata->tx_pin); + if (ret < 0) + dev_err(chip->dev, "Failed to update REG_ENABLE Register\n"); + return ret; +} + +/* chip control */ +static int lm3642_control(struct lm3642_chip_data *chip, + u8 brightness, enum lm3642_mode opmode) +{ + int ret; + + ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); + if (ret < 0) { + dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); + goto out; + } + + if (chip->last_flag) + dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag); + + /* brightness 0 means off-state */ + if (!brightness) + opmode = MODES_STASNDBY; + + switch (opmode) { + case MODES_TORCH: + ret = regmap_update_bits(chip->regmap, REG_I_CTRL, + TORCH_I_MASK << TORCH_I_SHIFT, + (brightness - 1) << TORCH_I_SHIFT); + + if (chip->torch_pin) + opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); + break; + + case MODES_FLASH: + ret = regmap_update_bits(chip->regmap, REG_I_CTRL, + FLASH_I_MASK << FLASH_I_SHIFT, + (brightness - 1) << FLASH_I_SHIFT); + + if (chip->strobe_pin) + opmode |= (STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT); + break; + + case MODES_INDIC: + ret = regmap_update_bits(chip->regmap, REG_I_CTRL, + TORCH_I_MASK << TORCH_I_SHIFT, + (brightness - 1) << TORCH_I_SHIFT); + break; + + case MODES_STASNDBY: + + break; + + default: + return ret; + } + if (ret < 0) { + dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); + goto out; + } + + if (chip->tx_pin) + opmode |= (TX_PIN_EN_MASK << TX_PIN_EN_SHIFT); + + ret = regmap_update_bits(chip->regmap, REG_ENABLE, + MODE_BITS_MASK << MODE_BITS_SHIFT, + opmode << MODE_BITS_SHIFT); +out: + return ret; +} + +/* torch */ + +/* torch pin config for lm3642*/ +static ssize_t lm3642_torch_pin_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3642_chip_data *chip = + container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out_strtoint; + if (state != 0) + state = 0x01 << TORCH_PIN_EN_SHIFT; + + chip->torch_pin = state; + ret = regmap_update_bits(chip->regmap, REG_ENABLE, + TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, + state); + if (ret < 0) + goto out; + + return size; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return size; +out_strtoint: + dev_err(chip->dev, "%s: fail to change str to int\n", __func__); + return size; +} + +static DEVICE_ATTR(torch_pin, 0666, NULL, lm3642_torch_pin_store); + +static void lm3642_deferred_torch_brightness_set(struct work_struct *work) +{ + struct lm3642_chip_data *chip = + container_of(work, struct lm3642_chip_data, work_torch); + + mutex_lock(&chip->lock); + lm3642_control(chip, chip->br_torch, MODES_TORCH); + mutex_unlock(&chip->lock); +} + +static void lm3642_torch_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm3642_chip_data *chip = + container_of(cdev, struct lm3642_chip_data, cdev_torch); + + chip->br_torch = brightness; + schedule_work(&chip->work_torch); +} + +/* flash */ + +/* strobe pin config for lm3642*/ +static ssize_t lm3642_strobe_pin_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3642_chip_data *chip = + container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out_strtoint; + if (state != 0) + state = 0x01 << STROBE_PIN_EN_SHIFT; + + chip->strobe_pin = state; + ret = regmap_update_bits(chip->regmap, REG_ENABLE, + STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, + state); + if (ret < 0) + goto out; + + return size; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return size; +out_strtoint: + dev_err(chip->dev, "%s: fail to change str to int\n", __func__); + return size; +} + +static DEVICE_ATTR(strobe_pin, 0666, NULL, lm3642_strobe_pin_store); + +static void lm3642_deferred_strobe_brightness_set(struct work_struct *work) +{ + struct lm3642_chip_data *chip = + container_of(work, struct lm3642_chip_data, work_flash); + + mutex_lock(&chip->lock); + lm3642_control(chip, chip->br_flash, MODES_FLASH); + mutex_unlock(&chip->lock); +} + +static void lm3642_strobe_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm3642_chip_data *chip = + container_of(cdev, struct lm3642_chip_data, cdev_flash); + + chip->br_flash = brightness; + schedule_work(&chip->work_flash); +} + +/* indicator */ +static void lm3642_deferred_indicator_brightness_set(struct work_struct *work) +{ + struct lm3642_chip_data *chip = + container_of(work, struct lm3642_chip_data, work_indicator); + + mutex_lock(&chip->lock); + lm3642_control(chip, chip->br_indicator, MODES_INDIC); + mutex_unlock(&chip->lock); +} + +static void lm3642_indicator_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm3642_chip_data *chip = + container_of(cdev, struct lm3642_chip_data, cdev_indicator); + + chip->br_indicator = brightness; + schedule_work(&chip->work_indicator); +} + +static const struct regmap_config lm3642_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, +}; + +static int __devinit lm3642_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm3642_platform_data *pdata = client->dev.platform_data; + struct lm3642_chip_data *chip; + + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c functionality check fail.\n"); + return -EOPNOTSUPP; + } + + if (pdata == NULL) { + dev_err(&client->dev, "needs Platform Data.\n"); + return -ENODATA; + } + + chip = devm_kzalloc(&client->dev, + sizeof(struct lm3642_chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + chip->pdata = pdata; + + chip->tx_pin = pdata->tx_pin; + chip->torch_pin = pdata->torch_pin; + chip->strobe_pin = pdata->strobe_pin; + + chip->regmap = devm_regmap_init_i2c(client, &lm3642_regmap); + if (IS_ERR(chip->regmap)) { + err = PTR_ERR(chip->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + err); + return err; + } + + mutex_init(&chip->lock); + i2c_set_clientdata(client, chip); + + err = lm3642_chip_init(chip); + if (err < 0) + goto err_out; + + /* flash */ + INIT_WORK(&chip->work_flash, lm3642_deferred_strobe_brightness_set); + chip->cdev_flash.name = "flash"; + chip->cdev_flash.max_brightness = 16; + chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_flash); + if (err < 0) { + dev_err(chip->dev, "failed to register flash\n"); + goto err_out; + } + err = device_create_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); + if (err < 0) { + dev_err(chip->dev, "failed to create strobe-pin file\n"); + goto err_create_flash_pin_file; + } + + /* torch */ + INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set); + chip->cdev_torch.name = "torch"; + chip->cdev_torch.max_brightness = 8; + chip->cdev_torch.brightness_set = lm3642_torch_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_torch); + if (err < 0) { + dev_err(chip->dev, "failed to register torch\n"); + goto err_create_torch_file; + } + err = device_create_file(chip->cdev_torch.dev, &dev_attr_torch_pin); + if (err < 0) { + dev_err(chip->dev, "failed to create torch-pin file\n"); + goto err_create_torch_pin_file; + } + + /* indicator */ + INIT_WORK(&chip->work_indicator, + lm3642_deferred_indicator_brightness_set); + chip->cdev_indicator.name = "indicator"; + chip->cdev_indicator.max_brightness = 8; + chip->cdev_indicator.brightness_set = lm3642_indicator_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_indicator); + if (err < 0) { + dev_err(chip->dev, "failed to register indicator\n"); + goto err_create_indicator_file; + } + + dev_info(&client->dev, "LM3642 is initialized\n"); + return 0; + +err_create_indicator_file: + device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); +err_create_torch_pin_file: + led_classdev_unregister(&chip->cdev_torch); +err_create_torch_file: + device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); +err_create_flash_pin_file: + led_classdev_unregister(&chip->cdev_flash); +err_out: + return err; +} + +static int __devexit lm3642_remove(struct i2c_client *client) +{ + struct lm3642_chip_data *chip = i2c_get_clientdata(client); + + led_classdev_unregister(&chip->cdev_indicator); + flush_work(&chip->work_indicator); + device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); + led_classdev_unregister(&chip->cdev_torch); + flush_work(&chip->work_torch); + device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); + led_classdev_unregister(&chip->cdev_flash); + flush_work(&chip->work_flash); + regmap_write(chip->regmap, REG_ENABLE, 0); + return 0; +} + +static const struct i2c_device_id lm3642_id[] = { + {LM3642_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm3642_id); + +static struct i2c_driver lm3642_i2c_driver = { + .driver = { + .name = LM3642_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, + .probe = lm3642_probe, + .remove = __devexit_p(lm3642_remove), + .id_table = lm3642_id, +}; + +module_i2c_driver(lm3642_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3642"); +MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); +MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index fbc12acada95..97994ffdc014 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -104,6 +104,11 @@ #define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) #define SHIFT_MASK(id) (((id) - 1) * 2) +enum lp5523_chip_id { + LP5523, + LP55231, +}; + struct lp5523_engine { int id; u8 mode; @@ -150,7 +155,7 @@ static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) leds[led->id]); } -static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode); +static void 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, const u8 *pattern); @@ -177,7 +182,7 @@ static int lp5523_detect(struct i2c_client *client) int ret; u8 buf; - ret = lp5523_write(client, LP5523_REG_ENABLE, 0x40); + ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); if (ret) return ret; ret = lp5523_read(client, LP5523_REG_ENABLE, &buf); @@ -338,7 +343,8 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) { int i; u16 tmp_mux = 0; - len = len < LP5523_LEDS ? len : LP5523_LEDS; + + len = min_t(int, len, LP5523_LEDS); for (i = 0; i < len; i++) { switch (buf[i]) { case '1': @@ -546,6 +552,9 @@ static int lp5523_do_store_load(struct lp5523_engine *engine, unsigned cmd; u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; + if (engine->mode != LP5523_CMD_LOAD) + return -EINVAL; + while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) { /* separate sscanfs because length is working only for %s */ ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); @@ -563,12 +572,7 @@ static int lp5523_do_store_load(struct lp5523_engine *engine, goto fail; mutex_lock(&chip->lock); - - if (engine->mode == LP5523_CMD_LOAD) - ret = lp5523_load_program(engine, pattern); - else - ret = -EINVAL; - + ret = lp5523_load_program(engine, pattern); mutex_unlock(&chip->lock); if (ret) { @@ -755,6 +759,7 @@ static struct attribute *lp5523_attributes[] = { &dev_attr_engine2_leds.attr, &dev_attr_engine3_load.attr, &dev_attr_engine3_leds.attr, + NULL, }; static const struct attribute_group lp5523_group = { @@ -789,26 +794,28 @@ static void lp5523_unregister_sysfs(struct i2c_client *client) /*--------------------------------------------------------------*/ /* Set chip operating mode */ /*--------------------------------------------------------------*/ -static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode) +static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) { - int ret = 0; - /* if in that mode already do nothing, except for run */ if (mode == engine->mode && mode != LP5523_CMD_RUN) - return 0; + return; - if (mode == LP5523_CMD_RUN) { - ret = lp5523_run_program(engine); - } else if (mode == LP5523_CMD_LOAD) { + switch (mode) { + case LP5523_CMD_RUN: + lp5523_run_program(engine); + break; + case LP5523_CMD_LOAD: lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - } else if (mode == LP5523_CMD_DISABLED) { + break; + case LP5523_CMD_DISABLED: lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); + break; + default: + return; } engine->mode = mode; - - return ret; } /*--------------------------------------------------------------*/ @@ -827,7 +834,8 @@ static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) } static int __devinit lp5523_init_led(struct lp5523_led *led, struct device *dev, - int chan, struct lp5523_platform_data *pdata) + int chan, struct lp5523_platform_data *pdata, + const char *chip_name) { char name[32]; int res; @@ -846,10 +854,14 @@ static int __devinit lp5523_init_led(struct lp5523_led *led, struct device *dev, return -EINVAL; } - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: "lp5523", chan); + if (pdata->led_config[chan].name) { + led->cdev.name = pdata->led_config[chan].name; + } else { + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ? : chip_name, chan); + led->cdev.name = name; + } - led->cdev.name = name; led->cdev.brightness_set = lp5523_set_brightness; res = led_classdev_register(dev, &led->cdev); if (res < 0) { @@ -917,7 +929,7 @@ static int __devinit lp5523_probe(struct i2c_client *client, if (ret) goto fail1; - dev_info(&client->dev, "LP5523 Programmable led chip found\n"); + dev_info(&client->dev, "%s Programmable led chip found\n", id->name); /* Initialize engines */ for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { @@ -945,7 +957,8 @@ static int __devinit lp5523_probe(struct i2c_client *client, INIT_WORK(&chip->leds[led].brightness_work, lp5523_led_brightness_work); - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata); + ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, + id->name); if (ret) { dev_err(&client->dev, "error initializing leds\n"); goto fail2; @@ -970,7 +983,7 @@ static int __devinit lp5523_probe(struct i2c_client *client, fail2: for (i = 0; i < chip->num_leds; i++) { led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); + flush_work(&chip->leds[i].brightness_work); } fail1: if (pdata->enable) @@ -985,11 +998,14 @@ static int lp5523_remove(struct i2c_client *client) struct lp5523_chip *chip = i2c_get_clientdata(client); int i; + /* Disable engine mode */ + lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); + lp5523_unregister_sysfs(client); for (i = 0; i < chip->num_leds; i++) { led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); + flush_work(&chip->leds[i].brightness_work); } if (chip->pdata->enable) @@ -1000,7 +1016,8 @@ static int lp5523_remove(struct i2c_client *client) } static const struct i2c_device_id lp5523_id[] = { - { "lp5523", 0 }, + { "lp5523", LP5523 }, + { "lp55231", LP55231 }, { } }; @@ -1008,7 +1025,7 @@ MODULE_DEVICE_TABLE(i2c, lp5523_id); static struct i2c_driver lp5523_driver = { .driver = { - .name = "lp5523", + .name = "lp5523x", }, .probe = lp5523_probe, .remove = lp5523_remove, diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c index edcd706c5631..2f2f9c43535d 100644 --- a/drivers/leds/leds-pca9633.c +++ b/drivers/leds/leds-pca9633.c @@ -22,6 +22,7 @@ #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 */ @@ -96,13 +97,13 @@ static int __devinit pca9633_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pca9633_led *pca9633; - struct led_platform_data *pdata; + struct pca9633_platform_data *pdata; int i, err; pdata = client->dev.platform_data; if (pdata) { - if (pdata->num_leds <= 0 || pdata->num_leds > 4) { + 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; } @@ -119,14 +120,14 @@ static int __devinit pca9633_probe(struct i2c_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) + 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[i].name); - if (pdata->leds[i].default_trigger) + pdata->leds.leds[i].name); + if (pdata->leds.leds[i].default_trigger) pca9633[i].led_cdev.default_trigger = - pdata->leds[i].default_trigger; + pdata->leds.leds[i].default_trigger; } else { snprintf(pca9633[i].name, sizeof(pca9633[i].name), "pca9633:%d", i); @@ -145,6 +146,10 @@ static int __devinit pca9633_probe(struct i2c_client *client, /* 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); diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 4c62113f7a77..88f23f845595 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -201,7 +201,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; - int ret, i; + int i; if (pdata == NULL) { dev_err(&pdev->dev, "no platform data\n"); @@ -214,24 +214,21 @@ static int wm8350_led_probe(struct platform_device *pdev) return -EINVAL; } - isink = regulator_get(&pdev->dev, "led_isink"); + isink = devm_regulator_get(&pdev->dev, "led_isink"); if (IS_ERR(isink)) { printk(KERN_ERR "%s: can't get ISINK\n", __func__); return PTR_ERR(isink); } - dcdc = regulator_get(&pdev->dev, "led_vcc"); + dcdc = devm_regulator_get(&pdev->dev, "led_vcc"); if (IS_ERR(dcdc)) { printk(KERN_ERR "%s: can't get DCDC\n", __func__); - ret = PTR_ERR(dcdc); - goto err_isink; + return PTR_ERR(dcdc); } led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); - if (led == NULL) { - ret = -ENOMEM; - goto err_dcdc; - } + if (led == NULL) + return -ENOMEM; led->cdev.brightness_set = wm8350_led_set; led->cdev.default_trigger = pdata->default_trigger; @@ -257,17 +254,7 @@ static int wm8350_led_probe(struct platform_device *pdev) led->value = LED_OFF; platform_set_drvdata(pdev, led); - ret = led_classdev_register(&pdev->dev, &led->cdev); - if (ret < 0) - goto err_dcdc; - - return 0; - - err_dcdc: - regulator_put(dcdc); - err_isink: - regulator_put(isink); - return ret; + return led_classdev_register(&pdev->dev, &led->cdev); } static int wm8350_led_remove(struct platform_device *pdev) @@ -277,8 +264,6 @@ static int wm8350_led_remove(struct platform_device *pdev) led_classdev_unregister(&led->cdev); flush_work(&led->work); wm8350_led_disable(led); - regulator_put(led->dcdc); - regulator_put(led->isink); return 0; } diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index d02acd496126..4c50365344a9 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -32,6 +32,8 @@ static inline int led_get_brightness(struct led_classdev *led_cdev) return led_cdev->brightness; } +void led_stop_software_blink(struct led_classdev *led_cdev); + extern struct rw_semaphore leds_list_lock; extern struct list_head leds_list; diff --git a/include/linux/leds-lp5523.h b/include/linux/leds-lp5523.h index 2694289babd0..727877fb406d 100644 --- a/include/linux/leds-lp5523.h +++ b/include/linux/leds-lp5523.h @@ -26,6 +26,7 @@ /* See Documentation/leds/leds-lp5523.txt */ struct lp5523_led_config { + const char *name; u8 chan_nr; u8 led_current; /* mA x10, 0 if led is not connected */ u8 max_current; diff --git a/include/linux/leds.h b/include/linux/leds.h index c6f8dad2ceb0..6e53bb31c220 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -16,6 +16,7 @@ #include <linux/spinlock.h> #include <linux/rwsem.h> #include <linux/timer.h> +#include <linux/workqueue.h> struct device; /* @@ -69,6 +70,9 @@ struct led_classdev { struct timer_list blink_timer; int blink_brightness; + struct work_struct set_brightness_work; + int delayed_set_value; + #ifdef CONFIG_LEDS_TRIGGERS /* Protects the trigger data below */ struct rw_semaphore trigger_lock; diff --git a/include/linux/platform_data/leds-lm3556.h b/include/linux/platform_data/leds-lm3556.h deleted file mode 100644 index 4b4e7d6b0527..000000000000 --- a/include/linux/platform_data/leds-lm3556.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03) - * Copyright (C) 2012 Texas Instruments - * - * 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. - * - */ - -#ifndef __LINUX_LM3556_H -#define __LINUX_LM3556_H - -#define LM3556_NAME "leds-lm3556" - -enum lm3556_pin_polarity { - PIN_LOW_ACTIVE = 0, - PIN_HIGH_ACTIVE, -}; - -enum lm3556_pin_enable { - PIN_DISABLED = 0, - PIN_ENABLED, -}; - -enum lm3556_strobe_usuage { - STROBE_EDGE_DETECT = 0, - STROBE_LEVEL_DETECT, -}; - -enum lm3556_indic_mode { - INDIC_MODE_INTERNAL = 0, - INDIC_MODE_EXTERNAL, -}; - -struct lm3556_platform_data { - enum lm3556_pin_enable torch_pin_en; - enum lm3556_pin_polarity torch_pin_polarity; - - enum lm3556_strobe_usuage strobe_usuage; - enum lm3556_pin_enable strobe_pin_en; - enum lm3556_pin_polarity strobe_pin_polarity; - - enum lm3556_pin_enable tx_pin_en; - enum lm3556_pin_polarity tx_pin_polarity; - - enum lm3556_indic_mode indicator_mode; -}; - -#endif /* __LINUX_LM3556_H */ diff --git a/include/linux/platform_data/leds-lm355x.h b/include/linux/platform_data/leds-lm355x.h new file mode 100644 index 000000000000..b88724bb0b46 --- /dev/null +++ b/include/linux/platform_data/leds-lm355x.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Texas Instruments + * + * License Terms: GNU General Public License v2 + * + * Simple driver for Texas Instruments LM355x LED driver chip + * + * Author: G.Shark Jeong <gshark.jeong@gmail.com> + * Daniel Jeong <daniel.jeong@ti.com> + */ + +#define LM355x_NAME "leds-lm355x" +#define LM3554_NAME "leds-lm3554" +#define LM3556_NAME "leds-lm3556" + +/* lm3554 : strobe def. on */ +enum lm355x_strobe { + LM355x_PIN_STROBE_DISABLE = 0x00, + LM355x_PIN_STROBE_ENABLE = 0x01, +}; + +enum lm355x_torch { + LM355x_PIN_TORCH_DISABLE = 0, + LM3554_PIN_TORCH_ENABLE = 0x80, + LM3556_PIN_TORCH_ENABLE = 0x10, +}; + +enum lm355x_tx2 { + LM355x_PIN_TX_DISABLE = 0, + LM3554_PIN_TX_ENABLE = 0x20, + LM3556_PIN_TX_ENABLE = 0x40, +}; + +enum lm355x_ntc { + LM355x_PIN_NTC_DISABLE = 0, + LM3554_PIN_NTC_ENABLE = 0x08, + LM3556_PIN_NTC_ENABLE = 0x80, +}; + +enum lm355x_pmode { + LM355x_PMODE_DISABLE = 0, + LM355x_PMODE_ENABLE = 0x04, +}; + +/* + * struct lm3554_platform_data + * @pin_strobe: strobe input + * @pin_torch : input pin + * lm3554-tx1/torch/gpio1 + * lm3556-torch + * @pin_tx2 : input pin + * lm3554-envm/tx2/gpio2 + * lm3556-tx pin + * @ntc_pin : output pin + * lm3554-ledi/ntc + * lm3556-temp pin + * @pass_mode : pass mode + */ +struct lm355x_platform_data { + enum lm355x_strobe pin_strobe; + enum lm355x_torch pin_tx1; + enum lm355x_tx2 pin_tx2; + enum lm355x_ntc ntc_pin; + + enum lm355x_pmode pass_mode; +}; diff --git a/include/linux/platform_data/leds-lm3642.h b/include/linux/platform_data/leds-lm3642.h new file mode 100644 index 000000000000..72d6ee6ade57 --- /dev/null +++ b/include/linux/platform_data/leds-lm3642.h @@ -0,0 +1,38 @@ +/* +* Copyright (C) 2012 Texas Instruments +* +* License Terms: GNU General Public License v2 +* +* Simple driver for Texas Instruments LM3642 LED driver chip +* +* Author: G.Shark Jeong <gshark.jeong@gmail.com> +* Daniel Jeong <daniel.jeong@ti.com> +*/ + +#ifndef __LINUX_LM3642_H +#define __LINUX_LM3642_H + +#define LM3642_NAME "leds-lm3642" + +enum lm3642_torch_pin_enable { + LM3642_TORCH_PIN_DISABLE = 0x00, + LM3642_TORCH_PIN_ENABLE = 0x10, +}; + +enum lm3642_strobe_pin_enable { + LM3642_STROBE_PIN_DISABLE = 0x00, + LM3642_STROBE_PIN_ENABLE = 0x20, +}; + +enum lm3642_tx_pin_enable { + LM3642_TX_PIN_DISABLE = 0x00, + LM3642_TX_PIN_ENABLE = 0x40, +}; + +struct lm3642_platform_data { + enum lm3642_torch_pin_enable torch_pin; + enum lm3642_strobe_pin_enable strobe_pin; + enum lm3642_tx_pin_enable tx_pin; +}; + +#endif /* __LINUX_LM3642_H */ diff --git a/include/linux/platform_data/leds-pca9633.h b/include/linux/platform_data/leds-pca9633.h new file mode 100644 index 000000000000..c5bf29b6fa7f --- /dev/null +++ b/include/linux/platform_data/leds-pca9633.h @@ -0,0 +1,35 @@ +/* + * PCA9633 LED chip driver. + * + * Copyright 2012 bct electronic GmbH + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __LINUX_PCA9633_H +#define __LINUX_PCA9633_H +#include <linux/leds.h> + +enum pca9633_outdrv { + PCA9633_OPEN_DRAIN, + PCA9633_TOTEM_POLE, /* aka push-pull */ +}; + +struct pca9633_platform_data { + struct led_platform_data leds; + enum pca9633_outdrv outdrv; +}; + +#endif /* __LINUX_PCA9633_H*/ |