From 96f09791ec4525e0ad838fe3c5918ceaee3bd81b Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 2 Aug 2012 05:04:46 +0800 Subject: [PATCH 01/24] leds-clevo-mail: add MODULE_DEVICE_TABLE Enable autoloading of leds-clevo-mail by using MODULE_DEVICE_TABLE. Signed-off-by: Ondrej Zary Signed-off-by: Bryan Wu --- drivers/leds/leds-clevo-mail.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 1ed1677c916..73eed7ef2a5 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", @@ -89,6 +89,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 +181,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. " From ee539a9309ecaf6b6eee59f1c9e1c4fd716624a0 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 2 Aug 2012 05:04:56 +0800 Subject: [PATCH 02/24] leds-clevo-mail: Fix Clevo M5x0V DMI strings There are more variants of Clevo M5x0V laptops and Positivo Mobile is just one of them. Another one is UMAX VisionBook 2400WXC. Use only generic Clevo DMI strings to match all variants. Signed-off-by: Ondrej Zary Signed-off-by: Bryan Wu --- drivers/leds/leds-clevo-mail.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 73eed7ef2a5..e024b0b1c3b 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -59,11 +59,10 @@ static struct dmi_system_id __initdata clevo_mail_led_dmi_table[] = { }, { .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") } }, From 490dcee9b433302da4ec5325c3e69a0be1201473 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 10 Aug 2012 10:08:32 +0800 Subject: [PATCH 03/24] leds: wm8350: Convert to devm_regulator_get() Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-wm8350.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 918d4baff1c..f5d9ac3ff16 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_sync(&led->work); wm8350_led_disable(led); - regulator_put(led->dcdc); - regulator_put(led->isink); return 0; } From d23a22a74fded23a12434c9463fe66cec2b0afcd Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 Aug 2012 21:44:34 +0800 Subject: [PATCH 04/24] leds: delay led_set_brightness if stopping soft-blink Delay execution of led_set_brightness() if need to stop soft-blink timer. This allows led_set_brightness to be called in hard-irq context even if soft-blink was activated on that LED. Signed-off-by: Fabio Baltieri Cc: Pawel Moll Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 15 +++++++++++++++ drivers/leds/led-core.c | 16 +++++++++++++--- drivers/leds/led-triggers.c | 4 +++- drivers/leds/leds.h | 2 ++ include/linux/leds.h | 4 ++++ 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index c599095bc00..48cce18e9d6 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 2ab05af3de3..ce8921a753a 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 363975b3c92..b53bf54023f 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -109,6 +109,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; @@ -224,7 +226,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.h b/drivers/leds/leds.h index d02acd49612..4c50365344a 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.h b/include/linux/leds.h index 3aade1d8f41..56761974f28 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -16,6 +16,7 @@ #include #include #include +#include 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; From 6ebcebddffbf5c074e467c725aafbd21dfd46ed5 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 16 Aug 2012 18:35:36 +0800 Subject: [PATCH 05/24] leds: leds-gpio: use of_match_ptr() Instead of having to define the match table to NULL if CONFIG_OF isn't set, use the of_match_ptr() macro which will do this for us. Signed-off-by: Tobias Klauser Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index c032b218034..4043f7732e5 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -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 */ @@ -287,7 +286,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), }, }; From 59c4dce13018b04275478e95b3929eca55afcdfb Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 16 Aug 2012 18:36:13 +0800 Subject: [PATCH 06/24] leds: leds-gpio: Use platform_{get,set}_drvdata Use the wrapper functions, so we can directly pass a struct platfrom_device. Signed-off-by: Tobias Klauser Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 4043f7732e5..c392c1e9553 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -269,13 +269,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; } From 127aedc8ecae2628511533ddeb239e99e4bed5e8 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 21 Aug 2012 17:21:53 +0800 Subject: [PATCH 07/24] leds: leds-gpio: Use of_get_child_count() helper Use of_get_child_count() instead of custom implementation. Signed-off-by: Tobias Klauser Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index c392c1e9553..cde85ba1903 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -170,11 +170,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; From 94b43b677104e50b9f8dd75aacb3c69e16089a68 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 22 Aug 2012 15:32:29 +0800 Subject: [PATCH 08/24] leds-lp5523: add channel name in the platform data The name of each led channel is configurable. If the name is NULL, just use the channel id for making the channel name Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- Documentation/leds/leds-lp5523.txt | 21 ++++++++++++++++++--- drivers/leds/leds-lp5523.c | 10 +++++++--- include/linux/leds-lp5523.h | 1 + 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Documentation/leds/leds-lp5523.txt b/Documentation/leds/leds-lp5523.txt index fad2feb8b7c..c2743f59f9a 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/leds-lp5523.c b/drivers/leds/leds-lp5523.c index fbc12acada9..9fd9a92ed91 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -846,10 +846,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 ?: "lp5523", 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) { diff --git a/include/linux/leds-lp5523.h b/include/linux/leds-lp5523.h index 2694289babd..727877fb406 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; From a39b9854c79cc9ac7b7b6fed58cf07da23f1f41e Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 22 Aug 2012 15:32:45 +0800 Subject: [PATCH 09/24] leds-lp5523: set the brightness to 0 forcely on removing the driver Turning off the brightness of each channel is required when removing the driver. So use flush_work() rather than cancel_work_sync() to execute remaining brightness works. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 9fd9a92ed91..2fd5689f65c 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -974,7 +974,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) @@ -993,7 +993,7 @@ static int lp5523_remove(struct i2c_client *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) From 6f6365fbac2d6bcf6867c633d3ec33af08b5b219 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 21 Aug 2012 17:03:58 +0800 Subject: [PATCH 10/24] leds-lp5523: change the return type of lp5523_set_mode() The return value of this function is not handled any place, so make it as void type. And three if-statements are replaced with switch-statements. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2fd5689f65c..a14495402cc 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -150,7 +150,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); @@ -789,26 +789,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; } /*--------------------------------------------------------------*/ From 469eba023087d8fa3974827e6bedf874a620d0e7 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 21 Aug 2012 17:04:02 +0800 Subject: [PATCH 11/24] leds-lp5523: minor code style fixes (a) use LP5523_ENABLE rather than magic number 0x40 (b) use min_t() in lp5523_mux_parse() (c) skip while loop and just return if invalid command Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index a14495402cc..80d37929044 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -177,7 +177,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 +338,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 +547,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 +567,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) { From 52c47742f79d9240f90af9a6722fe8bb3fa8c0f9 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 27 Aug 2012 09:31:49 +0800 Subject: [PATCH 12/24] leds: triggers: send uevent when changing triggers Some triggers create sysfs files when they are enabled. Send a uevent "change" notification whenever the trigger is changed to allow userspace processes such as udev to modify permissions on the new files. A change notification will also be sent during registration of led class devices or led triggers if the default trigger of an led class device is found. (bryan.wu@canonical.com: rename trigger to trig to fix the build error) Signed-off-by: Colin Cross Acked-by: Greg Kroah-Hartman Acked-by: Henrique de Moraes Holschuh Signed-off-by: Bryan Wu --- drivers/leds/led-triggers.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index b53bf54023f..262eb419371 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) { @@ -124,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); From ca7053216833d6b9cd3faaa0dfd2261ce3b9dffc Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 4 Sep 2012 15:06:11 +0800 Subject: [PATCH 13/24] leds-lp5523: support new LP55231 device New TI LP55231 device has same I2C register layout as LP5523. Therefore, all of LED operations can be shared with the LP5523 driver. To support new LP55231 device explicitly, the device description is added in the configuration file. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index c96bbaadeeb..86ea15a127f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -192,11 +192,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. From 27d7704e5ebf0bc0cba86508023dd484639a48de Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 4 Sep 2012 15:06:18 +0800 Subject: [PATCH 14/24] leds-lp5523: add new device id for LP55231 To support LP55231 device, the device id is added. Additionally, the i2c driver name is changed from lp5523 to lp5523x. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 80d37929044..e69e72a11a9 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; @@ -1005,7 +1010,8 @@ static int lp5523_remove(struct i2c_client *client) } static const struct i2c_device_id lp5523_id[] = { - { "lp5523", 0 }, + { "lp5523", LP5523 }, + { "lp55231", LP55231 }, { } }; @@ -1013,7 +1019,7 @@ MODULE_DEVICE_TABLE(i2c, lp5523_id); static struct i2c_driver lp5523_driver = { .driver = { - .name = "lp5523", + .name = "lp5523x", }, .probe = lp5523_probe, .remove = lp5523_remove, From 56a1e9adc83870ae9ad9ff1bf9fd8bccebdfe065 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 4 Sep 2012 15:06:26 +0800 Subject: [PATCH 15/24] leds-lp5523: use the i2c device id rather than fixed name LP5523 driver supports both LP5523 and LP55231. The i2c device id is one of the two - lp5523 or lp55231. So it's better to use matching i2c device id while enumerating the device and naming LED channels. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index e69e72a11a9..3d60428df64 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -833,7 +833,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; @@ -856,7 +857,7 @@ static int __devinit lp5523_init_led(struct lp5523_led *led, struct device *dev, led->cdev.name = pdata->led_config[chan].name; } else { snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: "lp5523", chan); + pdata->label ? : chip_name, chan); led->cdev.name = name; } @@ -927,7 +928,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++) { @@ -955,7 +956,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; From b98d13c725920e9ab7696e0d7d19c4db1bdf6737 Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Wed, 5 Sep 2012 15:05:58 +0800 Subject: [PATCH 16/24] leds: Add new LED driver for lm355x chips This driver is a general version for LM355x,lm3554 and lm3556,led chips of TI. LM3554 : The LM3554 is a 2 MHz fixed-frequency synchronous boost converter with 1.2A dual high side led drivers. Datasheet: www.ti.com/lit/ds/symlink/lm3554.pdf LM3556 : The LM3556 is a 4 MHz fixed-frequency synchronous boost converter plus 1.5A constant current driver for a high-current white LED. Datasheet: www.national.com/ds/LM/LM3556.pdf (bryan.wu@canonical.com: use flush_work() to replace flush_work_sync() which is deprecated) Signed-off-by: G.Shark Jeong Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 8 +- drivers/leds/Makefile | 2 +- drivers/leds/leds-lm3556.c | 512 ------------------- drivers/leds/leds-lm355x.c | 572 ++++++++++++++++++++++ include/linux/platform_data/leds-lm3556.h | 50 -- include/linux/platform_data/leds-lm355x.h | 66 +++ 6 files changed, 643 insertions(+), 567 deletions(-) delete mode 100644 drivers/leds/leds-lm3556.c create mode 100644 drivers/leds/leds-lm355x.c delete mode 100644 include/linux/platform_data/leds-lm3556.h create mode 100644 include/linux/platform_data/leds-lm355x.h diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 86ea15a127f..0dcb794df1f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -423,13 +423,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 a4429a9217b..b57a021d1fe 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -48,7 +48,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/leds-lm3556.c b/drivers/leds/leds-lm3556.c deleted file mode 100644 index 3062abd9a53..00000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -MODULE_AUTHOR("G.Shark Jeong "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c new file mode 100644 index 00000000000..065ec015d67 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_AUTHOR("G.Shark Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-lm3556.h b/include/linux/platform_data/leds-lm3556.h deleted file mode 100644 index 4b4e7d6b052..00000000000 --- 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 00000000000..b88724bb0b4 --- /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 + * Daniel Jeong + */ + +#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; +}; From 8fe4554f675c5822a0e12382e6843965ffd11c30 Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Sat, 1 Sep 2012 16:16:30 +0800 Subject: [PATCH 17/24] leds: leds-gpio: adopt pinctrl support Adopt pinctrl support to leds-gpio driver based on leds-gpio device pointer, pinctrl driver configure SoC pins to GPIO mode according to definitions provided in .dts file. Signed-off-by: AnilKumar Ch Acked-by: Marek Vasut Reviewed-by: Linus Walleij Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index cde85ba1903..087d1e66f4f 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -20,6 +20,7 @@ #include #include #include +#include struct gpio_led_data { struct led_classdev cdev; @@ -234,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), From a47df97c9936cff6d31f03e12651ad897e0e374c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 11 Sep 2012 19:42:10 +0800 Subject: [PATCH 18/24] leds-lm3530: Use devm_regulator_get function Device managed functions are already used in this file. Hence convert regulator_get() too to use it. Signed-off-by: Sachin Kamat Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3530.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 23637bdb275..1e7ed2abfdc 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -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 -ENODEV; } } 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 -ENODEV; } 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; } From e0e021600e3ebb80d6539cc5f8de8f9d19687e80 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Sep 2012 14:34:09 +0800 Subject: [PATCH 19/24] leds-lm3530: Fix smatch warnings Fixes the following smatch warnings: drivers/leds/leds-lm3530.c:361 lm3530_mode_set() info: why not propagate 'mode' from lm3530_get_mode_from_str() instead of -22? drivers/leds/leds-lm3530.c:432 lm3530_probe() info: why not propagate 'err' from lm3530_init_registers() instead of -19? drivers/leds/leds-lm3530.c:438 lm3530_probe() info: why not propagate 'err' from led_classdev_register() instead of -19? Reported-by: Fengguang Wu Cc: Shreshtha Kumar SAHU Signed-off-by: Sachin Kamat Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3530.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 1e7ed2abfdc..b26306f6724 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; @@ -429,13 +429,13 @@ static int __devinit lm3530_probe(struct i2c_client *client, if (err < 0) { dev_err(&client->dev, "Register Init failed: %d\n", err); - return -ENODEV; + 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); - return -ENODEV; + return err; } err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode); From 23301b7f1b8910fb3ba84935edbf665808f53871 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 12 Sep 2012 20:16:00 +0800 Subject: [PATCH 20/24] leds-lp5523: turn off the LED engines on unloading the driver The LP5523 has 3 engines which are used for running LED patterns. These engines should be off while unloading the driver. Obviously, LP5523 platform data are used for releasing the resource such like enable()/release_resource(), but these are not mandatory. Therefore this patch is required without the platform data dependency. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 3d60428df64..5ebdc9245d5 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -997,6 +997,9 @@ 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++) { From f162584232e1b7eb05647abb1f109cbe91543ac8 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 12 Sep 2012 20:16:03 +0800 Subject: [PATCH 21/24] leds-lp5523: Fix riskiness of the page fault The last attribute should be terminated as NULL because any member of attribute structure is accessed while adding the sysfs file. If not, invalid address may cause the page fault problem. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 5ebdc9245d5..97994ffdc01 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -759,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 = { From 8b7cfbec052d3a0b8e8c7de24a6b5f00d340e193 Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Wed, 12 Sep 2012 20:05:50 +0800 Subject: [PATCH 22/24] leds: Add new LED driver for lm3642 chips This driver is a general version for LM642 led chip of TI. LM3642 : The LM3642 is a 4MHz fixed-frequency synchronous boost converter plus 1.5A constant current driver for a high-current white LED. The LM3642 is controlled via an I2C-compatible interface. Signed-off-by: G.Shark Jeong Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 11 + drivers/leds/Makefile | 1 + drivers/leds/leds-lm3642.c | 471 ++++++++++++++++++++++ include/linux/platform_data/leds-lm3642.h | 38 ++ 4 files changed, 521 insertions(+) create mode 100644 drivers/leds/leds-lm3642.c create mode 100644 include/linux/platform_data/leds-lm3642.h diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 0dcb794df1f..7cfa4d1555d 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 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index b57a021d1fe..2ad9871becd 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 diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c new file mode 100644 index 00000000000..924853b7922 --- /dev/null +++ b/drivers/leds/leds-lm3642.c @@ -0,0 +1,471 @@ +/* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +{ + unsigned int reg_val; + int ret; + struct lm3642_platform_data *pdata = chip->pdata; + + /* set enable register */ + ret = regmap_read(chip->regmap, REG_ENABLE, ®_val); + if (ret < 0) + goto out; + + reg_val &= (~EX_PIN_ENABLE_MASK); + reg_val |= pdata->tx_pin; + ret = regmap_write(chip->regmap, REG_ENABLE, reg_val); + if (ret < 0) + goto out; + +out: + dev_err(chip->dev, "Failed to read 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 "); +MODULE_AUTHOR("G.Shark Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-lm3642.h b/include/linux/platform_data/leds-lm3642.h new file mode 100644 index 00000000000..72d6ee6ade5 --- /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 +* Daniel Jeong +*/ + +#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 */ From e76a322af16ba7be9b303239352e140209bf3822 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 22 Sep 2012 14:40:14 +0800 Subject: [PATCH 23/24] leds: lm3642: Use regmap_update_bits() in lm3642_chip_init() Use regmap_update_bits() to replace regmap_read() + regmap_write(). With this patch, we only show the error message when regmap_update_bits() fails. Signed-off-by: Axel Lin Acked-by: G.Shark Jeong Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3642.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 924853b7922..3285006e988 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -95,23 +95,14 @@ struct lm3642_chip_data { /* chip initialize */ static int __devinit lm3642_chip_init(struct lm3642_chip_data *chip) { - unsigned int reg_val; int ret; struct lm3642_platform_data *pdata = chip->pdata; /* set enable register */ - ret = regmap_read(chip->regmap, REG_ENABLE, ®_val); + ret = regmap_update_bits(chip->regmap, REG_ENABLE, EX_PIN_ENABLE_MASK, + pdata->tx_pin); if (ret < 0) - goto out; - - reg_val &= (~EX_PIN_ENABLE_MASK); - reg_val |= pdata->tx_pin; - ret = regmap_write(chip->regmap, REG_ENABLE, reg_val); - if (ret < 0) - goto out; - -out: - dev_err(chip->dev, "Failed to read REG_ENABLE Register\n"); + dev_err(chip->dev, "Failed to update REG_ENABLE Register\n"); return ret; } From 2f73c3927318abe3d7fcaecadfbad89e2d12f881 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 24 Sep 2012 14:53:49 +0800 Subject: [PATCH 24/24] leds: add output driver configuration for pca9633 led driver the pca9633 leds driver can be used in open-drain or totem pole (a.k.a. push/pull) output driver mode; default is the later the patch allows to set the output driver mode using platform data (similar to configuration inferface provided by the tca6507 led driver) v2: move leds-pca9633.h to include/linux/platform_data/ (Bryan Wu) Signed-off-by: Peter Meerwald Signed-off-by: Bryan Wu --- drivers/leds/leds-pca9633.c | 19 +++++++----- include/linux/platform_data/leds-pca9633.h | 35 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 include/linux/platform_data/leds-pca9633.h diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c index edcd706c563..2f2f9c43535 100644 --- a/drivers/leds/leds-pca9633.c +++ b/drivers/leds/leds-pca9633.c @@ -22,6 +22,7 @@ #include #include #include +#include /* 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/include/linux/platform_data/leds-pca9633.h b/include/linux/platform_data/leds-pca9633.h new file mode 100644 index 00000000000..c5bf29b6fa7 --- /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 + +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*/