@@ -45,6 +45,7 @@ struct vibra_info {
struct workqueue_struct *workqueue;
struct work_struct play_work;
spinlock_t lock; /* for workqueue */
+ int users;
bool enabled;
int speed;
@@ -153,6 +154,49 @@ out:
return 0;
}
+static int twl4030_vibra_open(struct input_dev *input)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+
+ if (info->workqueue == NULL) {
+ info->workqueue = create_singlethread_workqueue("vibra");
+ if (info->workqueue == NULL) {
+ dev_err(&input->dev, "couldn't create workqueue\n");
+ return -ENOMEM;
+ }
+ }
+
+ info->users++;
+ return 0;
+}
+
+static void twl4030_vibra_wq_destroy(struct vibra_info *info)
+{
+ struct workqueue_struct *wq;
+
+ spin_lock(&info->lock);
+ wq = info->workqueue;
+ info->workqueue = NULL;
+ spin_unlock(&info->lock);
+
+ if (wq) {
+ cancel_work_sync(&info->play_work);
+ INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
+ destroy_workqueue(wq);
+ }
+
+ if (info->enabled)
+ vibra_disable(info);
+}
+
+static void twl4030_vibra_close(struct input_dev *input)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+
+ if (!(--info->users))
+ twl4030_vibra_wq_destroy(info);
+}
+
/*** Module ***/
#if CONFIG_PM
static int twl4030_vibra_suspend(struct device *dev)
@@ -197,12 +241,8 @@ static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
- info->workqueue = create_singlethread_workqueue("vibra");
- if (info->workqueue == NULL) {
- dev_err(&pdev->dev, "couldn't create workqueue\n");
- ret = -ENOMEM;
- goto err_kzalloc;
- }
+ info->workqueue = NULL;
+ info->users = 0;
INIT_WORK(&info->play_work, vibra_play_work);
spin_lock_init(&info->lock);
@@ -210,13 +250,15 @@ static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
if (info->input_dev == NULL) {
dev_err(&pdev->dev, "couldn't allocate input device\n");
ret = -ENOMEM;
- goto err_workq;
+ goto err_kzalloc;
}
input_set_drvdata(info->input_dev, info);
info->input_dev->name = "twl4030:vibrator";
info->input_dev->id.version = 1;
info->input_dev->dev.parent = pdev->dev.parent;
+ info->input_dev->open = twl4030_vibra_open;
+ info->input_dev->close = twl4030_vibra_close;
set_bit(FF_RUMBLE, info->input_dev->ffbit);
ret = input_register_device(info->input_dev);
@@ -239,8 +281,6 @@ err_ireg:
info->input_dev = NULL;
err_ialloc:
input_free_device(info->input_dev);
-err_workq:
- destroy_workqueue(info->workqueue);
err_kzalloc:
kfree(info);
return ret;
@@ -249,17 +289,8 @@ err_kzalloc:
static int __devexit twl4030_vibra_remove(struct platform_device *pdev)
{
struct vibra_info *info = platform_get_drvdata(pdev);
- struct workqueue_struct *wq;
-
- spin_lock(&info->lock);
- wq = info->workqueue;
- info->workqueue = NULL;
- spin_unlock(&info->lock);
- cancel_work_sync(&info->play_work);
- destroy_workqueue(wq);
- if (info->enabled)
- vibra_disable(info);
+ twl4030_vibra_wq_destroy(info);
/* this also free ff-memless which (in turn) kfree info */
input_unregister_device(info->input_dev);