Commit d7d717fa authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds

* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds:
  leds: ledtrig-timer - on deactivation hardware blinking should be disabled
  leds: Add suspend/resume to the core class
  leds: Add WM8350 LED driver
  leds: leds-pcs9532 - Move i2c work to a workqueque
  leds: leds-pca9532 - fix memory leak and properly handle errors
  leds: Fix wrong loop direction on removal in leds-ams-delta
  leds: fix Cobalt Raq LED dependency
  leds: Fix sparse warning in leds-ams-delta
  leds: Fixup kdoc comment to match parameter names
  leds: Make header variable naming consistent
  leds: eds-pca9532: mark pca9532_event() static
  leds: ALIX.2 LEDs driver
parents b64dc5a4 ce70a245
...@@ -63,6 +63,12 @@ config LEDS_WRAP ...@@ -63,6 +63,12 @@ config LEDS_WRAP
help help
This option enables support for the PCEngines WRAP programmable LEDs. This option enables support for the PCEngines WRAP programmable LEDs.
config LEDS_ALIX2
tristate "LED Support for ALIX.2 and ALIX.3 series"
depends on LEDS_CLASS && X86 && EXPERIMENTAL
help
This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs.
config LEDS_H1940 config LEDS_H1940
tristate "LED Support for iPAQ H1940 device" tristate "LED Support for iPAQ H1940 device"
depends on LEDS_CLASS && ARCH_H1940 depends on LEDS_CLASS && ARCH_H1940
...@@ -77,7 +83,7 @@ config LEDS_COBALT_QUBE ...@@ -77,7 +83,7 @@ config LEDS_COBALT_QUBE
config LEDS_COBALT_RAQ config LEDS_COBALT_RAQ
bool "LED Support for the Cobalt Raq series" bool "LED Support for the Cobalt Raq series"
depends on LEDS_CLASS && MIPS_COBALT depends on LEDS_CLASS=y && MIPS_COBALT
select LEDS_TRIGGERS select LEDS_TRIGGERS
help help
This option enables support for the Cobalt Raq series LEDs. This option enables support for the Cobalt Raq series LEDs.
...@@ -158,6 +164,13 @@ config LEDS_PCA955X ...@@ -158,6 +164,13 @@ config LEDS_PCA955X
LED driver chips accessed via the I2C bus. Supported LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553. devices include PCA9550, PCA9551, PCA9552, and PCA9553.
config LEDS_WM8350
tristate "LED Support for WM8350 AudioPlus PMIC"
depends on LEDS_CLASS && MFD_WM8350
help
This option enables support for LEDs driven by the Wolfson
Microelectronics WM8350 AudioPlus PMIC.
config LEDS_DA903X config LEDS_DA903X
tristate "LED Support for DA9030/DA9034 PMIC" tristate "LED Support for DA9030/DA9034 PMIC"
depends on LEDS_CLASS && PMIC_DA903X depends on LEDS_CLASS && PMIC_DA903X
......
...@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o ...@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
...@@ -23,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o ...@@ -23,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
# LED Triggers # LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
......
...@@ -91,9 +91,29 @@ void led_classdev_resume(struct led_classdev *led_cdev) ...@@ -91,9 +91,29 @@ void led_classdev_resume(struct led_classdev *led_cdev)
} }
EXPORT_SYMBOL_GPL(led_classdev_resume); EXPORT_SYMBOL_GPL(led_classdev_resume);
static int led_suspend(struct device *dev, pm_message_t state)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_suspend(led_cdev);
return 0;
}
static int led_resume(struct device *dev)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_resume(led_cdev);
return 0;
}
/** /**
* led_classdev_register - register a new object of led_classdev class. * led_classdev_register - register a new object of led_classdev class.
* @dev: The device to register. * @parent: The device to register.
* @led_cdev: the led_classdev structure for this device. * @led_cdev: the led_classdev structure for this device.
*/ */
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
...@@ -174,6 +194,8 @@ static int __init leds_init(void) ...@@ -174,6 +194,8 @@ static int __init leds_init(void)
leds_class = class_create(THIS_MODULE, "leds"); leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class)) if (IS_ERR(leds_class))
return PTR_ERR(leds_class); return PTR_ERR(leds_class);
leds_class->suspend = led_suspend;
leds_class->resume = led_resume;
return 0; return 0;
} }
......
/*
* LEDs driver for PCEngines ALIX.2 and ALIX.3
*
* Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/string.h>
static int force = 0;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Assume system has ALIX.2 style LEDs");
struct alix_led {
struct led_classdev cdev;
unsigned short port;
unsigned int on_value;
unsigned int off_value;
};
static void alix_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct alix_led *led_dev =
container_of(led_cdev, struct alix_led, cdev);
if (brightness)
outl(led_dev->on_value, led_dev->port);
else
outl(led_dev->off_value, led_dev->port);
}
static struct alix_led alix_leds[] = {
{
.cdev = {
.name = "alix:1",
.brightness_set = alix_led_set,
},
.port = 0x6100,
.on_value = 1 << 22,
.off_value = 1 << 6,
},
{
.cdev = {
.name = "alix:2",
.brightness_set = alix_led_set,
},
.port = 0x6180,
.on_value = 1 << 25,
.off_value = 1 << 9,
},
{
.cdev = {
.name = "alix:3",
.brightness_set = alix_led_set,
},
.port = 0x6180,
.on_value = 1 << 27,
.off_value = 1 << 11,
},
};
static int __init alix_led_probe(struct platform_device *pdev)
{
int i;
int ret;
for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
if (ret < 0)
goto fail;
}
return 0;
fail:
while (--i >= 0)
led_classdev_unregister(&alix_leds[i].cdev);
return ret;
}
static int alix_led_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
led_classdev_unregister(&alix_leds[i].cdev);
return 0;
}
static struct platform_driver alix_led_driver = {
.remove = alix_led_remove,
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
},
};
static int __init alix_present(void)
{
const unsigned long bios_phys = 0x000f0000;
const size_t bios_len = 0x00010000;
const char alix_sig[] = "PC Engines ALIX.";
const size_t alix_sig_len = sizeof(alix_sig) - 1;
const char *bios_virt;
const char *scan_end;
const char *p;
int ret = 0;
if (force) {
printk(KERN_NOTICE "%s: forced to skip BIOS test, "
"assume system has ALIX.2 style LEDs\n",
KBUILD_MODNAME);
ret = 1;
goto out;
}
bios_virt = phys_to_virt(bios_phys);
scan_end = bios_virt + bios_len - (alix_sig_len + 2);
for (p = bios_virt; p < scan_end; p++) {
const char *tail;
if (memcmp(p, alix_sig, alix_sig_len) != 0) {
continue;
}
tail = p + alix_sig_len;
if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
printk(KERN_INFO
"%s: system is recognized as \"%s\"\n",
KBUILD_MODNAME, p);
ret = 1;
break;
}
}
out:
return ret;
}
static struct platform_device *pdev;
static int __init alix_led_init(void)
{
int ret;
if (!alix_present()) {
ret = -ENODEV;
goto out;
}
pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
if (!IS_ERR(pdev)) {
ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
if (ret)
platform_device_unregister(pdev);
} else
ret = PTR_ERR(pdev);
out:
return ret;
}
static void __exit alix_led_exit(void)
{
platform_device_unregister(pdev);
platform_driver_unregister(&alix_led_driver);
}
module_init(alix_led_init);
module_exit(alix_led_exit);
MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
MODULE_LICENSE("GPL");
...@@ -79,37 +79,12 @@ static struct ams_delta_led ams_delta_leds[] = { ...@@ -79,37 +79,12 @@ static struct ams_delta_led ams_delta_leds[] = {
}, },
}; };
#ifdef CONFIG_PM
static int ams_delta_led_suspend(struct platform_device *dev,
pm_message_t state)
{
int i;
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
led_classdev_suspend(&ams_delta_leds[i].cdev);
return 0;
}
static int ams_delta_led_resume(struct platform_device *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
led_classdev_resume(&ams_delta_leds[i].cdev);
return 0;
}
#else
#define ams_delta_led_suspend NULL
#define ams_delta_led_resume NULL
#endif
static int ams_delta_led_probe(struct platform_device *pdev) static int ams_delta_led_probe(struct platform_device *pdev)
{ {
int i, ret; int i, ret;
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) { for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, ret = led_classdev_register(&pdev->dev,
&ams_delta_leds[i].cdev); &ams_delta_leds[i].cdev);
if (ret < 0) if (ret < 0)
...@@ -127,7 +102,7 @@ static int ams_delta_led_remove(struct platform_device *pdev) ...@@ -127,7 +102,7 @@ static int ams_delta_led_remove(struct platform_device *pdev)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i--) for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
led_classdev_unregister(&ams_delta_leds[i].cdev); led_classdev_unregister(&ams_delta_leds[i].cdev);
return 0; return 0;
...@@ -136,8 +111,6 @@ static int ams_delta_led_remove(struct platform_device *pdev) ...@@ -136,8 +111,6 @@ static int ams_delta_led_remove(struct platform_device *pdev)
static struct platform_driver ams_delta_led_driver = { static struct platform_driver ams_delta_led_driver = {
.probe = ams_delta_led_probe, .probe = ams_delta_led_probe,
.remove = ams_delta_led_remove, .remove = ams_delta_led_remove,
.suspend = ams_delta_led_suspend,
.resume = ams_delta_led_resume,
.driver = { .driver = {
.name = "ams-delta-led", .name = "ams-delta-led",
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -151,7 +124,7 @@ static int __init ams_delta_led_init(void) ...@@ -151,7 +124,7 @@ static int __init ams_delta_led_init(void)
static void __exit ams_delta_led_exit(void) static void __exit ams_delta_led_exit(void)
{ {
return platform_driver_unregister(&ams_delta_led_driver); platform_driver_unregister(&ams_delta_led_driver);
} }
module_init(ams_delta_led_init); module_init(ams_delta_led_init);
......
...@@ -142,6 +142,7 @@ static struct led_classdev clevo_mail_led = { ...@@ -142,6 +142,7 @@ static struct led_classdev clevo_mail_led = {
.name = "clevo::mail", .name = "clevo::mail",
.brightness_set = clevo_mail_led_set, .brightness_set = clevo_mail_led_set,
.blink_set = clevo_mail_led_blink, .blink_set = clevo_mail_led_blink,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static int __init clevo_mail_led_probe(struct platform_device *pdev) static int __init clevo_mail_led_probe(struct platform_device *pdev)
...@@ -155,29 +156,9 @@ static int clevo_mail_led_remove(struct platform_device *pdev) ...@@ -155,29 +156,9 @@ static int clevo_mail_led_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int clevo_mail_led_suspend(struct platform_device *dev,
pm_message_t state)
{
led_classdev_suspend(&clevo_mail_led);
return 0;
}
static int clevo_mail_led_resume(struct platform_device *dev)
{
led_classdev_resume(&clevo_mail_led);
return 0;
}
#else
#define clevo_mail_led_suspend NULL
#define clevo_mail_led_resume NULL
#endif
static struct platform_driver clevo_mail_led_driver = { static struct platform_driver clevo_mail_led_driver = {
.probe = clevo_mail_led_probe, .probe = clevo_mail_led_probe,
.remove = clevo_mail_led_remove, .remove = clevo_mail_led_remove,
.suspend = clevo_mail_led_suspend,
.resume = clevo_mail_led_resume,
.driver = { .driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -99,64 +99,43 @@ static void fsg_led_ring_set(struct led_classdev *led_cdev, ...@@ -99,64 +99,43 @@ static void fsg_led_ring_set(struct led_classdev *led_cdev,
} }
static struct led_classdev fsg_wlan_led = { static struct led_classdev fsg_wlan_led = {
.name = "fsg:blue:wlan", .name = "fsg:blue:wlan",
.brightness_set = fsg_led_wlan_set, .brightness_set = fsg_led_wlan_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev fsg_wan_led = { static struct led_classdev fsg_wan_led = {
.name = "fsg:blue:wan", .name = "fsg:blue:wan",
.brightness_set = fsg_led_wan_set, .brightness_set = fsg_led_wan_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev fsg_sata_led = { static struct led_classdev fsg_sata_led = {
.name = "fsg:blue:sata", .name = "fsg:blue:sata",
.brightness_set = fsg_led_sata_set, .brightness_set = fsg_led_sata_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev fsg_usb_led = { static struct led_classdev fsg_usb_led = {
.name = "fsg:blue:usb", .name = "fsg:blue:usb",
.brightness_set = fsg_led_usb_set, .brightness_set = fsg_led_usb_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev fsg_sync_led = { static struct led_classdev fsg_sync_led = {
.name = "fsg:blue:sync", .name = "fsg:blue:sync",
.brightness_set = fsg_led_sync_set, .brightness_set = fsg_led_sync_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev fsg_ring_led = { static struct led_classdev fsg_ring_led = {
.name = "fsg:blue:ring", .name = "fsg:blue:ring",
.brightness_set = fsg_led_ring_set, .brightness_set = fsg_led_ring_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
#ifdef CONFIG_PM
static int fsg_led_suspend(struct platform_device *dev, pm_message_t state)
{
led_classdev_suspend(&fsg_wlan_led);
led_classdev_suspend(&fsg_wan_led);
led_classdev_suspend(&fsg_sata_led);
led_classdev_suspend(&fsg_usb_led);
led_classdev_suspend(&fsg_sync_led);
led_classdev_suspend(&fsg_ring_led);
return 0;
}
static int fsg_led_resume(struct platform_device *dev)
{
led_classdev_resume(&fsg_wlan_led);
led_classdev_resume(&fsg_wan_led);
led_classdev_resume(&fsg_sata_led);
led_classdev_resume(&fsg_usb_led);
led_classdev_resume(&fsg_sync_led);
led_classdev_resume(&fsg_ring_led);
return 0;
}
#endif
static int fsg_led_probe(struct platform_device *pdev) static int fsg_led_probe(struct platform_device *pdev)
{ {
int ret; int ret;
...@@ -232,10 +211,6 @@ static int fsg_led_remove(struct platform_device *pdev) ...@@ -232,10 +211,6 @@ static int fsg_led_remove(struct platform_device *pdev)
static struct platform_driver fsg_led_driver = { static struct platform_driver fsg_led_driver = {
.probe = fsg_led_probe, .probe = fsg_led_probe,
.remove = fsg_led_remove, .remove = fsg_led_remove,
#ifdef CONFIG_PM
.suspend = fsg_led_suspend,
.resume = fsg_led_resume,
#endif
.driver = { .driver = {
.name = "fsg-led", .name = "fsg-led",
}, },
......
...@@ -105,6 +105,7 @@ static int gpio_led_probe(struct platform_device *pdev) ...@@ -105,6 +105,7 @@ static int gpio_led_probe(struct platform_device *pdev)
} }
led_dat->cdev.brightness_set = gpio_led_set; led_dat->cdev.brightness_set = gpio_led_set;
led_dat->cdev.brightness = LED_OFF; led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
gpio_direction_output(led_dat->gpio, led_dat->active_low); gpio_direction_output(led_dat->gpio, led_dat->active_low);
...@@ -154,44 +155,9 @@ static int __devexit gpio_led_remove(struct platform_device *pdev) ...@@ -154,44 +155,9 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state)
{
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
struct gpio_led_data *leds_data;
int i;
leds_data = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++)
led_classdev_suspend(&leds_data[i].cdev);
return 0;
}
static int gpio_led_resume(struct platform_device *pdev)
{
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
struct gpio_led_data *leds_data;
int i;
leds_data = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++)
led_classdev_resume(&leds_data[i].cdev);
return 0;
}
#else
#define gpio_led_suspend NULL
#define gpio_led_resume NULL
#endif
static struct platform_driver gpio_led_driver = { static struct platform_driver gpio_led_driver = {
.probe = gpio_led_probe, .probe = gpio_led_probe,
.remove = __devexit_p(gpio_led_remove), .remove = __devexit_p(gpio_led_remove),
.suspend = gpio_led_suspend,
.resume = gpio_led_resume,
.driver = { .driver = {
.name = "leds-gpio", .name = "leds-gpio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -68,25 +68,9 @@ static struct led_classdev hpled_led = { ...@@ -68,25 +68,9 @@ static struct led_classdev hpled_led = {
.name = "hp:red:hddprotection", .name = "hp:red:hddprotection",
.default_trigger = "heartbeat", .default_trigger = "heartbeat",
.brightness_set = hpled_set, .brightness_set = hpled_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
#ifdef CONFIG_PM
static int hpled_suspend(struct acpi_device *dev, pm_message_t state)
{
led_classdev_suspend(&hpled_led);
return 0;
}
static int hpled_resume(struct acpi_device *dev)
{
led_classdev_resume(&hpled_led);
return 0;
}
#else
#define hpled_suspend NULL
#define hpled_resume NULL
#endif
static int hpled_add(struct acpi_device *device) static int hpled_add(struct acpi_device *device)
{ {
int ret; int ret;
...@@ -121,8 +105,6 @@ static struct acpi_driver leds_hp_driver = { ...@@ -121,8 +105,6 @@ static struct acpi_driver leds_hp_driver = {
.ops = { .ops = {
.add = hpled_add, .add = hpled_add,
.remove = hpled_remove, .remove = hpled_remove,
.suspend = hpled_suspend,
.resume = hpled_resume,
} }
}; };
......
...@@ -45,30 +45,16 @@ static struct led_classdev hp6xx_red_led = { ...@@ -45,30 +45,16 @@ static struct led_classdev hp6xx_red_led = {
.name = "hp6xx:red", .name = "hp6xx:red",
.default_trigger = "hp6xx-charge", .default_trigger = "hp6xx-charge",
.brightness_set = hp6xxled_red_set, .brightness_set = hp6xxled_red_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev hp6xx_green_led = { static struct led_classdev hp6xx_green_led = {
.name = "hp6xx:green", .name = "hp6xx:green",
.default_trigger = "ide-disk", .default_trigger = "ide-disk",
.brightness_set = hp6xxled_green_set, .brightness_set = hp6xxled_green_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
#ifdef CONFIG_PM
static int hp6xxled_suspend(struct platform_device *dev, pm_message_t state)
{
led_classdev_suspend(&hp6xx_red_led);
led_classdev_suspend(&hp6xx_green_led);
return 0;
}
static int hp6xxled_resume(struct platform_device *dev)
{
led_classdev_resume(&hp6xx_red_led);
led_classdev_resume(&hp6xx_green_led);
return 0;
}
#endif
static int hp6xxled_probe(struct platform_device *pdev) static int hp6xxled_probe(struct platform_device *pdev)
{ {
int ret; int ret;
...@@ -98,10 +84,6 @@ MODULE_ALIAS("platform:hp6xx-led"); ...@@ -98,10 +84,6 @@ MODULE_ALIAS("platform:hp6xx-led");
static struct platform_driver hp6xxled_driver = { static struct platform_driver hp6xxled_driver = {
.probe = hp6xxled_probe, .probe = hp6xxled_probe,
.remove = hp6xxled_remove, .remove = hp6xxled_remove,
#ifdef CONFIG_PM
.suspend = hp6xxled_suspend,
.resume = hp6xxled_resume,
#endif
.driver = { .driver = {
.name = "hp6xx-led", .name = "hp6xx-led",
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -33,26 +33,9 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev, ...@@ -33,26 +33,9 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev,
static struct led_classdev net48xx_error_led = { static struct led_classdev net48xx_error_led = {
.name = "net48xx::error", .name = "net48xx::error",
.brightness_set = net48xx_error_led_set, .brightness_set = net48xx_error_led_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
#ifdef CONFIG_PM
static int net48xx_led_suspend(struct platform_device *dev,
pm_message_t state)
{
led_classdev_suspend(&net48xx_error_led);
return 0;
}
static int net48xx_led_resume(struct platform_device *dev)
{
led_classdev_resume(&net48xx_error_led);
return 0;
}
#else
#define net48xx_led_suspend NULL
#define net48xx_led_resume NULL
#endif
static int net48xx_led_probe(struct platform_device *pdev) static int net48xx_led_probe(struct platform_device *pdev)
{ {
return led_classdev_register(&pdev->dev, &net48xx_error_led); return led_classdev_register(&pdev->dev, &net48xx_error_led);
...@@ -67,8 +50,6 @@ static int net48xx_led_remove(struct platform_device *pdev) ...@@ -67,8 +50,6 @@ static int net48xx_led_remove(struct platform_device *pdev)
static struct platform_driver net48xx_led_driver = { static struct platform_driver net48xx_led_driver = {
.probe = net48xx_led_probe, .probe = net48xx_led_probe,
.remove = net48xx_led_remove, .remove = net48xx_led_remove,
.suspend = net48xx_led_suspend,
.resume = net48xx_led_resume,
.driver = { .driver = {
.name = DRVNAME, .name = DRVNAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/leds-pca9532.h> #include <linux/leds-pca9532.h>
static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END};
...@@ -34,6 +35,7 @@ struct pca9532_data { ...@@ -34,6 +35,7 @@ struct pca9532_data {
struct pca9532_led leds[16]; struct pca9532_led leds[16];
struct mutex update_lock; struct mutex update_lock;
struct input_dev *idev; struct input_dev *idev;
struct work_struct work;
u8 pwm[2]; u8 pwm[2];
u8 psc[2]; u8 psc[2];
}; };
...@@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = { ...@@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = {
* as a compromise we average one pwm to the values requested by all * as a compromise we average one pwm to the values requested by all
* leds that are not ON/OFF. * leds that are not ON/OFF.
* */ * */
static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
enum led_brightness value) enum led_brightness value)
{ {
int a = 0, b = 0, i = 0; int a = 0, b = 0, i = 0;
...@@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, ...@@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink,
b = b/a; b = b/a;
if (b > 0xFF) if (b > 0xFF)
return -EINVAL; return -EINVAL;
mutex_lock(&data->update_lock);
data->pwm[pwm] = b; data->pwm[pwm] = b;
data->psc[pwm] = blink;
return 0;
}
static int pca9532_setpwm(struct i2c_client *client, int pwm)
{
struct pca9532_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
data->pwm[pwm]); data->pwm[pwm]);
data->psc[pwm] = blink;
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
data->psc[pwm]); data->psc[pwm]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
...@@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev, ...@@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev,
led->state = PCA9532_ON; led->state = PCA9532_ON;
else { else {
led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
err = pca9532_setpwm(led->client, 0, 0, value); err = pca9532_calcpwm(led->client, 0, 0, value);
if (err) if (err)
return; /* XXX: led api doesn't allow error code? */ return; /* XXX: led api doesn't allow error code? */
} }
pca9532_setled(led); schedule_work(&led->work);
} }
static int pca9532_set_blink(struct led_classdev *led_cdev, static int pca9532_set_blink(struct led_classdev *led_cdev,
...@@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, ...@@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
struct pca9532_led *led = ldev_to_led(led_cdev); struct pca9532_led *led = ldev_to_led(led_cdev);
struct i2c_client *client = led->client; struct i2c_client *client = led->client;
int psc; int psc;
int err = 0;
if (*delay_on == 0 && *delay_off == 0) { if (*delay_on == 0 && *delay_off == 0) {
/* led subsystem ask us for a blink rate */ /* led subsystem ask us for a blink rate */
...@@ -148,11 +157,15 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, ...@@ -148,11 +157,15 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
/* Thecus specific: only use PSC/PWM 0 */ /* Thecus specific: only use PSC/PWM 0 */
psc = (*delay_on * 152-1)/1000; psc = (*delay_on * 152-1)/1000;
return pca9532_setpwm(client, 0, psc, led_cdev->brightness); err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
if (err)
return err;
schedule_work(&led->work);
return 0;
} }
int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, static int pca9532_event(struct input_dev *dev, unsigned int type,
int value) unsigned int code, int value)
{ {
struct pca9532_data *data = input_get_drvdata(dev); struct pca9532_data *data = input_get_drvdata(dev);
...@@ -165,13 +178,28 @@ int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, ...@@ -165,13 +178,28 @@ int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code,
else else
data->pwm[1] = 0; data->pwm[1] = 0;
dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); schedule_work(&data->work);
return 0;
}
static void pca9532_input_work(struct work_struct *work)
{
struct pca9532_data *data;
data = container_of(work, struct pca9532_data, work);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
data->pwm[1]); data->pwm[1]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
}
return 0; static void pca9532_led_work(struct work_struct *work)
{
struct pca9532_led *led;
led = container_of(work, struct pca9532_led, work);
if (led->state == PCA9532_PWM0)
pca9532_setpwm(led->client, 0);
pca9532_setled(led);
} }
static int pca9532_configure(struct i2c_client *client, static int pca9532_configure(struct i2c_client *client,
...@@ -204,8 +232,9 @@ static int pca9532_configure(struct i2c_client *client, ...@@ -204,8 +232,9 @@ static int pca9532_configure(struct i2c_client *client,
led->ldev.brightness = LED_OFF; led->ldev.brightness = LED_OFF;
led->ldev.brightness_set = pca9532_set_brightness; led->ldev.brightness_set = pca9532_set_brightness;
led->ldev.blink_set = pca9532_set_blink; led->ldev.blink_set = pca9532_set_blink;
if (led_classdev_register(&client->dev, INIT_WORK(&led->work, pca9532_led_work);
&led->ldev) < 0) { err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) {
dev_err(&client->dev, dev_err(&client->dev,
"couldn't register LED %s\n", "couldn't register LED %s\n",
led->name); led->name);
...@@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client, ...@@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client,
BIT_MASK(SND_TONE); BIT_MASK(SND_TONE);
data->idev->event = pca9532_event; data->idev->event = pca9532_event;
input_set_drvdata(data->idev, data); input_set_drvdata(data->idev, data);
INIT_WORK(&data->work, pca9532_input_work);
err = input_register_device(data->idev); err = input_register_device(data->idev);
if (err) { if (err) {
input_free_device(data->idev); input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL; data->idev = NULL;
goto exit; goto exit;
} }
...@@ -252,18 +283,19 @@ exit: ...@@ -252,18 +283,19 @@ exit:
break; break;
case PCA9532_TYPE_LED: case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev); led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break; break;
case PCA9532_TYPE_N2100_BEEP: case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) { if (data->idev != NULL) {
input_unregister_device(data->idev); input_unregister_device(data->idev);
input_free_device(data->idev); input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL; data->idev = NULL;
} }
break; break;
} }
return err; return err;
} }
static int pca9532_probe(struct i2c_client *client, static int pca9532_probe(struct i2c_client *client,
...@@ -271,12 +303,16 @@ static int pca9532_probe(struct i2c_client *client, ...@@ -271,12 +303,16 @@ static int pca9532_probe(struct i2c_client *client,
{ {
struct pca9532_data *data = i2c_get_clientdata(client); struct pca9532_data *data = i2c_get_clientdata(client);
struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
int err;
if (!pca9532_pdata)
return -EIO;
if (!i2c_check_functionality(client->adapter, if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO; return -EIO;
data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
...@@ -285,12 +321,13 @@ static int pca9532_probe(struct i2c_client *client, ...@@ -285,12 +321,13 @@ static int pca9532_probe(struct i2c_client *client,
data->client = client; data->client = client;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
if (pca9532_pdata == NULL) err = pca9532_configure(client, data, pca9532_pdata);
return -EIO; if (err) {
kfree(data);
pca9532_configure(client, data, pca9532_pdata); i2c_set_clientdata(client, NULL);
return 0; }
return err;
} }
static int pca9532_remove(struct i2c_client *client) static int pca9532_remove(struct i2c_client *client)
...@@ -303,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client) ...@@ -303,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client)
break; break;
case PCA9532_TYPE_LED: case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev); led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break; break;
case PCA9532_TYPE_N2100_BEEP: case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) { if (data->idev != NULL) {
input_unregister_device(data->idev); input_unregister_device(data->idev);
input_free_device(data->idev); input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL; data->idev = NULL;
} }
break; break;
......
...@@ -82,6 +82,7 @@ static int s3c24xx_led_probe(struct platform_device *dev) ...@@ -82,6 +82,7 @@ static int s3c24xx_led_probe(struct platform_device *dev)
led->cdev.brightness_set = s3c24xx_led_set; led->cdev.brightness_set = s3c24xx_led_set;
led->cdev.default_trigger = pdata->def_trigger; led->cdev.default_trigger = pdata->def_trigger;
led->cdev.name = pdata->name; led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->pdata = pdata; led->pdata = pdata;
...@@ -111,33 +112,9 @@ static int s3c24xx_led_probe(struct platform_device *dev) ...@@ -111,33 +112,9 @@ static int s3c24xx_led_probe(struct platform_device *dev)
return ret; return ret;
} }
#ifdef CONFIG_PM
static int s3c24xx_led_suspend(struct platform_device *dev, pm_message_t state)
{
struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
led_classdev_suspend(&led->cdev);
return 0;
}
static int s3c24xx_led_resume(struct platform_device *dev)
{
struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
led_classdev_resume(&led->cdev);
return 0;
}
#else
#define s3c24xx_led_suspend NULL
#define s3c24xx_led_resume NULL
#endif
static struct platform_driver s3c24xx_led_driver = { static struct platform_driver s3c24xx_led_driver = {
.probe = s3c24xx_led_probe, .probe = s3c24xx_led_probe,
.remove = s3c24xx_led_remove, .remove = s3c24xx_led_remove,
.suspend = s3c24xx_led_suspend,
.resume = s3c24xx_led_resume,
.driver = { .driver = {
.name = "s3c24xx_led", .name = "s3c24xx_led",
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
/*
* LED driver for WM8350 driven LEDS.
*
* Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/mfd/wm8350/pmic.h>
#include <linux/regulator/consumer.h>
/* Microamps */
static const int isink_cur[] = {
4,
5,
6,
7,
8,
10,
11,
14,
16,
19,
23,
27,
32,
39,
46,
54,
65,
77,
92,
109,
130,
154,
183,
218,
259,
308,
367,
436,
518,
616,
733,
872,
1037,
1233,
1466,
1744,
2073,
2466,
2933,
3487,
4147,
4932,
5865,
6975,
8294,
9864,
11730,
13949,
16589,
19728,
23460,
27899,
33178,
39455,
46920,
55798,
66355,
78910,
93840,
111596,
132710,
157820,
187681,
223191
};
#define to_wm8350_led(led_cdev) \
container_of(led_cdev, struct wm8350_led, cdev)
static void wm8350_led_enable(struct wm8350_led *led)
{
int ret;
if (led->enabled)
return;
ret = regulator_enable(led->isink);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
return;
}
ret = regulator_enable(led->dcdc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
regulator_disable(led->isink);
return;
}
led->enabled = 1;
}
static void wm8350_led_disable(struct wm8350_led *led)
{
int ret;
if (!led->enabled)
return;
ret = regulator_disable(led->dcdc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
return;
}
ret = regulator_disable(led->isink);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
regulator_enable(led->dcdc);
return;
}
led->enabled = 0;
}
static void led_work(struct work_struct *work)
{
struct wm8350_led *led = container_of(work, struct wm8350_led, work);
int ret;
int uA;
unsigned long flags;
mutex_lock(&led->mutex);
spin_lock_irqsave(&led->value_lock, flags);
if (led->value == LED_OFF) {
spin_unlock_irqrestore(&led->value_lock, flags);
wm8350_led_disable(led);
goto out;
}
/* This scales linearly into the index of valid current
* settings which results in a linear scaling of perceived
* brightness due to the non-linear current settings provided
* by the hardware.
*/
uA = (led->max_uA_index * led->value) / LED_FULL;
spin_unlock_irqrestore(&led->value_lock, flags);
BUG_ON(uA >= ARRAY_SIZE(isink_cur));
ret = regulator_set_current_limit(led->isink, isink_cur[uA],
isink_cur[uA]);
if (ret != 0)
dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
isink_cur[uA], ret);
wm8350_led_enable(led);
out:
mutex_unlock(&led->mutex);
}
static void wm8350_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct wm8350_led *led = to_wm8350_led(led_cdev);
unsigned long flags;
spin_lock_irqsave(&led->value_lock, flags);
led->value = value;
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags);
}
static void wm8350_led_shutdown(struct platform_device *pdev)
{
struct wm8350_led *led = platform_get_drvdata(pdev);
mutex_lock(&led->mutex);
led->value = LED_OFF;
wm8350_led_disable(led);
mutex_unlock(&led->mutex);
}
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;
if (pdata == NULL) {
dev_err(&pdev->dev, "no platform data\n");
return -ENODEV;
}
if (pdata->max_uA < isink_cur[0]) {
dev_err(&pdev->dev, "Invalid maximum current %duA\n",
pdata->max_uA);
return -EINVAL;
}
isink = regulator_get(&pdev->dev, "led_isink");
if (IS_ERR(isink)) {
printk(KERN_ERR "%s: cant get ISINK\n", __func__);
return PTR_ERR(isink);
}
dcdc = regulator_get(&pdev->dev, "led_vcc");
if (IS_ERR(dcdc)) {
printk(KERN_ERR "%s: cant get DCDC\n", __func__);
ret = PTR_ERR(dcdc);
goto err_isink;
}
led = kzalloc(sizeof(*led), GFP_KERNEL);
if (led == NULL) {
ret = -ENOMEM;
goto err_dcdc;
}
led->cdev.brightness_set = wm8350_led_set;
led->cdev.default_trigger = pdata->default_trigger;
led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->enabled = regulator_is_enabled(isink);
led->isink = isink;
led->dcdc = dcdc;
for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
if (isink_cur[i] >= pdata->max_uA)
break;
led->max_uA_index = i;
if (pdata->max_uA != isink_cur[i])
dev_warn(&pdev->dev,
"Maximum current %duA is not directly supported,"
" check platform data\n",
pdata->max_uA);
spin_lock_init(&led->value_lock);
mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
led->value = LED_OFF;
platform_set_drvdata(pdev, led);
ret = led_classdev_register(&pdev->dev, &led->cdev);
if (ret < 0)
goto err_led;
return 0;
err_led:
kfree(led);
err_dcdc:
regulator_put(dcdc);
err_isink:
regulator_put(isink);
return ret;
}
static int wm8350_led_remove(struct platform_device *pdev)
{
struct wm8350_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
flush_scheduled_work();
wm8350_led_disable(led);
regulator_put(led->dcdc);
regulator_put(led->isink);
kfree(led);
return 0;
}
static struct platform_driver wm8350_led_driver = {
.driver = {
.name = "wm8350-led",
.owner = THIS_MODULE,
},
.probe = wm8350_led_probe,
.remove = wm8350_led_remove,
.shutdown = wm8350_led_shutdown,
};
static int __devinit wm8350_led_init(void)
{
return platform_driver_register(&wm8350_led_driver);
}
module_init(wm8350_led_init);
static void wm8350_led_exit(void)
{
platform_driver_unregister(&wm8350_led_driver);
}
module_exit(wm8350_led_exit);
MODULE_AUTHOR("Mark Brown");
MODULE_DESCRIPTION("WM8350 LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm8350-led");
...@@ -56,40 +56,21 @@ static struct led_classdev wrap_power_led = { ...@@ -56,40 +56,21 @@ static struct led_classdev wrap_power_led = {
.name = "wrap::power", .name = "wrap::power",
.brightness_set = wrap_power_led_set, .brightness_set = wrap_power_led_set,
.default_trigger = "default-on", .default_trigger = "default-on",
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev wrap_error_led = { static struct led_classdev wrap_error_led = {
.name = "wrap::error", .name = "wrap::error",
.brightness_set = wrap_error_led_set, .brightness_set = wrap_error_led_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
static struct led_classdev wrap_extra_led = { static struct led_classdev wrap_extra_led = {
.name = "wrap::extra", .name = "wrap::extra",
.brightness_set = wrap_extra_led_set, .brightness_set = wrap_extra_led_set,
.flags = LED_CORE_SUSPENDRESUME,
}; };
#ifdef CONFIG_PM
static int wrap_led_suspend(struct platform_device *dev,
pm_message_t state)
{
led_classdev_suspend(&wrap_power_led);
led_classdev_suspend(&wrap_error_led);
led_classdev_suspend(&wrap_extra_led);
return 0;
}
static int wrap_led_resume(struct platform_device *dev)
{
led_classdev_resume(&wrap_power_led);
led_classdev_resume(&wrap_error_led);
led_classdev_resume(&wrap_extra_led);
return 0;
}
#else
#define wrap_led_suspend NULL
#define wrap_led_resume NULL
#endif
static int wrap_led_probe(struct platform_device *pdev) static int wrap_led_probe(struct platform_device *pdev)
{ {
int ret; int ret;
...@@ -127,8 +108,6 @@ static int wrap_led_remove(struct platform_device *pdev) ...@@ -127,8 +108,6 @@ static int wrap_led_remove(struct platform_device *pdev)
static struct platform_driver wrap_led_driver = { static struct platform_driver wrap_led_driver = {
.probe = wrap_led_probe, .probe = wrap_led_probe,
.remove = wrap_led_remove, .remove = wrap_led_remove,
.suspend = wrap_led_suspend,
.resume = wrap_led_resume,
.driver = { .driver = {
.name = DRVNAME, .name = DRVNAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -199,6 +199,7 @@ err_out: ...@@ -199,6 +199,7 @@ err_out:
static void timer_trig_deactivate(struct led_classdev *led_cdev) static void timer_trig_deactivate(struct led_classdev *led_cdev)
{ {
struct timer_trig_data *timer_data = led_cdev->trigger_data; struct timer_trig_data *timer_data = led_cdev->trigger_data;
unsigned long on = 0, off = 0;
if (timer_data) { if (timer_data) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on); device_remove_file(led_cdev->dev, &dev_attr_delay_on);
...@@ -206,6 +207,10 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev) ...@@ -206,6 +207,10 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev)
del_timer_sync(&timer_data->timer); del_timer_sync(&timer_data->timer);
kfree(timer_data); kfree(timer_data);
} }
/* If there is hardware support for blinking, stop it */
if (led_cdev->blink_set)
led_cdev->blink_set(led_cdev, &on, &off);
} }
static struct led_trigger timer_led_trigger = { static struct led_trigger timer_led_trigger = {
......
...@@ -1453,6 +1453,9 @@ void wm8350_device_exit(struct wm8350 *wm8350) ...@@ -1453,6 +1453,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++)
platform_device_unregister(wm8350->pmic.led[i].pdev);
for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
platform_device_unregister(wm8350->pmic.pdev[i]); platform_device_unregister(wm8350->pmic.pdev[i]);
......
...@@ -1412,6 +1412,97 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg, ...@@ -1412,6 +1412,97 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
} }
EXPORT_SYMBOL_GPL(wm8350_register_regulator); EXPORT_SYMBOL_GPL(wm8350_register_regulator);
/**
* wm8350_register_led - Register a WM8350 LED output
*
* @param wm8350 The WM8350 device to configure.
* @param lednum LED device index to create.
* @param dcdc The DCDC to use for the LED.
* @param isink The ISINK to use for the LED.
* @param pdata Configuration for the LED.
*
* The WM8350 supports the use of an ISINK together with a DCDC to
* provide a power-efficient LED driver. This function registers the
* regulators and instantiates the platform device for a LED. The
* operating modes for the LED regulators must be configured using
* wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and
* wm8350_dcdc_set_slot() prior to calling this function.
*/
int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
struct wm8350_led_platform_data *pdata)
{
struct wm8350_led *led;
struct platform_device *pdev;
int ret;
if (lednum > ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) {
dev_err(wm8350->dev, "Invalid LED index %d\n", lednum);
return -ENODEV;
}
led = &wm8350->pmic.led[lednum];
if (led->pdev) {
dev_err(wm8350->dev, "LED %d already allocated\n", lednum);
return -EINVAL;
}
pdev = platform_device_alloc("wm8350-led", lednum);
if (pdev == NULL) {
dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum);
return -ENOMEM;
}
led->isink_consumer.dev = &pdev->dev;
led->isink_consumer.supply = "led_isink";
led->isink_init.num_consumer_supplies = 1;
led->isink_init.consumer_supplies = &led->isink_consumer;
led->isink_init.constraints.min_uA = 0;
led->isink_init.constraints.max_uA = pdata->max_uA;
led->isink_init.constraints.valid_ops_mask = REGULATOR_CHANGE_CURRENT;
led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
ret = wm8350_register_regulator(wm8350, isink, &led->isink_init);
if (ret != 0) {
platform_device_put(pdev);
return ret;
}
led->dcdc_consumer.dev = &pdev->dev;
led->dcdc_consumer.supply = "led_vcc";
led->dcdc_init.num_consumer_supplies = 1;
led->dcdc_init.consumer_supplies = &led->dcdc_consumer;
led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init);
if (ret != 0) {
platform_device_put(pdev);
return ret;
}
switch (isink) {
case WM8350_ISINK_A:
wm8350->pmic.isink_A_dcdc = dcdc;
break;
case WM8350_ISINK_B:
wm8350->pmic.isink_B_dcdc = dcdc;
break;
}
pdev->dev.platform_data = pdata;
pdev->dev.parent = wm8350->dev;
ret = platform_device_add(pdev);
if (ret != 0) {
dev_err(wm8350->dev, "Failed to register LED %d: %d\n",
lednum, ret);
platform_device_put(pdev);
return ret;
}
led->pdev = pdev;
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_register_led);
static struct platform_driver wm8350_regulator_driver = { static struct platform_driver wm8350_regulator_driver = {
.probe = wm8350_regulator_probe, .probe = wm8350_regulator_probe,
.remove = wm8350_regulator_remove, .remove = wm8350_regulator_remove,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define __LINUX_PCA9532_H #define __LINUX_PCA9532_H
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/workqueue.h>
enum pca9532_state { enum pca9532_state {
PCA9532_OFF = 0x0, PCA9532_OFF = 0x0,
...@@ -31,6 +32,7 @@ struct pca9532_led { ...@@ -31,6 +32,7 @@ struct pca9532_led {
struct i2c_client *client; struct i2c_client *client;
char *name; char *name;
struct led_classdev ldev; struct led_classdev ldev;
struct work_struct work;
enum pca9532_type type; enum pca9532_type type;
enum pca9532_state state; enum pca9532_state state;
}; };
......
...@@ -32,7 +32,10 @@ struct led_classdev { ...@@ -32,7 +32,10 @@ struct led_classdev {
int brightness; int brightness;
int flags; int flags;
/* Lower 16 bits reflect status */
#define LED_SUSPENDED (1 << 0) #define LED_SUSPENDED (1 << 0)
/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME (1 << 16)
/* Set LED brightness level */ /* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */ /* Must not sleep, use a workqueue if needed */
...@@ -62,7 +65,7 @@ struct led_classdev { ...@@ -62,7 +65,7 @@ struct led_classdev {
extern int led_classdev_register(struct device *parent, extern int led_classdev_register(struct device *parent,
struct led_classdev *led_cdev); struct led_classdev *led_cdev);
extern void led_classdev_unregister(struct led_classdev *lcd); extern void led_classdev_unregister(struct led_classdev *led_cdev);
extern void led_classdev_suspend(struct led_classdev *led_cdev); extern void led_classdev_suspend(struct led_classdev *led_cdev);
extern void led_classdev_resume(struct led_classdev *led_cdev); extern void led_classdev_resume(struct led_classdev *led_cdev);
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
#ifndef __LINUX_MFD_WM8350_PMIC_H #ifndef __LINUX_MFD_WM8350_PMIC_H
#define __LINUX_MFD_WM8350_PMIC_H #define __LINUX_MFD_WM8350_PMIC_H
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/regulator/machine.h>
/* /*
* Register values. * Register values.
*/ */
...@@ -700,6 +704,33 @@ struct wm8350; ...@@ -700,6 +704,33 @@ struct wm8350;
struct platform_device; struct platform_device;
struct regulator_init_data; struct regulator_init_data;
/*
* WM8350 LED platform data
*/
struct wm8350_led_platform_data {
const char *name;
const char *default_trigger;
int max_uA;
};
struct wm8350_led {
struct platform_device *pdev;
struct mutex mutex;
struct work_struct work;
spinlock_t value_lock;
enum led_brightness value;
struct led_classdev cdev;
int max_uA_index;
int enabled;
struct regulator *isink;
struct regulator_consumer_supply isink_consumer;
struct regulator_init_data isink_init;
struct regulator *dcdc;
struct regulator_consumer_supply dcdc_consumer;
struct regulator_init_data dcdc_init;
};
struct wm8350_pmic { struct wm8350_pmic {
/* Number of regulators of each type on this device */ /* Number of regulators of each type on this device */
int max_dcdc; int max_dcdc;
...@@ -717,10 +748,15 @@ struct wm8350_pmic { ...@@ -717,10 +748,15 @@ struct wm8350_pmic {
/* regulator devices */ /* regulator devices */
struct platform_device *pdev[NUM_WM8350_REGULATORS]; struct platform_device *pdev[NUM_WM8350_REGULATORS];
/* LED devices */
struct wm8350_led led[2];
}; };
int wm8350_register_regulator(struct wm8350 *wm8350, int reg, int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
struct regulator_init_data *initdata); struct regulator_init_data *initdata);
int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
struct wm8350_led_platform_data *pdata);
/* /*
* Additional DCDC control not supported via regulator API * Additional DCDC control not supported via regulator API
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment