#include "psmouse.h"
 #include "hgpk.h"
 
+#define ILLEGAL_XY 999999
+
 static bool tpdebug;
 module_param(tpdebug, bool, 0644);
 MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
 static int recalib_delta = 100;
 module_param(recalib_delta, int, 0644);
 MODULE_PARM_DESC(recalib_delta,
-       "packets containing a delta this large will cause a recalibration.");
+       "packets containing a delta this large will be discarded, and a "
+       "recalibration may be scheduled.");
 
-static int jumpy_delay = 1000;
+static int jumpy_delay = 20;
 module_param(jumpy_delay, int, 0644);
 MODULE_PARM_DESC(jumpy_delay,
        "delay (ms) before recal after jumpiness detected");
 }
 
 /*
- * When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
- * above the pad and still have it send packets.  This causes a jump cursor
- * when one places their finger on the pad.  We can probably detect the
- * jump as we see a large deltas (>= 100px).  In mouse mode, I've been
- * unable to even come close to 100px deltas during normal usage, so I think
- * this threshold is safe.  If a large delta occurs, trigger a recalibration.
+ * see if new value is within 20% of half of old value
+ */
+static int approx_half(int curr, int prev)
+{
+       int belowhalf, abovehalf;
+
+       if (curr < 5 || prev < 5)
+               return 0;
+
+       belowhalf = (prev * 8) / 20;
+       abovehalf = (prev * 12) / 20;
+
+       return belowhalf < curr && curr <= abovehalf;
+}
+
+/*
+ * Throw out oddly large delta packets, and any that immediately follow whose
+ * values are each approximately half of the previous.  It seems that the ALPS
+ * firmware emits errant packets, and they get averaged out slowly.
  */
-static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
+static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
 {
        struct hgpk_data *priv = psmouse->private;
+       int avx, avy;
+       bool do_recal = false;
+
+       avx = abs(x);
+       avy = abs(y);
+
+       /* discard if too big, or half that but > 4 times the prev delta */
+       if (avx > recalib_delta ||
+               (avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) {
+               hgpk_err(psmouse, "detected %dpx jump in x\n", x);
+               priv->xbigj = avx;
+       } else if (approx_half(avx, priv->xbigj)) {
+               hgpk_err(psmouse, "detected secondary %dpx jump in x\n", x);
+               priv->xbigj = avx;
+               priv->xsaw_secondary++;
+       } else {
+               if (priv->xbigj && priv->xsaw_secondary > 1)
+                       do_recal = true;
+               priv->xbigj = 0;
+               priv->xsaw_secondary = 0;
+       }
 
-       if (abs(x) > recalib_delta || abs(y) > recalib_delta) {
-               hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n",
-                               recalib_delta, x, y);
-               /* My car gets forty rods to the hogshead and that's the
-                * way I likes it! */
+       if (avy > recalib_delta ||
+               (avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) {
+               hgpk_err(psmouse, "detected %dpx jump in y\n", y);
+               priv->ybigj = avy;
+       } else if (approx_half(avy, priv->ybigj)) {
+               hgpk_err(psmouse, "detected secondary %dpx jump in y\n", y);
+               priv->ybigj = avy;
+               priv->ysaw_secondary++;
+       } else {
+               if (priv->ybigj && priv->ysaw_secondary > 1)
+                       do_recal = true;
+               priv->ybigj = 0;
+               priv->ysaw_secondary = 0;
+       }
+
+       priv->xlast = avx;
+       priv->ylast = avy;
+
+       if (do_recal && jumpy_delay) {
+               hgpk_err(psmouse, "scheduling recalibration\n");
                psmouse_queue_work(psmouse, &priv->recalib_wq,
                                msecs_to_jiffies(jumpy_delay));
        }
+
+       return priv->xbigj || priv->ybigj;
 }
 
 static void hgpk_reset_spew_detection(struct hgpk_data *priv)
        struct hgpk_data *priv = psmouse->private;
 
        priv->abs_x = priv->abs_y = -1;
+       priv->xlast = priv->ylast = ILLEGAL_XY;
+       priv->xbigj = priv->ybigj = 0;
+       priv->xsaw_secondary = priv->ysaw_secondary = 0;
        hgpk_reset_spew_detection(priv);
 }
 
         * tracking so that we don't erroneously detect a jump on next press.
         */
        if (!down) {
-               hgpk_reset_hack_state(priv);
+               hgpk_reset_hack_state(psmouse);
                goto done;
        }
 
 
        /* Don't apply hacks in PT mode, it seems reliable */
        if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) {
-               hgpk_jumpy_hack(psmouse,
-                               priv->abs_x - x, priv->abs_y - y);
-               hgpk_spewing_hack(psmouse, left, right,
-                                 priv->abs_x - x, priv->abs_y - y);
+               int x_diff = priv->abs_x - x;
+               int y_diff = priv->abs_y - y;
+               if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) {
+                       if (tpdebug)
+                               hgpk_dbg(psmouse, "discarding\n");
+                       goto done;
+               }
+               hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff);
        }
 
        input_report_abs(idev, ABS_X, x);
        int x = packet[1] - ((packet[0] << 4) & 0x100);
        int y = ((packet[0] << 3) & 0x100) - packet[2];
 
-       hgpk_jumpy_hack(psmouse, x, y);
+       if (hgpk_discard_decay_hack(psmouse, x, y)) {
+               if (tpdebug)
+                       hgpk_dbg(psmouse, "discarding\n");
+               return;
+       }
+
        hgpk_spewing_hack(psmouse, left, right, x, y);
 
        if (tpdebug)