diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-09 13:55:37 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-09 13:55:37 -0800 |
commit | d7d717fa889fc7a60c38497846c7618940a999d9 (patch) | |
tree | 8a19ffa13d305588a4433defb9f128ed68ecf47c /drivers/leds/leds-pca9532.c | |
parent | b64dc5a4842c3420d7a093bf5e8979c57ceb789c (diff) | |
parent | ce70a24575414d615f6a7a05caf9b7e8f91a1b6f (diff) |
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
Diffstat (limited to 'drivers/leds/leds-pca9532.c')
-rw-r--r-- | drivers/leds/leds-pca9532.c | 77 |
1 files changed, 58 insertions, 19 deletions
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 4064d4f6b33..76ec7498e2d 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -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; |