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
This commit is contained in:
commit
d7d717fa88
@ -63,6 +63,12 @@ config LEDS_WRAP
|
||||
help
|
||||
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
|
||||
tristate "LED Support for iPAQ H1940 device"
|
||||
depends on LEDS_CLASS && ARCH_H1940
|
||||
@ -77,7 +83,7 @@ config LEDS_COBALT_QUBE
|
||||
|
||||
config LEDS_COBALT_RAQ
|
||||
bool "LED Support for the Cobalt Raq series"
|
||||
depends on LEDS_CLASS && MIPS_COBALT
|
||||
depends on LEDS_CLASS=y && MIPS_COBALT
|
||||
select LEDS_TRIGGERS
|
||||
help
|
||||
This option enables support for the Cobalt Raq series LEDs.
|
||||
@ -158,6 +164,13 @@ config LEDS_PCA955X
|
||||
LED driver chips accessed via the I2C bus. Supported
|
||||
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
|
||||
tristate "LED Support for DA9030/DA9034 PMIC"
|
||||
depends on LEDS_CLASS && PMIC_DA903X
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
|
||||
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
|
||||
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
|
||||
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
|
||||
obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o
|
||||
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
|
||||
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
|
||||
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
|
||||
@ -23,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
|
||||
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
|
||||
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
|
||||
obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o
|
||||
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
|
||||
|
||||
# LED Triggers
|
||||
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
|
||||
|
@ -91,9 +91,29 @@ void led_classdev_resume(struct led_classdev *led_cdev)
|
||||
}
|
||||
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.
|
||||
* @dev: The device to register.
|
||||
* @parent: The device to register.
|
||||
* @led_cdev: the led_classdev structure for this device.
|
||||
*/
|
||||
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
|
||||
@ -174,6 +194,8 @@ static int __init leds_init(void)
|
||||
leds_class = class_create(THIS_MODULE, "leds");
|
||||
if (IS_ERR(leds_class))
|
||||
return PTR_ERR(leds_class);
|
||||
leds_class->suspend = led_suspend;
|
||||
leds_class->resume = led_resume;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
181
drivers/leds/leds-alix2.c
Normal file
181
drivers/leds/leds-alix2.c
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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[] = {
|
||||
},
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
|
||||
ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
ret = led_classdev_register(&pdev->dev,
|
||||
&ams_delta_leds[i].cdev);
|
||||
if (ret < 0)
|
||||
@ -127,7 +102,7 @@ static int ams_delta_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
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);
|
||||
|
||||
return 0;
|
||||
@ -136,8 +111,6 @@ static int ams_delta_led_remove(struct platform_device *pdev)
|
||||
static struct platform_driver ams_delta_led_driver = {
|
||||
.probe = ams_delta_led_probe,
|
||||
.remove = ams_delta_led_remove,
|
||||
.suspend = ams_delta_led_suspend,
|
||||
.resume = ams_delta_led_resume,
|
||||
.driver = {
|
||||
.name = "ams-delta-led",
|
||||
.owner = THIS_MODULE,
|
||||
@ -151,7 +124,7 @@ static int __init ams_delta_led_init(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);
|
||||
|
@ -142,6 +142,7 @@ static struct led_classdev clevo_mail_led = {
|
||||
.name = "clevo::mail",
|
||||
.brightness_set = clevo_mail_led_set,
|
||||
.blink_set = clevo_mail_led_blink,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
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)
|
||||
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 = {
|
||||
.probe = clevo_mail_led_probe,
|
||||
.remove = clevo_mail_led_remove,
|
||||
.suspend = clevo_mail_led_suspend,
|
||||
.resume = clevo_mail_led_resume,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -99,64 +99,43 @@ static void fsg_led_ring_set(struct led_classdev *led_cdev,
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct led_classdev fsg_wlan_led = {
|
||||
.name = "fsg:blue:wlan",
|
||||
.brightness_set = fsg_led_wlan_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev fsg_wan_led = {
|
||||
.name = "fsg:blue:wan",
|
||||
.brightness_set = fsg_led_wan_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev fsg_sata_led = {
|
||||
.name = "fsg:blue:sata",
|
||||
.brightness_set = fsg_led_sata_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev fsg_usb_led = {
|
||||
.name = "fsg:blue:usb",
|
||||
.brightness_set = fsg_led_usb_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev fsg_sync_led = {
|
||||
.name = "fsg:blue:sync",
|
||||
.brightness_set = fsg_led_sync_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev fsg_ring_led = {
|
||||
.name = "fsg:blue:ring",
|
||||
.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)
|
||||
{
|
||||
int ret;
|
||||
@ -232,10 +211,6 @@ static int fsg_led_remove(struct platform_device *pdev)
|
||||
static struct platform_driver fsg_led_driver = {
|
||||
.probe = fsg_led_probe,
|
||||
.remove = fsg_led_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = fsg_led_suspend,
|
||||
.resume = fsg_led_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = "fsg-led",
|
||||
},
|
||||
|
@ -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 = LED_OFF;
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
gpio_direction_output(led_dat->gpio, led_dat->active_low);
|
||||
|
||||
@ -154,44 +155,9 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
|
||||
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 = {
|
||||
.probe = gpio_led_probe,
|
||||
.remove = __devexit_p(gpio_led_remove),
|
||||
.suspend = gpio_led_suspend,
|
||||
.resume = gpio_led_resume,
|
||||
.driver = {
|
||||
.name = "leds-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -68,25 +68,9 @@ static struct led_classdev hpled_led = {
|
||||
.name = "hp:red:hddprotection",
|
||||
.default_trigger = "heartbeat",
|
||||
.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)
|
||||
{
|
||||
int ret;
|
||||
@ -121,8 +105,6 @@ static struct acpi_driver leds_hp_driver = {
|
||||
.ops = {
|
||||
.add = hpled_add,
|
||||
.remove = hpled_remove,
|
||||
.suspend = hpled_suspend,
|
||||
.resume = hpled_resume,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -45,30 +45,16 @@ static struct led_classdev hp6xx_red_led = {
|
||||
.name = "hp6xx:red",
|
||||
.default_trigger = "hp6xx-charge",
|
||||
.brightness_set = hp6xxled_red_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev hp6xx_green_led = {
|
||||
.name = "hp6xx:green",
|
||||
.default_trigger = "ide-disk",
|
||||
.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)
|
||||
{
|
||||
int ret;
|
||||
@ -98,10 +84,6 @@ MODULE_ALIAS("platform:hp6xx-led");
|
||||
static struct platform_driver hp6xxled_driver = {
|
||||
.probe = hp6xxled_probe,
|
||||
.remove = hp6xxled_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = hp6xxled_suspend,
|
||||
.resume = hp6xxled_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = "hp6xx-led",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -33,26 +33,9 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev,
|
||||
static struct led_classdev net48xx_error_led = {
|
||||
.name = "net48xx::error",
|
||||
.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)
|
||||
{
|
||||
return led_classdev_register(&pdev->dev, &net48xx_error_led);
|
||||
@ -67,8 +50,6 @@ static int net48xx_led_remove(struct platform_device *pdev)
|
||||
static struct platform_driver net48xx_led_driver = {
|
||||
.probe = net48xx_led_probe,
|
||||
.remove = net48xx_led_remove,
|
||||
.suspend = net48xx_led_suspend,
|
||||
.resume = net48xx_led_resume,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/leds-pca9532.h>
|
||||
|
||||
static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END};
|
||||
@ -34,6 +35,7 @@ struct pca9532_data {
|
||||
struct pca9532_led leds[16];
|
||||
struct mutex update_lock;
|
||||
struct input_dev *idev;
|
||||
struct work_struct work;
|
||||
u8 pwm[2];
|
||||
u8 psc[2];
|
||||
};
|
||||
@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = {
|
||||
* as a compromise we average one pwm to the values requested by all
|
||||
* 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)
|
||||
{
|
||||
int a = 0, b = 0, i = 0;
|
||||
@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink,
|
||||
b = b/a;
|
||||
if (b > 0xFF)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->update_lock);
|
||||
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),
|
||||
data->pwm[pwm]);
|
||||
data->psc[pwm] = blink;
|
||||
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
|
||||
data->psc[pwm]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev,
|
||||
led->state = PCA9532_ON;
|
||||
else {
|
||||
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)
|
||||
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,
|
||||
@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
|
||||
struct pca9532_led *led = ldev_to_led(led_cdev);
|
||||
struct i2c_client *client = led->client;
|
||||
int psc;
|
||||
int err = 0;
|
||||
|
||||
if (*delay_on == 0 && *delay_off == 0) {
|
||||
/* led subsystem ask us for a blink rate */
|
||||
@ -148,11 +157,15 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
|
||||
|
||||
/* Thecus specific: only use PSC/PWM 0 */
|
||||
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,
|
||||
int value)
|
||||
static int pca9532_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
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,
|
||||
else
|
||||
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);
|
||||
i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
|
||||
data->pwm[1]);
|
||||
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,
|
||||
@ -204,8 +232,9 @@ static int pca9532_configure(struct i2c_client *client,
|
||||
led->ldev.brightness = LED_OFF;
|
||||
led->ldev.brightness_set = pca9532_set_brightness;
|
||||
led->ldev.blink_set = pca9532_set_blink;
|
||||
if (led_classdev_register(&client->dev,
|
||||
&led->ldev) < 0) {
|
||||
INIT_WORK(&led->work, pca9532_led_work);
|
||||
err = led_classdev_register(&client->dev, &led->ldev);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev,
|
||||
"couldn't register LED %s\n",
|
||||
led->name);
|
||||
@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client,
|
||||
BIT_MASK(SND_TONE);
|
||||
data->idev->event = pca9532_event;
|
||||
input_set_drvdata(data->idev, data);
|
||||
INIT_WORK(&data->work, pca9532_input_work);
|
||||
err = input_register_device(data->idev);
|
||||
if (err) {
|
||||
input_free_device(data->idev);
|
||||
cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
goto exit;
|
||||
}
|
||||
@ -252,18 +283,19 @@ exit:
|
||||
break;
|
||||
case PCA9532_TYPE_LED:
|
||||
led_classdev_unregister(&data->leds[i].ldev);
|
||||
cancel_work_sync(&data->leds[i].work);
|
||||
break;
|
||||
case PCA9532_TYPE_N2100_BEEP:
|
||||
if (data->idev != NULL) {
|
||||
input_unregister_device(data->idev);
|
||||
input_free_device(data->idev);
|
||||
cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
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_platform_data *pca9532_pdata = client->dev.platform_data;
|
||||
int err;
|
||||
|
||||
if (!pca9532_pdata)
|
||||
return -EIO;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EIO;
|
||||
|
||||
data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL);
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -285,12 +321,13 @@ static int pca9532_probe(struct i2c_client *client,
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
if (pca9532_pdata == NULL)
|
||||
return -EIO;
|
||||
|
||||
pca9532_configure(client, data, pca9532_pdata);
|
||||
return 0;
|
||||
err = pca9532_configure(client, data, pca9532_pdata);
|
||||
if (err) {
|
||||
kfree(data);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pca9532_remove(struct i2c_client *client)
|
||||
@ -303,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client)
|
||||
break;
|
||||
case PCA9532_TYPE_LED:
|
||||
led_classdev_unregister(&data->leds[i].ldev);
|
||||
cancel_work_sync(&data->leds[i].work);
|
||||
break;
|
||||
case PCA9532_TYPE_N2100_BEEP:
|
||||
if (data->idev != NULL) {
|
||||
input_unregister_device(data->idev);
|
||||
input_free_device(data->idev);
|
||||
cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
}
|
||||
break;
|
||||
|
@ -82,6 +82,7 @@ static int s3c24xx_led_probe(struct platform_device *dev)
|
||||
led->cdev.brightness_set = s3c24xx_led_set;
|
||||
led->cdev.default_trigger = pdata->def_trigger;
|
||||
led->cdev.name = pdata->name;
|
||||
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
led->pdata = pdata;
|
||||
|
||||
@ -111,33 +112,9 @@ static int s3c24xx_led_probe(struct platform_device *dev)
|
||||
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 = {
|
||||
.probe = s3c24xx_led_probe,
|
||||
.remove = s3c24xx_led_remove,
|
||||
.suspend = s3c24xx_led_suspend,
|
||||
.resume = s3c24xx_led_resume,
|
||||
.driver = {
|
||||
.name = "s3c24xx_led",
|
||||
.owner = THIS_MODULE,
|
||||
|
311
drivers/leds/leds-wm8350.c
Normal file
311
drivers/leds/leds-wm8350.c
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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 = {
|
||||
.name = "wrap::power",
|
||||
.brightness_set = wrap_power_led_set,
|
||||
.default_trigger = "default-on",
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev wrap_error_led = {
|
||||
.name = "wrap::error",
|
||||
.brightness_set = wrap_error_led_set,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static struct led_classdev wrap_extra_led = {
|
||||
.name = "wrap::extra",
|
||||
.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)
|
||||
{
|
||||
int ret;
|
||||
@ -127,8 +108,6 @@ static int wrap_led_remove(struct platform_device *pdev)
|
||||
static struct platform_driver wrap_led_driver = {
|
||||
.probe = wrap_led_probe,
|
||||
.remove = wrap_led_remove,
|
||||
.suspend = wrap_led_suspend,
|
||||
.resume = wrap_led_resume,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -199,6 +199,7 @@ err_out:
|
||||
static void timer_trig_deactivate(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct timer_trig_data *timer_data = led_cdev->trigger_data;
|
||||
unsigned long on = 0, off = 0;
|
||||
|
||||
if (timer_data) {
|
||||
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
|
||||
@ -206,6 +207,10 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev)
|
||||
del_timer_sync(&timer_data->timer);
|
||||
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 = {
|
||||
|
@ -1453,6 +1453,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
|
||||
{
|
||||
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++)
|
||||
platform_device_unregister(wm8350->pmic.pdev[i]);
|
||||
|
||||
|
@ -1412,6 +1412,97 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
|
||||
}
|
||||
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 = {
|
||||
.probe = wm8350_regulator_probe,
|
||||
.remove = wm8350_regulator_remove,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define __LINUX_PCA9532_H
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
enum pca9532_state {
|
||||
PCA9532_OFF = 0x0,
|
||||
@ -31,6 +32,7 @@ struct pca9532_led {
|
||||
struct i2c_client *client;
|
||||
char *name;
|
||||
struct led_classdev ldev;
|
||||
struct work_struct work;
|
||||
enum pca9532_type type;
|
||||
enum pca9532_state state;
|
||||
};
|
||||
|
@ -32,7 +32,10 @@ struct led_classdev {
|
||||
int brightness;
|
||||
int flags;
|
||||
|
||||
/* Lower 16 bits reflect status */
|
||||
#define LED_SUSPENDED (1 << 0)
|
||||
/* Upper 16 bits reflect control information */
|
||||
#define LED_CORE_SUSPENDRESUME (1 << 16)
|
||||
|
||||
/* Set LED brightness level */
|
||||
/* Must not sleep, use a workqueue if needed */
|
||||
@ -62,7 +65,7 @@ struct led_classdev {
|
||||
|
||||
extern int led_classdev_register(struct device *parent,
|
||||
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_resume(struct led_classdev *led_cdev);
|
||||
|
||||
|
@ -13,6 +13,10 @@
|
||||
#ifndef __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.
|
||||
*/
|
||||
@ -700,6 +704,33 @@ struct wm8350;
|
||||
struct platform_device;
|
||||
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 {
|
||||
/* Number of regulators of each type on this device */
|
||||
int max_dcdc;
|
||||
@ -717,10 +748,15 @@ struct wm8350_pmic {
|
||||
|
||||
/* regulator devices */
|
||||
struct platform_device *pdev[NUM_WM8350_REGULATORS];
|
||||
|
||||
/* LED devices */
|
||||
struct wm8350_led led[2];
|
||||
};
|
||||
|
||||
int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user