staging: Use GFP_ATOMIC when a lock is held
[safe/jmp/linux-2.6] / drivers / input / ff-memless.c
index eba18b6..1d881c9 100644 (file)
 
 #define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg)
 
+#include <linux/slab.h>
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
-#include <linux/sched.h>
+#include <linux/jiffies.h>
 
 #include "fixp-arith.h"
 
@@ -61,7 +62,6 @@ struct ml_device {
        struct ml_effect_state states[FF_MEMLESS_EFFECTS];
        int gain;
        struct timer_list timer;
-       spinlock_t timer_lock;
        struct input_dev *dev;
 
        int (*play_effect)(struct input_dev *dev, void *data,
@@ -222,6 +222,22 @@ static int get_compatible_type(struct ff_device *ff, int effect_type)
 }
 
 /*
+ * Only left/right direction should be used (under/over 0x8000) for
+ * forward/reverse motor direction (to keep calculation fast & simple).
+ */
+static u16 ml_calculate_direction(u16 direction, u16 force,
+                                 u16 new_direction, u16 new_force)
+{
+       if (!force)
+               return new_direction;
+       if (!new_force)
+               return direction;
+       return (((u32)(direction >> 1) * force +
+                (new_direction >> 1) * new_force) /
+               (force + new_force)) << 1;
+}
+
+/*
  * Combine two effects and apply gain.
  */
 static void ml_combine_effects(struct ff_effect *effect,
@@ -247,14 +263,27 @@ static void ml_combine_effects(struct ff_effect *effect,
                 * in s8, this should be changed to something more generic
                 */
                effect->u.ramp.start_level =
-                       max(min(effect->u.ramp.start_level + x, 0x7f), -0x80);
+                       clamp_val(effect->u.ramp.start_level + x, -0x80, 0x7f);
                effect->u.ramp.end_level =
-                       max(min(effect->u.ramp.end_level + y, 0x7f), -0x80);
+                       clamp_val(effect->u.ramp.end_level + y, -0x80, 0x7f);
                break;
 
        case FF_RUMBLE:
-               strong = new->u.rumble.strong_magnitude * gain / 0xffff;
-               weak = new->u.rumble.weak_magnitude * gain / 0xffff;
+               strong = (u32)new->u.rumble.strong_magnitude * gain / 0xffff;
+               weak = (u32)new->u.rumble.weak_magnitude * gain / 0xffff;
+
+               if (effect->u.rumble.strong_magnitude + strong)
+                       effect->direction = ml_calculate_direction(
+                               effect->direction,
+                               effect->u.rumble.strong_magnitude,
+                               new->direction, strong);
+               else if (effect->u.rumble.weak_magnitude + weak)
+                       effect->direction = ml_calculate_direction(
+                               effect->direction,
+                               effect->u.rumble.weak_magnitude,
+                               new->direction, weak);
+               else
+                       effect->direction = 0;
                effect->u.rumble.strong_magnitude =
                        min(strong + effect->u.rumble.strong_magnitude,
                            0xffffU);
@@ -269,6 +298,13 @@ static void ml_combine_effects(struct ff_effect *effect,
                /* here we also scale it 0x7fff => 0xffff */
                i = i * gain / 0x7fff;
 
+               if (effect->u.rumble.strong_magnitude + i)
+                       effect->direction = ml_calculate_direction(
+                               effect->direction,
+                               effect->u.rumble.strong_magnitude,
+                               new->direction, i);
+               else
+                       effect->direction = 0;
                effect->u.rumble.strong_magnitude =
                        min(i + effect->u.rumble.strong_magnitude, 0xffffU);
                effect->u.rumble.weak_magnitude =
@@ -368,38 +404,39 @@ static void ml_effect_timer(unsigned long timer_data)
 {
        struct input_dev *dev = (struct input_dev *)timer_data;
        struct ml_device *ml = dev->ff->private;
+       unsigned long flags;
 
        debug("timer: updating effects");
 
-       spin_lock(&ml->timer_lock);
+       spin_lock_irqsave(&dev->event_lock, flags);
        ml_play_effects(ml);
-       spin_unlock(&ml->timer_lock);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
+/*
+ * Sets requested gain for FF effects. Called with dev->event_lock held.
+ */
 static void ml_ff_set_gain(struct input_dev *dev, u16 gain)
 {
        struct ml_device *ml = dev->ff->private;
        int i;
 
-       spin_lock_bh(&ml->timer_lock);
-
        ml->gain = gain;
 
        for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
                __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags);
 
        ml_play_effects(ml);
-
-       spin_unlock_bh(&ml->timer_lock);
 }
 
+/*
+ * Start/stop specified FF effect. Called with dev->event_lock held.
+ */
 static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
 {
        struct ml_device *ml = dev->ff->private;
        struct ml_effect_state *state = &ml->states[effect_id];
 
-       spin_lock_bh(&ml->timer_lock);
-
        if (value > 0) {
                debug("initiated play");
 
@@ -411,8 +448,6 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
                                 msecs_to_jiffies(state->effect->replay.length);
                state->adj_at = state->play_at;
 
-               ml_schedule_timer(ml);
-
        } else {
                debug("initiated stop");
 
@@ -420,11 +455,9 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
                        __set_bit(FF_EFFECT_ABORTING, &state->flags);
                else
                        __clear_bit(FF_EFFECT_STARTED, &state->flags);
-
-               ml_play_effects(ml);
        }
 
-       spin_unlock_bh(&ml->timer_lock);
+       ml_play_effects(ml);
 
        return 0;
 }
@@ -435,7 +468,7 @@ static int ml_ff_upload(struct input_dev *dev,
        struct ml_device *ml = dev->ff->private;
        struct ml_effect_state *state = &ml->states[effect->id];
 
-       spin_lock_bh(&ml->timer_lock);
+       spin_lock_irq(&dev->event_lock);
 
        if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
                __clear_bit(FF_EFFECT_PLAYING, &state->flags);
@@ -447,7 +480,7 @@ static int ml_ff_upload(struct input_dev *dev,
                ml_schedule_timer(ml);
        }
 
-       spin_unlock_bh(&ml->timer_lock);
+       spin_unlock_irq(&dev->event_lock);
 
        return 0;
 }
@@ -481,7 +514,6 @@ int input_ff_create_memless(struct input_dev *dev, void *data,
        ml->private = data;
        ml->play_effect = play_effect;
        ml->gain = 0xffff;
-       spin_lock_init(&ml->timer_lock);
        setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev);
 
        set_bit(FF_GAIN, dev->ffbit);