diff options
-rw-r--r-- | Documentation/leds-lp3944.txt | 50 | ||||
-rw-r--r-- | Documentation/powerpc/dts-bindings/gpio/led.txt | 17 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 14 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-alix2.c | 7 | ||||
-rw-r--r-- | drivers/leds/leds-bd2802.c | 96 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 22 | ||||
-rw-r--r-- | drivers/leds/leds-lp3944.c | 466 | ||||
-rw-r--r-- | drivers/leds/leds-pca9532.c | 58 | ||||
-rw-r--r-- | include/linux/leds-lp3944.h | 53 | ||||
-rw-r--r-- | include/linux/leds.h | 14 |
11 files changed, 733 insertions, 65 deletions
diff --git a/Documentation/leds-lp3944.txt b/Documentation/leds-lp3944.txt new file mode 100644 index 000000000000..c6eda18b15ef --- /dev/null +++ b/Documentation/leds-lp3944.txt | |||
@@ -0,0 +1,50 @@ | |||
1 | Kernel driver lp3944 | ||
2 | ==================== | ||
3 | |||
4 | * National Semiconductor LP3944 Fun-light Chip | ||
5 | Prefix: 'lp3944' | ||
6 | Addresses scanned: None (see the Notes section below) | ||
7 | Datasheet: Publicly available at the National Semiconductor website | ||
8 | http://www.national.com/pf/LP/LP3944.html | ||
9 | |||
10 | Authors: | ||
11 | Antonio Ospite <ospite@studenti.unina.it> | ||
12 | |||
13 | |||
14 | Description | ||
15 | ----------- | ||
16 | The LP3944 is a helper chip that can drive up to 8 leds, with two programmable | ||
17 | DIM modes; it could even be used as a gpio expander but this driver assumes it | ||
18 | is used as a led controller. | ||
19 | |||
20 | The DIM modes are used to set _blink_ patterns for leds, the pattern is | ||
21 | specified supplying two parameters: | ||
22 | - period: from 0s to 1.6s | ||
23 | - duty cycle: percentage of the period the led is on, from 0 to 100 | ||
24 | |||
25 | Setting a led in DIM0 or DIM1 mode makes it blink according to the pattern. | ||
26 | See the datasheet for details. | ||
27 | |||
28 | LP3944 can be found on Motorola A910 smartphone, where it drives the rgb | ||
29 | leds, the camera flash light and the lcds power. | ||
30 | |||
31 | |||
32 | Notes | ||
33 | ----- | ||
34 | The chip is used mainly in embedded contexts, so this driver expects it is | ||
35 | registered using the i2c_board_info mechanism. | ||
36 | |||
37 | To register the chip at address 0x60 on adapter 0, set the platform data | ||
38 | according to include/linux/leds-lp3944.h, set the i2c board info: | ||
39 | |||
40 | static struct i2c_board_info __initdata a910_i2c_board_info[] = { | ||
41 | { | ||
42 | I2C_BOARD_INFO("lp3944", 0x60), | ||
43 | .platform_data = &a910_lp3944_leds, | ||
44 | }, | ||
45 | }; | ||
46 | |||
47 | and register it in the platform init function | ||
48 | |||
49 | i2c_register_board_info(0, a910_i2c_board_info, | ||
50 | ARRAY_SIZE(a910_i2c_board_info)); | ||
diff --git a/Documentation/powerpc/dts-bindings/gpio/led.txt b/Documentation/powerpc/dts-bindings/gpio/led.txt index 4fe14deedc0a..064db928c3c1 100644 --- a/Documentation/powerpc/dts-bindings/gpio/led.txt +++ b/Documentation/powerpc/dts-bindings/gpio/led.txt | |||
@@ -16,10 +16,17 @@ LED sub-node properties: | |||
16 | string defining the trigger assigned to the LED. Current triggers are: | 16 | string defining the trigger assigned to the LED. Current triggers are: |
17 | "backlight" - LED will act as a back-light, controlled by the framebuffer | 17 | "backlight" - LED will act as a back-light, controlled by the framebuffer |
18 | system | 18 | system |
19 | "default-on" - LED will turn on | 19 | "default-on" - LED will turn on, but see "default-state" below |
20 | "heartbeat" - LED "double" flashes at a load average based rate | 20 | "heartbeat" - LED "double" flashes at a load average based rate |
21 | "ide-disk" - LED indicates disk activity | 21 | "ide-disk" - LED indicates disk activity |
22 | "timer" - LED flashes at a fixed, configurable rate | 22 | "timer" - LED flashes at a fixed, configurable rate |
23 | - default-state: (optional) The initial state of the LED. Valid | ||
24 | values are "on", "off", and "keep". If the LED is already on or off | ||
25 | and the default-state property is set the to same value, then no | ||
26 | glitch should be produced where the LED momentarily turns off (or | ||
27 | on). The "keep" setting will keep the LED at whatever its current | ||
28 | state is, without producing a glitch. The default is off if this | ||
29 | property is not present. | ||
23 | 30 | ||
24 | Examples: | 31 | Examples: |
25 | 32 | ||
@@ -30,14 +37,22 @@ leds { | |||
30 | gpios = <&mcu_pio 0 1>; /* Active low */ | 37 | gpios = <&mcu_pio 0 1>; /* Active low */ |
31 | linux,default-trigger = "ide-disk"; | 38 | linux,default-trigger = "ide-disk"; |
32 | }; | 39 | }; |
40 | |||
41 | fault { | ||
42 | gpios = <&mcu_pio 1 0>; | ||
43 | /* Keep LED on if BIOS detected hardware fault */ | ||
44 | default-state = "keep"; | ||
45 | }; | ||
33 | }; | 46 | }; |
34 | 47 | ||
35 | run-control { | 48 | run-control { |
36 | compatible = "gpio-leds"; | 49 | compatible = "gpio-leds"; |
37 | red { | 50 | red { |
38 | gpios = <&mpc8572 6 0>; | 51 | gpios = <&mpc8572 6 0>; |
52 | default-state = "off"; | ||
39 | }; | 53 | }; |
40 | green { | 54 | green { |
41 | gpios = <&mpc8572 7 0>; | 55 | gpios = <&mpc8572 7 0>; |
56 | default-state = "on"; | ||
42 | }; | 57 | }; |
43 | } | 58 | } |
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 9b60b6b684d9..7c8e7122aaa9 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -75,6 +75,7 @@ config LEDS_ALIX2 | |||
75 | depends on LEDS_CLASS && X86 && EXPERIMENTAL | 75 | depends on LEDS_CLASS && X86 && EXPERIMENTAL |
76 | help | 76 | help |
77 | This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. | 77 | This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. |
78 | You have to set leds-alix2.force=1 for boards with Award BIOS. | ||
78 | 79 | ||
79 | config LEDS_H1940 | 80 | config LEDS_H1940 |
80 | tristate "LED Support for iPAQ H1940 device" | 81 | tristate "LED Support for iPAQ H1940 device" |
@@ -145,15 +146,16 @@ config LEDS_GPIO_OF | |||
145 | of_platform devices. For instance, LEDs which are listed in a "dts" | 146 | of_platform devices. For instance, LEDs which are listed in a "dts" |
146 | file. | 147 | file. |
147 | 148 | ||
148 | config LEDS_LP5521 | 149 | config LEDS_LP3944 |
149 | tristate "LED Support for the LP5521 LEDs" | 150 | tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip" |
150 | depends on LEDS_CLASS && I2C | 151 | depends on LEDS_CLASS && I2C |
151 | help | 152 | help |
152 | If you say 'Y' here you get support for the National Semiconductor | 153 | This option enables support for LEDs connected to the National |
153 | LP5521 LED driver used in n8x0 boards. | 154 | Semiconductor LP3944 Lighting Management Unit (LMU) also known as |
155 | Fun Light Chip. | ||
154 | 156 | ||
155 | This driver can be built as a module by choosing 'M'. The module | 157 | To compile this driver as a module, choose M here: the |
156 | will be called leds-lp5521. | 158 | module will be called leds-lp3944. |
157 | 159 | ||
158 | config LEDS_CLEVO_MAIL | 160 | config LEDS_CLEVO_MAIL |
159 | tristate "Mail LED on Clevo notebook" | 161 | tristate "Mail LED on Clevo notebook" |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 2d41c4dcf92f..e8cdcf77a4c3 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -20,6 +20,7 @@ obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o | |||
20 | obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o | 20 | obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o |
21 | obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o | 21 | obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o |
22 | obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o | 22 | obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o |
23 | obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o | ||
23 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o | 24 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o |
24 | obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o | 25 | obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o |
25 | obj-$(CONFIG_LEDS_FSG) += leds-fsg.o | 26 | obj-$(CONFIG_LEDS_FSG) += leds-fsg.o |
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c index ddbd7730dfc8..731d4eef3425 100644 --- a/drivers/leds/leds-alix2.c +++ b/drivers/leds/leds-alix2.c | |||
@@ -14,7 +14,7 @@ | |||
14 | 14 | ||
15 | static int force = 0; | 15 | static int force = 0; |
16 | module_param(force, bool, 0444); | 16 | module_param(force, bool, 0444); |
17 | MODULE_PARM_DESC(force, "Assume system has ALIX.2 style LEDs"); | 17 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); |
18 | 18 | ||
19 | struct alix_led { | 19 | struct alix_led { |
20 | struct led_classdev cdev; | 20 | struct led_classdev cdev; |
@@ -155,6 +155,11 @@ static int __init alix_led_init(void) | |||
155 | goto out; | 155 | goto out; |
156 | } | 156 | } |
157 | 157 | ||
158 | /* enable output on GPIO for LED 1,2,3 */ | ||
159 | outl(1 << 6, 0x6104); | ||
160 | outl(1 << 9, 0x6184); | ||
161 | outl(1 << 11, 0x6184); | ||
162 | |||
158 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | 163 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); |
159 | if (!IS_ERR(pdev)) { | 164 | if (!IS_ERR(pdev)) { |
160 | ret = platform_driver_probe(&alix_led_driver, alix_led_probe); | 165 | ret = platform_driver_probe(&alix_led_driver, alix_led_probe); |
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index 4149ecb3a9b2..779d7f262c04 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c | |||
@@ -97,6 +97,10 @@ struct bd2802_led { | |||
97 | enum led_ids led_id; | 97 | enum led_ids led_id; |
98 | enum led_colors color; | 98 | enum led_colors color; |
99 | enum led_bits state; | 99 | enum led_bits state; |
100 | |||
101 | /* General attributes of RGB LEDs */ | ||
102 | int wave_pattern; | ||
103 | int rgb_current; | ||
100 | }; | 104 | }; |
101 | 105 | ||
102 | 106 | ||
@@ -254,7 +258,7 @@ static void bd2802_set_on(struct bd2802_led *led, enum led_ids id, | |||
254 | bd2802_reset_cancel(led); | 258 | bd2802_reset_cancel(led); |
255 | 259 | ||
256 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | 260 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); |
257 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_032); | 261 | bd2802_write_byte(led->client, reg, led->rgb_current); |
258 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | 262 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); |
259 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | 263 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); |
260 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); | 264 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); |
@@ -275,9 +279,9 @@ static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id, | |||
275 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | 279 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); |
276 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | 280 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); |
277 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | 281 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); |
278 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_032); | 282 | bd2802_write_byte(led->client, reg, led->rgb_current); |
279 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); | 283 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); |
280 | bd2802_write_byte(led->client, reg, BD2802_PATTERN_HALF); | 284 | bd2802_write_byte(led->client, reg, led->wave_pattern); |
281 | 285 | ||
282 | bd2802_enable(led, id); | 286 | bd2802_enable(led, id); |
283 | bd2802_update_state(led, id, color, BD2802_BLINK); | 287 | bd2802_update_state(led, id, color, BD2802_BLINK); |
@@ -406,7 +410,7 @@ static void bd2802_enable_adv_conf(struct bd2802_led *led) | |||
406 | ret = device_create_file(&led->client->dev, | 410 | ret = device_create_file(&led->client->dev, |
407 | bd2802_addr_attributes[i]); | 411 | bd2802_addr_attributes[i]); |
408 | if (ret) { | 412 | if (ret) { |
409 | dev_err(&led->client->dev, "failed to sysfs file %s\n", | 413 | dev_err(&led->client->dev, "failed: sysfs file %s\n", |
410 | bd2802_addr_attributes[i]->attr.name); | 414 | bd2802_addr_attributes[i]->attr.name); |
411 | goto failed_remove_files; | 415 | goto failed_remove_files; |
412 | } | 416 | } |
@@ -483,6 +487,52 @@ static struct device_attribute bd2802_adv_conf_attr = { | |||
483 | .store = bd2802_store_adv_conf, | 487 | .store = bd2802_store_adv_conf, |
484 | }; | 488 | }; |
485 | 489 | ||
490 | #define BD2802_CONTROL_ATTR(attr_name, name_str) \ | ||
491 | static ssize_t bd2802_show_##attr_name(struct device *dev, \ | ||
492 | struct device_attribute *attr, char *buf) \ | ||
493 | { \ | ||
494 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ | ||
495 | ssize_t ret; \ | ||
496 | down_read(&led->rwsem); \ | ||
497 | ret = sprintf(buf, "0x%02x\n", led->attr_name); \ | ||
498 | up_read(&led->rwsem); \ | ||
499 | return ret; \ | ||
500 | } \ | ||
501 | static ssize_t bd2802_store_##attr_name(struct device *dev, \ | ||
502 | struct device_attribute *attr, const char *buf, size_t count) \ | ||
503 | { \ | ||
504 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ | ||
505 | unsigned long val; \ | ||
506 | int ret; \ | ||
507 | if (!count) \ | ||
508 | return -EINVAL; \ | ||
509 | ret = strict_strtoul(buf, 16, &val); \ | ||
510 | if (ret) \ | ||
511 | return ret; \ | ||
512 | down_write(&led->rwsem); \ | ||
513 | led->attr_name = val; \ | ||
514 | up_write(&led->rwsem); \ | ||
515 | return count; \ | ||
516 | } \ | ||
517 | static struct device_attribute bd2802_##attr_name##_attr = { \ | ||
518 | .attr = { \ | ||
519 | .name = name_str, \ | ||
520 | .mode = 0644, \ | ||
521 | .owner = THIS_MODULE \ | ||
522 | }, \ | ||
523 | .show = bd2802_show_##attr_name, \ | ||
524 | .store = bd2802_store_##attr_name, \ | ||
525 | }; | ||
526 | |||
527 | BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern"); | ||
528 | BD2802_CONTROL_ATTR(rgb_current, "rgb_current"); | ||
529 | |||
530 | static struct device_attribute *bd2802_attributes[] = { | ||
531 | &bd2802_adv_conf_attr, | ||
532 | &bd2802_wave_pattern_attr, | ||
533 | &bd2802_rgb_current_attr, | ||
534 | }; | ||
535 | |||
486 | static void bd2802_led_work(struct work_struct *work) | 536 | static void bd2802_led_work(struct work_struct *work) |
487 | { | 537 | { |
488 | struct bd2802_led *led = container_of(work, struct bd2802_led, work); | 538 | struct bd2802_led *led = container_of(work, struct bd2802_led, work); |
@@ -538,7 +588,6 @@ static int bd2802_register_led_classdev(struct bd2802_led *led) | |||
538 | led->cdev_led1r.brightness = LED_OFF; | 588 | led->cdev_led1r.brightness = LED_OFF; |
539 | led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness; | 589 | led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness; |
540 | led->cdev_led1r.blink_set = bd2802_set_led1r_blink; | 590 | led->cdev_led1r.blink_set = bd2802_set_led1r_blink; |
541 | led->cdev_led1r.flags |= LED_CORE_SUSPENDRESUME; | ||
542 | 591 | ||
543 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1r); | 592 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1r); |
544 | if (ret < 0) { | 593 | if (ret < 0) { |
@@ -551,7 +600,6 @@ static int bd2802_register_led_classdev(struct bd2802_led *led) | |||
551 | led->cdev_led1g.brightness = LED_OFF; | 600 | led->cdev_led1g.brightness = LED_OFF; |
552 | led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness; | 601 | led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness; |
553 | led->cdev_led1g.blink_set = bd2802_set_led1g_blink; | 602 | led->cdev_led1g.blink_set = bd2802_set_led1g_blink; |
554 | led->cdev_led1g.flags |= LED_CORE_SUSPENDRESUME; | ||
555 | 603 | ||
556 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1g); | 604 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1g); |
557 | if (ret < 0) { | 605 | if (ret < 0) { |
@@ -564,7 +612,6 @@ static int bd2802_register_led_classdev(struct bd2802_led *led) | |||
564 | led->cdev_led1b.brightness = LED_OFF; | 612 | led->cdev_led1b.brightness = LED_OFF; |
565 | led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness; | 613 | led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness; |
566 | led->cdev_led1b.blink_set = bd2802_set_led1b_blink; | 614 | led->cdev_led1b.blink_set = bd2802_set_led1b_blink; |
567 | led->cdev_led1b.flags |= LED_CORE_SUSPENDRESUME; | ||
568 | 615 | ||
569 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1b); | 616 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1b); |
570 | if (ret < 0) { | 617 | if (ret < 0) { |
@@ -577,7 +624,6 @@ static int bd2802_register_led_classdev(struct bd2802_led *led) | |||
577 | led->cdev_led2r.brightness = LED_OFF; | 624 | led->cdev_led2r.brightness = LED_OFF; |
578 | led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness; | 625 | led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness; |
579 | led->cdev_led2r.blink_set = bd2802_set_led2r_blink; | 626 | led->cdev_led2r.blink_set = bd2802_set_led2r_blink; |
580 | led->cdev_led2r.flags |= LED_CORE_SUSPENDRESUME; | ||
581 | 627 | ||
582 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2r); | 628 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2r); |
583 | if (ret < 0) { | 629 | if (ret < 0) { |
@@ -590,7 +636,6 @@ static int bd2802_register_led_classdev(struct bd2802_led *led) | |||
590 | led->cdev_led2g.brightness = LED_OFF; | 636 | led->cdev_led2g.brightness = LED_OFF; |
591 | led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness; | 637 | led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness; |
592 | led->cdev_led2g.blink_set = bd2802_set_led2g_blink; | 638 | led->cdev_led2g.blink_set = bd2802_set_led2g_blink; |
593 | led->cdev_led2g.flags |= LED_CORE_SUSPENDRESUME; | ||
594 | 639 | ||
595 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2g); | 640 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2g); |
596 | if (ret < 0) { | 641 | if (ret < 0) { |
@@ -640,7 +685,7 @@ static int __devinit bd2802_probe(struct i2c_client *client, | |||
640 | { | 685 | { |
641 | struct bd2802_led *led; | 686 | struct bd2802_led *led; |
642 | struct bd2802_led_platform_data *pdata; | 687 | struct bd2802_led_platform_data *pdata; |
643 | int ret; | 688 | int ret, i; |
644 | 689 | ||
645 | led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL); | 690 | led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL); |
646 | if (!led) { | 691 | if (!led) { |
@@ -670,13 +715,20 @@ static int __devinit bd2802_probe(struct i2c_client *client, | |||
670 | /* To save the power, reset BD2802 after detecting */ | 715 | /* To save the power, reset BD2802 after detecting */ |
671 | gpio_set_value(led->pdata->reset_gpio, 0); | 716 | gpio_set_value(led->pdata->reset_gpio, 0); |
672 | 717 | ||
718 | /* Default attributes */ | ||
719 | led->wave_pattern = BD2802_PATTERN_HALF; | ||
720 | led->rgb_current = BD2802_CURRENT_032; | ||
721 | |||
673 | init_rwsem(&led->rwsem); | 722 | init_rwsem(&led->rwsem); |
674 | 723 | ||
675 | ret = device_create_file(&client->dev, &bd2802_adv_conf_attr); | 724 | for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) { |
676 | if (ret) { | 725 | ret = device_create_file(&led->client->dev, |
677 | dev_err(&client->dev, "failed to create sysfs file %s\n", | 726 | bd2802_attributes[i]); |
678 | bd2802_adv_conf_attr.attr.name); | 727 | if (ret) { |
679 | goto failed_free; | 728 | dev_err(&led->client->dev, "failed: sysfs file %s\n", |
729 | bd2802_attributes[i]->attr.name); | ||
730 | goto failed_unregister_dev_file; | ||
731 | } | ||
680 | } | 732 | } |
681 | 733 | ||
682 | ret = bd2802_register_led_classdev(led); | 734 | ret = bd2802_register_led_classdev(led); |
@@ -686,7 +738,8 @@ static int __devinit bd2802_probe(struct i2c_client *client, | |||
686 | return 0; | 738 | return 0; |
687 | 739 | ||
688 | failed_unregister_dev_file: | 740 | failed_unregister_dev_file: |
689 | device_remove_file(&client->dev, &bd2802_adv_conf_attr); | 741 | for (i--; i >= 0; i--) |
742 | device_remove_file(&led->client->dev, bd2802_attributes[i]); | ||
690 | failed_free: | 743 | failed_free: |
691 | i2c_set_clientdata(client, NULL); | 744 | i2c_set_clientdata(client, NULL); |
692 | kfree(led); | 745 | kfree(led); |
@@ -697,12 +750,14 @@ failed_free: | |||
697 | static int __exit bd2802_remove(struct i2c_client *client) | 750 | static int __exit bd2802_remove(struct i2c_client *client) |
698 | { | 751 | { |
699 | struct bd2802_led *led = i2c_get_clientdata(client); | 752 | struct bd2802_led *led = i2c_get_clientdata(client); |
753 | int i; | ||
700 | 754 | ||
701 | bd2802_unregister_led_classdev(led); | ||
702 | gpio_set_value(led->pdata->reset_gpio, 0); | 755 | gpio_set_value(led->pdata->reset_gpio, 0); |
756 | bd2802_unregister_led_classdev(led); | ||
703 | if (led->adf_on) | 757 | if (led->adf_on) |
704 | bd2802_disable_adv_conf(led); | 758 | bd2802_disable_adv_conf(led); |
705 | device_remove_file(&client->dev, &bd2802_adv_conf_attr); | 759 | for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) |
760 | device_remove_file(&led->client->dev, bd2802_attributes[i]); | ||
706 | i2c_set_clientdata(client, NULL); | 761 | i2c_set_clientdata(client, NULL); |
707 | kfree(led); | 762 | kfree(led); |
708 | 763 | ||
@@ -723,8 +778,7 @@ static int bd2802_resume(struct i2c_client *client) | |||
723 | struct bd2802_led *led = i2c_get_clientdata(client); | 778 | struct bd2802_led *led = i2c_get_clientdata(client); |
724 | 779 | ||
725 | if (!bd2802_is_all_off(led) || led->adf_on) { | 780 | if (!bd2802_is_all_off(led) || led->adf_on) { |
726 | gpio_set_value(led->pdata->reset_gpio, 1); | 781 | bd2802_reset_cancel(led); |
727 | udelay(100); | ||
728 | bd2802_restore_state(led); | 782 | bd2802_restore_state(led); |
729 | } | 783 | } |
730 | 784 | ||
@@ -762,4 +816,4 @@ module_exit(bd2802_exit); | |||
762 | 816 | ||
763 | MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); | 817 | MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); |
764 | MODULE_DESCRIPTION("BD2802 LED driver"); | 818 | MODULE_DESCRIPTION("BD2802 LED driver"); |
765 | MODULE_LICENSE("GPL"); | 819 | MODULE_LICENSE("GPL v2"); |
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index d2109054de85..6b06638eb5b4 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -76,7 +76,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template, | |||
76 | struct gpio_led_data *led_dat, struct device *parent, | 76 | struct gpio_led_data *led_dat, struct device *parent, |
77 | int (*blink_set)(unsigned, unsigned long *, unsigned long *)) | 77 | int (*blink_set)(unsigned, unsigned long *, unsigned long *)) |
78 | { | 78 | { |
79 | int ret; | 79 | int ret, state; |
80 | 80 | ||
81 | /* skip leds that aren't available */ | 81 | /* skip leds that aren't available */ |
82 | if (!gpio_is_valid(template->gpio)) { | 82 | if (!gpio_is_valid(template->gpio)) { |
@@ -99,11 +99,15 @@ static int __devinit create_gpio_led(const struct gpio_led *template, | |||
99 | led_dat->cdev.blink_set = gpio_blink_set; | 99 | led_dat->cdev.blink_set = gpio_blink_set; |
100 | } | 100 | } |
101 | led_dat->cdev.brightness_set = gpio_led_set; | 101 | led_dat->cdev.brightness_set = gpio_led_set; |
102 | led_dat->cdev.brightness = LED_OFF; | 102 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) |
103 | state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low; | ||
104 | else | ||
105 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | ||
106 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | ||
103 | if (!template->retain_state_suspended) | 107 | if (!template->retain_state_suspended) |
104 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 108 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
105 | 109 | ||
106 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low); | 110 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); |
107 | if (ret < 0) | 111 | if (ret < 0) |
108 | goto err; | 112 | goto err; |
109 | 113 | ||
@@ -129,7 +133,7 @@ static void delete_gpio_led(struct gpio_led_data *led) | |||
129 | } | 133 | } |
130 | 134 | ||
131 | #ifdef CONFIG_LEDS_GPIO_PLATFORM | 135 | #ifdef CONFIG_LEDS_GPIO_PLATFORM |
132 | static int gpio_led_probe(struct platform_device *pdev) | 136 | static int __devinit gpio_led_probe(struct platform_device *pdev) |
133 | { | 137 | { |
134 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 138 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; |
135 | struct gpio_led_data *leds_data; | 139 | struct gpio_led_data *leds_data; |
@@ -223,12 +227,22 @@ static int __devinit of_gpio_leds_probe(struct of_device *ofdev, | |||
223 | memset(&led, 0, sizeof(led)); | 227 | memset(&led, 0, sizeof(led)); |
224 | for_each_child_of_node(np, child) { | 228 | for_each_child_of_node(np, child) { |
225 | enum of_gpio_flags flags; | 229 | enum of_gpio_flags flags; |
230 | const char *state; | ||
226 | 231 | ||
227 | led.gpio = of_get_gpio_flags(child, 0, &flags); | 232 | led.gpio = of_get_gpio_flags(child, 0, &flags); |
228 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; | 233 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; |
229 | led.name = of_get_property(child, "label", NULL) ? : child->name; | 234 | led.name = of_get_property(child, "label", NULL) ? : child->name; |
230 | led.default_trigger = | 235 | led.default_trigger = |
231 | of_get_property(child, "linux,default-trigger", NULL); | 236 | of_get_property(child, "linux,default-trigger", NULL); |
237 | state = of_get_property(child, "default-state", NULL); | ||
238 | if (state) { | ||
239 | if (!strcmp(state, "keep")) | ||
240 | led.default_state = LEDS_GPIO_DEFSTATE_KEEP; | ||
241 | else if(!strcmp(state, "on")) | ||
242 | led.default_state = LEDS_GPIO_DEFSTATE_ON; | ||
243 | else | ||
244 | led.default_state = LEDS_GPIO_DEFSTATE_OFF; | ||
245 | } | ||
232 | 246 | ||
233 | ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++], | 247 | ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++], |
234 | &ofdev->dev, NULL); | 248 | &ofdev->dev, NULL); |
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c new file mode 100644 index 000000000000..5946208ba26e --- /dev/null +++ b/drivers/leds/leds-lp3944.c | |||
@@ -0,0 +1,466 @@ | |||
1 | /* | ||
2 | * leds-lp3944.c - driver for National Semiconductor LP3944 Funlight Chip | ||
3 | * | ||
4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * I2C driver for National Semiconductor LP3944 Funlight Chip | ||
14 | * http://www.national.com/pf/LP/LP3944.html | ||
15 | * | ||
16 | * This helper chip can drive up to 8 leds, with two programmable DIM modes; | ||
17 | * it could even be used as a gpio expander but this driver assumes it is used | ||
18 | * as a led controller. | ||
19 | * | ||
20 | * The DIM modes are used to set _blink_ patterns for leds, the pattern is | ||
21 | * specified supplying two parameters: | ||
22 | * - period: from 0s to 1.6s | ||
23 | * - duty cycle: percentage of the period the led is on, from 0 to 100 | ||
24 | * | ||
25 | * LP3944 can be found on Motorola A910 smartphone, where it drives the rgb | ||
26 | * leds, the camera flash light and the displays backlights. | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/leds.h> | ||
32 | #include <linux/mutex.h> | ||
33 | #include <linux/workqueue.h> | ||
34 | #include <linux/leds-lp3944.h> | ||
35 | |||
36 | /* Read Only Registers */ | ||
37 | #define LP3944_REG_INPUT1 0x00 /* LEDs 0-7 InputRegister (Read Only) */ | ||
38 | #define LP3944_REG_REGISTER1 0x01 /* None (Read Only) */ | ||
39 | |||
40 | #define LP3944_REG_PSC0 0x02 /* Frequency Prescaler 0 (R/W) */ | ||
41 | #define LP3944_REG_PWM0 0x03 /* PWM Register 0 (R/W) */ | ||
42 | #define LP3944_REG_PSC1 0x04 /* Frequency Prescaler 1 (R/W) */ | ||
43 | #define LP3944_REG_PWM1 0x05 /* PWM Register 1 (R/W) */ | ||
44 | #define LP3944_REG_LS0 0x06 /* LEDs 0-3 Selector (R/W) */ | ||
45 | #define LP3944_REG_LS1 0x07 /* LEDs 4-7 Selector (R/W) */ | ||
46 | |||
47 | /* These registers are not used to control leds in LP3944, they can store | ||
48 | * arbitrary values which the chip will ignore. | ||
49 | */ | ||
50 | #define LP3944_REG_REGISTER8 0x08 | ||
51 | #define LP3944_REG_REGISTER9 0x09 | ||
52 | |||
53 | #define LP3944_DIM0 0 | ||
54 | #define LP3944_DIM1 1 | ||
55 | |||
56 | /* period in ms */ | ||
57 | #define LP3944_PERIOD_MIN 0 | ||
58 | #define LP3944_PERIOD_MAX 1600 | ||
59 | |||
60 | /* duty cycle is a percentage */ | ||
61 | #define LP3944_DUTY_CYCLE_MIN 0 | ||
62 | #define LP3944_DUTY_CYCLE_MAX 100 | ||
63 | |||
64 | #define ldev_to_led(c) container_of(c, struct lp3944_led_data, ldev) | ||
65 | |||
66 | /* Saved data */ | ||
67 | struct lp3944_led_data { | ||
68 | u8 id; | ||
69 | enum lp3944_type type; | ||
70 | enum lp3944_status status; | ||
71 | struct led_classdev ldev; | ||
72 | struct i2c_client *client; | ||
73 | struct work_struct work; | ||
74 | }; | ||
75 | |||
76 | struct lp3944_data { | ||
77 | struct mutex lock; | ||
78 | struct i2c_client *client; | ||
79 | struct lp3944_led_data leds[LP3944_LEDS_MAX]; | ||
80 | }; | ||
81 | |||
82 | static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value) | ||
83 | { | ||
84 | int tmp; | ||
85 | |||
86 | tmp = i2c_smbus_read_byte_data(client, reg); | ||
87 | if (tmp < 0) | ||
88 | return -EINVAL; | ||
89 | |||
90 | *value = tmp; | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int lp3944_reg_write(struct i2c_client *client, u8 reg, u8 value) | ||
96 | { | ||
97 | return i2c_smbus_write_byte_data(client, reg, value); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Set the period for DIM status | ||
102 | * | ||
103 | * @client: the i2c client | ||
104 | * @dim: either LP3944_DIM0 or LP3944_DIM1 | ||
105 | * @period: period of a blink, that is a on/off cycle, expressed in ms. | ||
106 | */ | ||
107 | static int lp3944_dim_set_period(struct i2c_client *client, u8 dim, u16 period) | ||
108 | { | ||
109 | u8 psc_reg; | ||
110 | u8 psc_value; | ||
111 | int err; | ||
112 | |||
113 | if (dim == LP3944_DIM0) | ||
114 | psc_reg = LP3944_REG_PSC0; | ||
115 | else if (dim == LP3944_DIM1) | ||
116 | psc_reg = LP3944_REG_PSC1; | ||
117 | else | ||
118 | return -EINVAL; | ||
119 | |||
120 | /* Convert period to Prescaler value */ | ||
121 | if (period > LP3944_PERIOD_MAX) | ||
122 | return -EINVAL; | ||
123 | |||
124 | psc_value = (period * 255) / LP3944_PERIOD_MAX; | ||
125 | |||
126 | err = lp3944_reg_write(client, psc_reg, psc_value); | ||
127 | |||
128 | return err; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * Set the duty cycle for DIM status | ||
133 | * | ||
134 | * @client: the i2c client | ||
135 | * @dim: either LP3944_DIM0 or LP3944_DIM1 | ||
136 | * @duty_cycle: percentage of a period during which a led is ON | ||
137 | */ | ||
138 | static int lp3944_dim_set_dutycycle(struct i2c_client *client, u8 dim, | ||
139 | u8 duty_cycle) | ||
140 | { | ||
141 | u8 pwm_reg; | ||
142 | u8 pwm_value; | ||
143 | int err; | ||
144 | |||
145 | if (dim == LP3944_DIM0) | ||
146 | pwm_reg = LP3944_REG_PWM0; | ||
147 | else if (dim == LP3944_DIM1) | ||
148 | pwm_reg = LP3944_REG_PWM1; | ||
149 | else | ||
150 | return -EINVAL; | ||
151 | |||
152 | /* Convert duty cycle to PWM value */ | ||
153 | if (duty_cycle > LP3944_DUTY_CYCLE_MAX) | ||
154 | return -EINVAL; | ||
155 | |||
156 | pwm_value = (duty_cycle * 255) / LP3944_DUTY_CYCLE_MAX; | ||
157 | |||
158 | err = lp3944_reg_write(client, pwm_reg, pwm_value); | ||
159 | |||
160 | return err; | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * Set the led status | ||
165 | * | ||
166 | * @led: a lp3944_led_data structure | ||
167 | * @status: one of LP3944_LED_STATUS_OFF | ||
168 | * LP3944_LED_STATUS_ON | ||
169 | * LP3944_LED_STATUS_DIM0 | ||
170 | * LP3944_LED_STATUS_DIM1 | ||
171 | */ | ||
172 | static int lp3944_led_set(struct lp3944_led_data *led, u8 status) | ||
173 | { | ||
174 | struct lp3944_data *data = i2c_get_clientdata(led->client); | ||
175 | u8 id = led->id; | ||
176 | u8 reg; | ||
177 | u8 val = 0; | ||
178 | int err; | ||
179 | |||
180 | dev_dbg(&led->client->dev, "%s: %s, status before normalization:%d\n", | ||
181 | __func__, led->ldev.name, status); | ||
182 | |||
183 | switch (id) { | ||
184 | case LP3944_LED0: | ||
185 | case LP3944_LED1: | ||
186 | case LP3944_LED2: | ||
187 | case LP3944_LED3: | ||
188 | reg = LP3944_REG_LS0; | ||
189 | break; | ||
190 | case LP3944_LED4: | ||
191 | case LP3944_LED5: | ||
192 | case LP3944_LED6: | ||
193 | case LP3944_LED7: | ||
194 | id -= LP3944_LED4; | ||
195 | reg = LP3944_REG_LS1; | ||
196 | break; | ||
197 | default: | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | |||
201 | if (status > LP3944_LED_STATUS_DIM1) | ||
202 | return -EINVAL; | ||
203 | |||
204 | /* invert only 0 and 1, leave unchanged the other values, | ||
205 | * remember we are abusing status to set blink patterns | ||
206 | */ | ||
207 | if (led->type == LP3944_LED_TYPE_LED_INVERTED && status < 2) | ||
208 | status = 1 - status; | ||
209 | |||
210 | mutex_lock(&data->lock); | ||
211 | lp3944_reg_read(led->client, reg, &val); | ||
212 | |||
213 | val &= ~(LP3944_LED_STATUS_MASK << (id << 1)); | ||
214 | val |= (status << (id << 1)); | ||
215 | |||
216 | dev_dbg(&led->client->dev, "%s: %s, reg:%d id:%d status:%d val:%#x\n", | ||
217 | __func__, led->ldev.name, reg, id, status, val); | ||
218 | |||
219 | /* set led status */ | ||
220 | err = lp3944_reg_write(led->client, reg, val); | ||
221 | mutex_unlock(&data->lock); | ||
222 | |||
223 | return err; | ||
224 | } | ||
225 | |||
226 | static int lp3944_led_set_blink(struct led_classdev *led_cdev, | ||
227 | unsigned long *delay_on, | ||
228 | unsigned long *delay_off) | ||
229 | { | ||
230 | struct lp3944_led_data *led = ldev_to_led(led_cdev); | ||
231 | u16 period; | ||
232 | u8 duty_cycle; | ||
233 | int err; | ||
234 | |||
235 | /* units are in ms */ | ||
236 | if (*delay_on + *delay_off > LP3944_PERIOD_MAX) | ||
237 | return -EINVAL; | ||
238 | |||
239 | if (*delay_on == 0 && *delay_off == 0) { | ||
240 | /* Special case: the leds subsystem requires a default user | ||
241 | * friendly blink pattern for the LED. Let's blink the led | ||
242 | * slowly (1Hz). | ||
243 | */ | ||
244 | *delay_on = 500; | ||
245 | *delay_off = 500; | ||
246 | } | ||
247 | |||
248 | period = (*delay_on) + (*delay_off); | ||
249 | |||
250 | /* duty_cycle is the percentage of period during which the led is ON */ | ||
251 | duty_cycle = 100 * (*delay_on) / period; | ||
252 | |||
253 | /* invert duty cycle for inverted leds, this has the same effect of | ||
254 | * swapping delay_on and delay_off | ||
255 | */ | ||
256 | if (led->type == LP3944_LED_TYPE_LED_INVERTED) | ||
257 | duty_cycle = 100 - duty_cycle; | ||
258 | |||
259 | /* NOTE: using always the first DIM mode, this means that all leds | ||
260 | * will have the same blinking pattern. | ||
261 | * | ||
262 | * We could find a way later to have two leds blinking in hardware | ||
263 | * with different patterns at the same time, falling back to software | ||
264 | * control for the other ones. | ||
265 | */ | ||
266 | err = lp3944_dim_set_period(led->client, LP3944_DIM0, period); | ||
267 | if (err) | ||
268 | return err; | ||
269 | |||
270 | err = lp3944_dim_set_dutycycle(led->client, LP3944_DIM0, duty_cycle); | ||
271 | if (err) | ||
272 | return err; | ||
273 | |||
274 | dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n", | ||
275 | __func__); | ||
276 | |||
277 | led->status = LP3944_LED_STATUS_DIM0; | ||
278 | schedule_work(&led->work); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static void lp3944_led_set_brightness(struct led_classdev *led_cdev, | ||
284 | enum led_brightness brightness) | ||
285 | { | ||
286 | struct lp3944_led_data *led = ldev_to_led(led_cdev); | ||
287 | |||
288 | dev_dbg(&led->client->dev, "%s: %s, %d\n", | ||
289 | __func__, led_cdev->name, brightness); | ||
290 | |||
291 | led->status = brightness; | ||
292 | schedule_work(&led->work); | ||
293 | } | ||
294 | |||
295 | static void lp3944_led_work(struct work_struct *work) | ||
296 | { | ||
297 | struct lp3944_led_data *led; | ||
298 | |||
299 | led = container_of(work, struct lp3944_led_data, work); | ||
300 | lp3944_led_set(led, led->status); | ||
301 | } | ||
302 | |||
303 | static int lp3944_configure(struct i2c_client *client, | ||
304 | struct lp3944_data *data, | ||
305 | struct lp3944_platform_data *pdata) | ||
306 | { | ||
307 | int i, err = 0; | ||
308 | |||
309 | for (i = 0; i < pdata->leds_size; i++) { | ||
310 | struct lp3944_led *pled = &pdata->leds[i]; | ||
311 | struct lp3944_led_data *led = &data->leds[i]; | ||
312 | led->client = client; | ||
313 | led->id = i; | ||
314 | |||
315 | switch (pled->type) { | ||
316 | |||
317 | case LP3944_LED_TYPE_LED: | ||
318 | case LP3944_LED_TYPE_LED_INVERTED: | ||
319 | led->type = pled->type; | ||
320 | led->status = pled->status; | ||
321 | led->ldev.name = pled->name; | ||
322 | led->ldev.max_brightness = 1; | ||
323 | led->ldev.brightness_set = lp3944_led_set_brightness; | ||
324 | led->ldev.blink_set = lp3944_led_set_blink; | ||
325 | led->ldev.flags = LED_CORE_SUSPENDRESUME; | ||
326 | |||
327 | INIT_WORK(&led->work, lp3944_led_work); | ||
328 | err = led_classdev_register(&client->dev, &led->ldev); | ||
329 | if (err < 0) { | ||
330 | dev_err(&client->dev, | ||
331 | "couldn't register LED %s\n", | ||
332 | led->ldev.name); | ||
333 | goto exit; | ||
334 | } | ||
335 | |||
336 | /* to expose the default value to userspace */ | ||
337 | led->ldev.brightness = led->status; | ||
338 | |||
339 | /* Set the default led status */ | ||
340 | err = lp3944_led_set(led, led->status); | ||
341 | if (err < 0) { | ||
342 | dev_err(&client->dev, | ||
343 | "%s couldn't set STATUS %d\n", | ||
344 | led->ldev.name, led->status); | ||
345 | goto exit; | ||
346 | } | ||
347 | break; | ||
348 | |||
349 | case LP3944_LED_TYPE_NONE: | ||
350 | default: | ||
351 | break; | ||
352 | |||
353 | } | ||
354 | } | ||
355 | return 0; | ||
356 | |||
357 | exit: | ||
358 | if (i > 0) | ||
359 | for (i = i - 1; i >= 0; i--) | ||
360 | switch (pdata->leds[i].type) { | ||
361 | |||
362 | case LP3944_LED_TYPE_LED: | ||
363 | case LP3944_LED_TYPE_LED_INVERTED: | ||
364 | led_classdev_unregister(&data->leds[i].ldev); | ||
365 | cancel_work_sync(&data->leds[i].work); | ||
366 | break; | ||
367 | |||
368 | case LP3944_LED_TYPE_NONE: | ||
369 | default: | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | return err; | ||
374 | } | ||
375 | |||
376 | static int __devinit lp3944_probe(struct i2c_client *client, | ||
377 | const struct i2c_device_id *id) | ||
378 | { | ||
379 | struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data; | ||
380 | struct lp3944_data *data; | ||
381 | |||
382 | if (lp3944_pdata == NULL) { | ||
383 | dev_err(&client->dev, "no platform data\n"); | ||
384 | return -EINVAL; | ||
385 | } | ||
386 | |||
387 | /* Let's see whether this adapter can support what we need. */ | ||
388 | if (!i2c_check_functionality(client->adapter, | ||
389 | I2C_FUNC_SMBUS_BYTE_DATA)) { | ||
390 | dev_err(&client->dev, "insufficient functionality!\n"); | ||
391 | return -ENODEV; | ||
392 | } | ||
393 | |||
394 | data = kzalloc(sizeof(struct lp3944_data), GFP_KERNEL); | ||
395 | if (!data) | ||
396 | return -ENOMEM; | ||
397 | |||
398 | data->client = client; | ||
399 | i2c_set_clientdata(client, data); | ||
400 | |||
401 | mutex_init(&data->lock); | ||
402 | |||
403 | dev_info(&client->dev, "lp3944 enabled\n"); | ||
404 | |||
405 | lp3944_configure(client, data, lp3944_pdata); | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static int __devexit lp3944_remove(struct i2c_client *client) | ||
410 | { | ||
411 | struct lp3944_platform_data *pdata = client->dev.platform_data; | ||
412 | struct lp3944_data *data = i2c_get_clientdata(client); | ||
413 | int i; | ||
414 | |||
415 | for (i = 0; i < pdata->leds_size; i++) | ||
416 | switch (data->leds[i].type) { | ||
417 | case LP3944_LED_TYPE_LED: | ||
418 | case LP3944_LED_TYPE_LED_INVERTED: | ||
419 | led_classdev_unregister(&data->leds[i].ldev); | ||
420 | cancel_work_sync(&data->leds[i].work); | ||
421 | break; | ||
422 | |||
423 | case LP3944_LED_TYPE_NONE: | ||
424 | default: | ||
425 | break; | ||
426 | } | ||
427 | |||
428 | kfree(data); | ||
429 | i2c_set_clientdata(client, NULL); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | /* lp3944 i2c driver struct */ | ||
435 | static const struct i2c_device_id lp3944_id[] = { | ||
436 | {"lp3944", 0}, | ||
437 | {} | ||
438 | }; | ||
439 | |||
440 | MODULE_DEVICE_TABLE(i2c, lp3944_id); | ||
441 | |||
442 | static struct i2c_driver lp3944_driver = { | ||
443 | .driver = { | ||
444 | .name = "lp3944", | ||
445 | }, | ||
446 | .probe = lp3944_probe, | ||
447 | .remove = __devexit_p(lp3944_remove), | ||
448 | .id_table = lp3944_id, | ||
449 | }; | ||
450 | |||
451 | static int __init lp3944_module_init(void) | ||
452 | { | ||
453 | return i2c_add_driver(&lp3944_driver); | ||
454 | } | ||
455 | |||
456 | static void __exit lp3944_module_exit(void) | ||
457 | { | ||
458 | i2c_del_driver(&lp3944_driver); | ||
459 | } | ||
460 | |||
461 | module_init(lp3944_module_init); | ||
462 | module_exit(lp3944_module_exit); | ||
463 | |||
464 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | ||
465 | MODULE_DESCRIPTION("LP3944 Fun Light Chip"); | ||
466 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 3937244fdcab..dba8921240f2 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c | |||
@@ -35,7 +35,7 @@ struct pca9532_data { | |||
35 | struct pca9532_led leds[16]; | 35 | struct pca9532_led leds[16]; |
36 | struct mutex update_lock; | 36 | struct mutex update_lock; |
37 | struct input_dev *idev; | 37 | struct input_dev *idev; |
38 | struct work_struct work; | 38 | struct work_struct work; |
39 | u8 pwm[2]; | 39 | u8 pwm[2]; |
40 | u8 psc[2]; | 40 | u8 psc[2]; |
41 | }; | 41 | }; |
@@ -87,14 +87,14 @@ static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, | |||
87 | if (b > 0xFF) | 87 | if (b > 0xFF) |
88 | return -EINVAL; | 88 | return -EINVAL; |
89 | data->pwm[pwm] = b; | 89 | data->pwm[pwm] = b; |
90 | data->psc[pwm] = blink; | 90 | data->psc[pwm] = blink; |
91 | return 0; | 91 | return 0; |
92 | } | 92 | } |
93 | 93 | ||
94 | static int pca9532_setpwm(struct i2c_client *client, int pwm) | 94 | static int pca9532_setpwm(struct i2c_client *client, int pwm) |
95 | { | 95 | { |
96 | struct pca9532_data *data = i2c_get_clientdata(client); | 96 | struct pca9532_data *data = i2c_get_clientdata(client); |
97 | mutex_lock(&data->update_lock); | 97 | mutex_lock(&data->update_lock); |
98 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), | 98 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), |
99 | data->pwm[pwm]); | 99 | data->pwm[pwm]); |
100 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), | 100 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), |
@@ -132,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev, | |||
132 | led->state = PCA9532_ON; | 132 | led->state = PCA9532_ON; |
133 | else { | 133 | else { |
134 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ | 134 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ |
135 | err = pca9532_calcpwm(led->client, 0, 0, value); | 135 | err = pca9532_calcpwm(led->client, 0, 0, value); |
136 | if (err) | 136 | if (err) |
137 | return; /* XXX: led api doesn't allow error code? */ | 137 | return; /* XXX: led api doesn't allow error code? */ |
138 | } | 138 | } |
139 | schedule_work(&led->work); | 139 | schedule_work(&led->work); |
140 | } | 140 | } |
141 | 141 | ||
142 | static int pca9532_set_blink(struct led_classdev *led_cdev, | 142 | static int pca9532_set_blink(struct led_classdev *led_cdev, |
@@ -145,7 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, | |||
145 | struct pca9532_led *led = ldev_to_led(led_cdev); | 145 | struct pca9532_led *led = ldev_to_led(led_cdev); |
146 | struct i2c_client *client = led->client; | 146 | struct i2c_client *client = led->client; |
147 | int psc; | 147 | int psc; |
148 | int err = 0; | 148 | int err = 0; |
149 | 149 | ||
150 | if (*delay_on == 0 && *delay_off == 0) { | 150 | if (*delay_on == 0 && *delay_off == 0) { |
151 | /* led subsystem ask us for a blink rate */ | 151 | /* led subsystem ask us for a blink rate */ |
@@ -157,11 +157,11 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, | |||
157 | 157 | ||
158 | /* Thecus specific: only use PSC/PWM 0 */ | 158 | /* Thecus specific: only use PSC/PWM 0 */ |
159 | psc = (*delay_on * 152-1)/1000; | 159 | psc = (*delay_on * 152-1)/1000; |
160 | err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); | 160 | err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); |
161 | if (err) | 161 | if (err) |
162 | return err; | 162 | return err; |
163 | schedule_work(&led->work); | 163 | schedule_work(&led->work); |
164 | return 0; | 164 | return 0; |
165 | } | 165 | } |
166 | 166 | ||
167 | static int pca9532_event(struct input_dev *dev, unsigned int type, | 167 | static int pca9532_event(struct input_dev *dev, unsigned int type, |
@@ -178,15 +178,15 @@ static int pca9532_event(struct input_dev *dev, unsigned int type, | |||
178 | else | 178 | else |
179 | data->pwm[1] = 0; | 179 | data->pwm[1] = 0; |
180 | 180 | ||
181 | schedule_work(&data->work); | 181 | schedule_work(&data->work); |
182 | 182 | ||
183 | return 0; | 183 | return 0; |
184 | } | 184 | } |
185 | 185 | ||
186 | static void pca9532_input_work(struct work_struct *work) | 186 | static void pca9532_input_work(struct work_struct *work) |
187 | { | 187 | { |
188 | struct pca9532_data *data; | 188 | struct pca9532_data *data; |
189 | data = container_of(work, struct pca9532_data, work); | 189 | data = container_of(work, struct pca9532_data, work); |
190 | mutex_lock(&data->update_lock); | 190 | mutex_lock(&data->update_lock); |
191 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), | 191 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), |
192 | data->pwm[1]); | 192 | data->pwm[1]); |
@@ -195,11 +195,11 @@ static void pca9532_input_work(struct work_struct *work) | |||
195 | 195 | ||
196 | static void pca9532_led_work(struct work_struct *work) | 196 | static void pca9532_led_work(struct work_struct *work) |
197 | { | 197 | { |
198 | struct pca9532_led *led; | 198 | struct pca9532_led *led; |
199 | led = container_of(work, struct pca9532_led, work); | 199 | led = container_of(work, struct pca9532_led, work); |
200 | if (led->state == PCA9532_PWM0) | 200 | if (led->state == PCA9532_PWM0) |
201 | pca9532_setpwm(led->client, 0); | 201 | pca9532_setpwm(led->client, 0); |
202 | pca9532_setled(led); | 202 | pca9532_setled(led); |
203 | } | 203 | } |
204 | 204 | ||
205 | static int pca9532_configure(struct i2c_client *client, | 205 | static int pca9532_configure(struct i2c_client *client, |
@@ -232,7 +232,7 @@ static int pca9532_configure(struct i2c_client *client, | |||
232 | led->ldev.brightness = LED_OFF; | 232 | led->ldev.brightness = LED_OFF; |
233 | led->ldev.brightness_set = pca9532_set_brightness; | 233 | led->ldev.brightness_set = pca9532_set_brightness; |
234 | led->ldev.blink_set = pca9532_set_blink; | 234 | led->ldev.blink_set = pca9532_set_blink; |
235 | INIT_WORK(&led->work, pca9532_led_work); | 235 | INIT_WORK(&led->work, pca9532_led_work); |
236 | err = led_classdev_register(&client->dev, &led->ldev); | 236 | err = led_classdev_register(&client->dev, &led->ldev); |
237 | if (err < 0) { | 237 | if (err < 0) { |
238 | dev_err(&client->dev, | 238 | dev_err(&client->dev, |
@@ -262,11 +262,11 @@ static int pca9532_configure(struct i2c_client *client, | |||
262 | BIT_MASK(SND_TONE); | 262 | BIT_MASK(SND_TONE); |
263 | data->idev->event = pca9532_event; | 263 | data->idev->event = pca9532_event; |
264 | input_set_drvdata(data->idev, data); | 264 | input_set_drvdata(data->idev, data); |
265 | INIT_WORK(&data->work, pca9532_input_work); | 265 | INIT_WORK(&data->work, pca9532_input_work); |
266 | err = input_register_device(data->idev); | 266 | err = input_register_device(data->idev); |
267 | if (err) { | 267 | if (err) { |
268 | input_free_device(data->idev); | 268 | input_free_device(data->idev); |
269 | cancel_work_sync(&data->work); | 269 | cancel_work_sync(&data->work); |
270 | data->idev = NULL; | 270 | data->idev = NULL; |
271 | goto exit; | 271 | goto exit; |
272 | } | 272 | } |
@@ -283,13 +283,13 @@ exit: | |||
283 | break; | 283 | break; |
284 | case PCA9532_TYPE_LED: | 284 | case PCA9532_TYPE_LED: |
285 | led_classdev_unregister(&data->leds[i].ldev); | 285 | led_classdev_unregister(&data->leds[i].ldev); |
286 | cancel_work_sync(&data->leds[i].work); | 286 | cancel_work_sync(&data->leds[i].work); |
287 | break; | 287 | break; |
288 | case PCA9532_TYPE_N2100_BEEP: | 288 | case PCA9532_TYPE_N2100_BEEP: |
289 | if (data->idev != NULL) { | 289 | if (data->idev != NULL) { |
290 | input_unregister_device(data->idev); | 290 | input_unregister_device(data->idev); |
291 | input_free_device(data->idev); | 291 | input_free_device(data->idev); |
292 | cancel_work_sync(&data->work); | 292 | cancel_work_sync(&data->work); |
293 | data->idev = NULL; | 293 | data->idev = NULL; |
294 | } | 294 | } |
295 | break; | 295 | break; |
@@ -340,13 +340,13 @@ static int pca9532_remove(struct i2c_client *client) | |||
340 | break; | 340 | break; |
341 | case PCA9532_TYPE_LED: | 341 | case PCA9532_TYPE_LED: |
342 | led_classdev_unregister(&data->leds[i].ldev); | 342 | led_classdev_unregister(&data->leds[i].ldev); |
343 | cancel_work_sync(&data->leds[i].work); | 343 | cancel_work_sync(&data->leds[i].work); |
344 | break; | 344 | break; |
345 | case PCA9532_TYPE_N2100_BEEP: | 345 | case PCA9532_TYPE_N2100_BEEP: |
346 | if (data->idev != NULL) { | 346 | if (data->idev != NULL) { |
347 | input_unregister_device(data->idev); | 347 | input_unregister_device(data->idev); |
348 | input_free_device(data->idev); | 348 | input_free_device(data->idev); |
349 | cancel_work_sync(&data->work); | 349 | cancel_work_sync(&data->work); |
350 | data->idev = NULL; | 350 | data->idev = NULL; |
351 | } | 351 | } |
352 | break; | 352 | break; |
diff --git a/include/linux/leds-lp3944.h b/include/linux/leds-lp3944.h new file mode 100644 index 000000000000..afc9f9fd70f5 --- /dev/null +++ b/include/linux/leds-lp3944.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * leds-lp3944.h - platform data structure for lp3944 led controller | ||
3 | * | ||
4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #ifndef __LINUX_LEDS_LP3944_H | ||
13 | #define __LINUX_LEDS_LP3944_H | ||
14 | |||
15 | #include <linux/leds.h> | ||
16 | #include <linux/workqueue.h> | ||
17 | |||
18 | #define LP3944_LED0 0 | ||
19 | #define LP3944_LED1 1 | ||
20 | #define LP3944_LED2 2 | ||
21 | #define LP3944_LED3 3 | ||
22 | #define LP3944_LED4 4 | ||
23 | #define LP3944_LED5 5 | ||
24 | #define LP3944_LED6 6 | ||
25 | #define LP3944_LED7 7 | ||
26 | #define LP3944_LEDS_MAX 8 | ||
27 | |||
28 | #define LP3944_LED_STATUS_MASK 0x03 | ||
29 | enum lp3944_status { | ||
30 | LP3944_LED_STATUS_OFF = 0x0, | ||
31 | LP3944_LED_STATUS_ON = 0x1, | ||
32 | LP3944_LED_STATUS_DIM0 = 0x2, | ||
33 | LP3944_LED_STATUS_DIM1 = 0x3 | ||
34 | }; | ||
35 | |||
36 | enum lp3944_type { | ||
37 | LP3944_LED_TYPE_NONE, | ||
38 | LP3944_LED_TYPE_LED, | ||
39 | LP3944_LED_TYPE_LED_INVERTED, | ||
40 | }; | ||
41 | |||
42 | struct lp3944_led { | ||
43 | char *name; | ||
44 | enum lp3944_type type; | ||
45 | enum lp3944_status status; | ||
46 | }; | ||
47 | |||
48 | struct lp3944_platform_data { | ||
49 | struct lp3944_led leds[LP3944_LEDS_MAX]; | ||
50 | u8 leds_size; | ||
51 | }; | ||
52 | |||
53 | #endif /* __LINUX_LEDS_LP3944_H */ | ||
diff --git a/include/linux/leds.h b/include/linux/leds.h index 376fe07732ea..d8bf9665e70c 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h | |||
@@ -45,7 +45,10 @@ struct led_classdev { | |||
45 | /* Get LED brightness level */ | 45 | /* Get LED brightness level */ |
46 | enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); | 46 | enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); |
47 | 47 | ||
48 | /* Activate hardware accelerated blink */ | 48 | /* Activate hardware accelerated blink, delays are in |
49 | * miliseconds and if none is provided then a sensible default | ||
50 | * should be chosen. The call can adjust the timings if it can't | ||
51 | * match the values specified exactly. */ | ||
49 | int (*blink_set)(struct led_classdev *led_cdev, | 52 | int (*blink_set)(struct led_classdev *led_cdev, |
50 | unsigned long *delay_on, | 53 | unsigned long *delay_on, |
51 | unsigned long *delay_off); | 54 | unsigned long *delay_off); |
@@ -141,9 +144,14 @@ struct gpio_led { | |||
141 | const char *name; | 144 | const char *name; |
142 | const char *default_trigger; | 145 | const char *default_trigger; |
143 | unsigned gpio; | 146 | unsigned gpio; |
144 | u8 active_low : 1; | 147 | unsigned active_low : 1; |
145 | u8 retain_state_suspended : 1; | 148 | unsigned retain_state_suspended : 1; |
149 | unsigned default_state : 2; | ||
150 | /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ | ||
146 | }; | 151 | }; |
152 | #define LEDS_GPIO_DEFSTATE_OFF 0 | ||
153 | #define LEDS_GPIO_DEFSTATE_ON 1 | ||
154 | #define LEDS_GPIO_DEFSTATE_KEEP 2 | ||
147 | 155 | ||
148 | struct gpio_led_platform_data { | 156 | struct gpio_led_platform_data { |
149 | int num_leds; | 157 | int num_leds; |