int fade_time;
int brightness;
int desired_brightness;
+ int running;
+ struct mutex lock;
struct work_struct work;
struct led_classdev cdev;
};
struct i2c_client *client;
struct work_struct work;
struct input_dev *idev;
- int irq;
unsigned kp_enabled : 1;
unsigned pm_suspend : 1;
unsigned keys_down;
return 0;
}
+static void pwm_done(struct lm8323_pwm *pwm)
+{
+ mutex_lock(&pwm->lock);
+ pwm->running = 0;
+ if (pwm->desired_brightness != pwm->brightness)
+ schedule_work(&pwm->work);
+ mutex_unlock(&pwm->lock);
+}
+
/*
* Bottom half: handle the interrupt by posting key events, or dealing with
* errors appropriately.
"reinitialising\n");
lm8323_configure(lm);
}
- if (ints & INT_PWM1)
+ if (ints & INT_PWM1) {
debug(&lm->client->dev, "pwm1 engine completed\n");
- if (ints & INT_PWM2)
+ pwm_done(&lm->pwm1);
+ }
+ if (ints & INT_PWM2) {
debug(&lm->client->dev, "pwm2 engine completed\n");
- if (ints & INT_PWM3)
+ pwm_done(&lm->pwm2);
+ }
+ if (ints & INT_PWM3) {
debug(&lm->client->dev, "pwm3 engine completed\n");
+ pwm_done(&lm->pwm3);
+ }
}
mutex_unlock(&lm->lock);
/*
* Write a script into a given PWM engine, concluding with PWM_END.
- * If 'keepalive' is specified, the engine will be kept running
- * indefinitely.
+ * If 'kill' is nonzero, the engine will be shut down at the end
+ * of the script, producing a zero output. Otherwise the engine
+ * will be kept running at the final PWM level indefinitely.
*/
-static void lm8323_write_pwm(struct lm8323_pwm *pwm, int keepalive,
- int len, ...)
+static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill,
+ int len, const u16 *cmds)
{
struct lm8323_chip *lm = pwm_to_lm8323(pwm);
- int i, cmd;
- va_list ap;
-
- /*
- * If there are any scripts running at the moment, terminate them
- * and make sure the duty cycle is as if it finished.
- */
- lm8323_write(lm, 2, LM8323_CMD_STOP_PWM, pwm->id);
-
- va_start(ap, len);
- for (i = 0; i < len; i++) {
- cmd = va_arg(ap, int);
- lm8323_write_pwm_one(pwm, i, cmd);
- }
- va_end(ap);
+ int i;
- /* Wait for a trigger from any channel. This keeps the engine alive. */
- if (keepalive)
- lm8323_write_pwm_one(pwm, i++, PWM_WAIT_TRIG(0xe));
- else
- lm8323_write_pwm_one(pwm, i++, PWM_END(1));
+ for (i = 0; i < len; i++)
+ lm8323_write_pwm_one(pwm, i, cmds[i]);
+ lm8323_write_pwm_one(pwm, i++, PWM_END(kill));
lm8323_write(lm, 2, LM8323_CMD_START_PWM, pwm->id);
+ pwm->running = 1;
}
static void lm8323_pwm_work(struct work_struct *work)
{
struct lm8323_pwm *pwm = work_to_pwm(work);
- int div, perstep, steps, hz, direction, keepalive;
+ int div512, perstep, steps, hz, up, kill;
+ u16 pwm_cmds[3];
+ int num_cmds = 0;
- /* Do nothing if we're already at the requested level. */
- if (pwm->desired_brightness == pwm->brightness)
+ mutex_lock(&pwm->lock);
+
+ /*
+ * Do nothing if we're already at the requested level,
+ * or previous setting is not yet complete. In the latter
+ * case we will be called again when the previous PWM script
+ * finishes.
+ */
+ if (pwm->running || pwm->desired_brightness == pwm->brightness) {
+ mutex_unlock(&pwm->lock);
return;
+ }
- keepalive = (pwm->desired_brightness > 0);
- direction = (pwm->desired_brightness > pwm->brightness);
+ kill = (pwm->desired_brightness == 0);
+ up = (pwm->desired_brightness > pwm->brightness);
steps = abs(pwm->desired_brightness - pwm->brightness);
/*
* Convert time (in ms) into a divisor (512 or 16 on a refclk of
* 32768Hz), and number of ticks per step.
*/
- if ((pwm->fade_time / steps) > (32768 / 512))
- div = 512;
- else
- div = 16;
+ if ((pwm->fade_time / steps) > (32768 / 512)) {
+ div512 = 1;
+ hz = 32768 / 512;
+ }
+ else {
+ div512 = 0;
+ hz = 32768 / 16;
+ }
- hz = 32768 / div;
- if (pwm->fade_time < ((steps * 1000) / hz))
- perstep = 1;
- else
- perstep = (hz * pwm->fade_time) / (steps * 1000);
+ perstep = (hz * pwm->fade_time) / (steps * 1000);
if (perstep == 0)
perstep = 1;
else if (perstep > 63)
perstep = 63;
- if (steps > 252) {
- lm8323_write_pwm(pwm, keepalive, 3,
- PWM_RAMP((div == 512), perstep, 126,
- direction),
- PWM_RAMP((div == 512), perstep, 126,
- direction),
- PWM_RAMP((div == 512), perstep, steps - 252,
- direction));
- } else if (steps > 126) {
- lm8323_write_pwm(pwm, keepalive, 2,
- PWM_RAMP((div == 512), perstep, 126,
- direction),
- PWM_RAMP((div == 512), perstep, steps - 126,
- direction));
- } else {
- lm8323_write_pwm(pwm, keepalive, 1,
- PWM_RAMP((div == 512), perstep, steps,
- direction));
+ while (steps) {
+ int s;
+
+ s = min(126, steps);
+ pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up);
+ steps -= s;
}
+ lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds);
+
pwm->brightness = pwm->desired_brightness;
+ mutex_unlock(&pwm->lock);
}
static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev,
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
struct lm8323_chip *lm = pwm_to_lm8323(pwm);
+ mutex_lock(&pwm->lock);
pwm->desired_brightness = brightness;
+ mutex_unlock(&pwm->lock);
if (in_interrupt()) {
schedule_work(&pwm->work);
int ret;
int time;
- ret = strict_strtoul(buf, 10, &time);
+ ret = sscanf(buf, "%d", &time);
/* Numbers only, please. */
if (ret)
return -EINVAL;
pwm->fade_time = 0;
pwm->brightness = 0;
pwm->desired_brightness = 0;
+ pwm->running = 0;
+ mutex_init(&pwm->lock);
if (name) {
pwm->cdev.name = name;
pwm->cdev.brightness_set = lm8323_pwm_set_brightness;
int ret;
int i;
- i = strict_strtoul(buf, 10, &ret);
+ i = sscanf(buf, "%d", &ret);
mutex_lock(&lm->lock);
lm->kp_enabled = !i;
}
static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable);
-static int lm8323_probe(struct i2c_client *client)
+static int lm8323_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct input_dev *idev;
struct lm8323_chip *lm;
if (init_pwm(lm, 3, &client->dev, lm8323_pdata->pwm3_name) < 0)
goto fail5;
- lm->irq = lm8323_pdata->irq_gpio;
- debug(&c->dev, "IRQ: %d\n", lm->irq);
-
mutex_init(&lm->lock);
INIT_WORK(&lm->work, lm8323_work);
IRQF_TRIGGER_FALLING | IRQF_DISABLED |
IRQF_SAMPLE_RANDOM, DRIVER_NAME, lm);
if (err) {
- dev_err(&client->dev, "could not get IRQ %d\n", lm->irq);
+ dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
goto fail6;
}
- set_irq_wake(lm->irq, 1);
+ set_irq_wake(client->irq, 1);
lm->kp_enabled = 1;
err = device_create_file(&client->dev, &dev_attr_disable_kp);
set_bit(EV_REP, idev->evbit);
lm->idev = idev;
- if (input_register_device(idev)) {
+ err = input_register_device(idev);
+ if (err) {
dev_dbg(&client->dev, "error registering input device\n");
goto fail8;
}
fail8:
device_remove_file(&client->dev, &dev_attr_disable_kp);
fail7:
- free_irq(lm->irq, lm);
+ free_irq(client->irq, lm);
fail6:
if (lm->pwm3.enabled)
led_classdev_unregister(&lm->pwm3.cdev);
{
struct lm8323_chip *lm = i2c_get_clientdata(client);
- free_irq(lm->irq, lm);
+ free_irq(client->irq, lm);
+ cancel_work_sync(&lm->work);
+ input_unregister_device(lm->idev);
device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
+ if (lm->pwm3.enabled)
+ led_classdev_unregister(&lm->pwm3.cdev);
+ if (lm->pwm2.enabled)
+ led_classdev_unregister(&lm->pwm2.cdev);
+ if (lm->pwm1.enabled)
+ led_classdev_unregister(&lm->pwm1.cdev);
+ kfree(lm);
return 0;
}
{
struct lm8323_chip *lm = i2c_get_clientdata(client);
- set_irq_wake(lm->irq, 0);
- disable_irq(lm->irq);
+ set_irq_wake(client->irq, 0);
+ disable_irq(client->irq);
mutex_lock(&lm->lock);
lm->pm_suspend = 1;
if (lm->pwm3.enabled)
led_classdev_resume(&lm->pwm3.cdev);
- enable_irq(lm->irq);
- set_irq_wake(lm->irq, 1);
+ enable_irq(client->irq);
+ set_irq_wake(client->irq, 1);
return 0;
}
+static const struct i2c_device_id lm8323_id[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+
static struct i2c_driver lm8323_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
},
.probe = lm8323_probe,
- .remove = __exit_p(lm8323_remove),
+ .remove = __devexit_p(lm8323_remove),
.suspend = lm8323_suspend,
.resume = lm8323_resume,
+ .id_table = lm8323_id,
};
+MODULE_DEVICE_TABLE(i2c, lm8323_id);
static int __init lm8323_init(void)
{
i2c_del_driver(&lm8323_i2c_driver);
}
-MODULE_AUTHOR("Daniel Stone");
+MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>, Daniel Stone");
MODULE_DESCRIPTION("LM8323 keypad driver");
MODULE_LICENSE("GPL");