commit 322ece0d269516d6663103739247d447ed12d4b1
Author: Christian Gabriel <christian.gabriel@auerswald.de>
Date:   Thu Sep 3 12:23:11 2015 +0200

    Touch Filter, Port of Rework done by WM

diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 5fbd264..2bd8b30 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/sort.h>
 
 #include <linux/mfd/ti_am335x_tscadc.h>
 
@@ -33,6 +34,12 @@
 #define SEQ_SETTLE		275
 #define MAX_12BIT		((1 << 12) - 1)
 #define MOVE_DIFF		(5 * 5)
+#define PEN_DETECT_LEVEL	(MAX_12BIT / 8)
+#define PEN_OFF			4
+#define PENFILTER_DEFAULT	100
+#define SORT_SAMPLES		8
+#define Z_FACT			4
+
 
 static const int config_pins[] = {
 	STEPCONFIG_XPP,
@@ -56,11 +63,17 @@ struct titsc {
 	unsigned int		wires;
         struct tsc_axis 	x;
         struct tsc_axis 	y;
-	int			last_x;
-	int			last_y;
-	unsigned int		x_plate_resistance;
+	int			penfilterjiffies;
+	int			pen;
+	struct timer_list	penfiltertimer;
+	unsigned int		x_index;
+	unsigned int		y_index;
+	unsigned short		x_values[SORT_SAMPLES];
+	unsigned short		y_values[SORT_SAMPLES];
+	int			old_sx;
+	int			old_sy;
+	unsigned int		old_z;
 	bool			pen_down;
-	int			coordinate_readouts;
 	u32			config_inp[4];
 	u32			bit_xp, bit_xn, bit_yp, bit_yn;
 	u32			inp_xp, inp_xn, inp_yp, inp_yn;
@@ -236,239 +249,312 @@ static int titsc_config_wires(struct titsc *ts_dev)
 
 static void titsc_step_config(struct titsc *ts_dev)
 {
-	unsigned int	config;
-	int i;
-	int end_step;
-	u32 stepenable;
-
-	config = STEPCONFIG_MODE_HWSYNC |
-			STEPCONFIG_AVG_16 | ts_dev->bit_xp;
-	switch (ts_dev->wires) {
-	case 4:
-		config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
-		break;
-	case 5:
-		config |= ts_dev->bit_yn |
-				STEPCONFIG_INP_AN4 | ts_dev->bit_xn |
-				ts_dev->bit_yp;
-		break;
-	case 8:
-		config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
-		break;
-	}
-
-	/* 1 … coordinate_readouts is for X */
-	end_step = ts_dev->coordinate_readouts;
-	for (i = 0; i < end_step; i++) {
-		titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
-		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
-	}
-
-	config = 0;
-	config = STEPCONFIG_MODE_HWSYNC |
-			STEPCONFIG_AVG_16 | ts_dev->bit_yn |
-			STEPCONFIG_INM_ADCREFM;
-	switch (ts_dev->wires) {
-	case 4:
-		config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
-		break;
-	case 5:
-		config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 |
-				ts_dev->bit_xn | ts_dev->bit_yp;
-		break;
-	case 8:
-		config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
-		break;
-	}
-
-	/* coordinate_readouts … coordinate_readouts * 2 is for Y */
-	end_step = ts_dev->coordinate_readouts * 2;
-	for (i = ts_dev->coordinate_readouts; i < end_step; i++) {
-		titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
-		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
-	}
-
-	/* Charge step configuration */
-	config = ts_dev->bit_xp | ts_dev->bit_yn |
-			STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
-			STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp);
-
-	titsc_writel(ts_dev, REG_CHARGECONFIG, config);
-	titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
-
-	/* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */
-	config = STEPCONFIG_MODE_HWSYNC |
-			STEPCONFIG_AVG_16 | ts_dev->bit_yp |
-			ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
-			STEPCONFIG_INP(ts_dev->inp_xp);
-	titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
-	titsc_writel(ts_dev, REG_STEPDELAY(end_step),
-			STEPCONFIG_OPENDLY);
-
-	end_step++;
-	config |= STEPCONFIG_INP(ts_dev->inp_yn);
-	titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
-	titsc_writel(ts_dev, REG_STEPDELAY(end_step),
-			STEPCONFIG_OPENDLY);
-
-	/* The steps1 … end and bit 0 for TS_Charge */
-	stepenable = (1 << (end_step + 2)) - 1;
-	ts_dev->step_mask = stepenable;
+	unsigned int stepconfig;
+	unsigned int step;
+
+	// NOTE: only support for 4-wire touchscreen at this time.
+
+	step = 0;
+	// =============== Pen Up/Down Detection ====================
+	// Step 1: Drive X and Y foils in opp. directions, X = high
+	//         Y-Foil = low via strong pulldown
+	//         X-Foil = high via strong pullup
+	//         Analog Sample in FIFO0 not used
+	// No Open Delay, no Sample Delay, no Averaging
+	stepconfig = STEPCONFIG_XPP | STEPCONFIG_YNN;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), 0);
+	step++;
+
+	// Step 2: Measure on X Foil. Must be LOW for contact.
+	//         Y-Foil = low via strong pulldown;
+	//         X-Foil = open
+	//         Analog Sample from AIN0 in FIFO1
+	// Open Delay = 1000(us), no Sample Delay, 16 Averaging
+	stepconfig = STEPCONFIG_YNN | STEPCONFIG_AVG_16
+	           | STEPCONFIG_FIFO1 | STEPCONFIG_MODE_SW_ONE;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step),STEPDELAY_OPEN(1000));
+	step++;
+
+	// Step 3: Drive X and Y foils in opp. directions, Y = high
+	//         Y-Foil = high via strong pullup
+	//         X-Foil = low via strong pulldown
+	//         Analog Sample in FIFO0 not used
+	// No Open Delay, no Sample Delay, no Averaging
+	stepconfig = STEPCONFIG_YPP | STEPCONFIG_XNN;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), 0);
+	step++;
+
+	// Step 4: Measure on Y Foil. Must be LOW for contact.
+	//         Y-Foil = open
+	//         X-Foil = low via strong pulldown
+	//         Analog Sample from AIN2 in FIFO1
+	// Open Delay = 1000(us), no Sample Delay, 16 Averaging
+	stepconfig = STEPCONFIG_XNN | STEPCONFIG_AVG_16
+	           | STEPCONFIG_FIFO1 | STEPCONFIG_MODE_SW_ONE
+	           | STEPCONFIG_INP_AN2;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), STEPDELAY_OPEN(1000));
+	step++;
+
+	// ================= Position Detection ====================
+	// Step 5: Measure the Value on X Foil.
+	//         Y-Foil = open
+	//         X-Foil = linear voltage
+	//         Analog Sample from AIN2 to FIFO0
+	// Open Delay = 1000(us), no Sample Delay, 16 Averaging
+	stepconfig = STEPCONFIG_XPP | STEPCONFIG_XNN
+		   | STEPCONFIG_MODE_SW_ONE | STEPCONFIG_INP_AN2
+		   | STEPCONFIG_AVG_16;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), STEPDELAY_OPEN(1000));
+	step++;
+
+	// Step 6: Measure the Value on Y Foil.
+	//         Y-Foil = linear voltage
+	//         X-Foil = open
+	//         Analog Sample from AIN0 to FIFO1
+	// Open Delay = 1000(us), no Sample Delay, 16 Averaging
+	stepconfig = STEPCONFIG_YPP | STEPCONFIG_YNN
+	           | STEPCONFIG_MODE_SW_ONE | STEPCONFIG_AVG_16
+	           | STEPCONFIG_FIFO1;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), STEPDELAY_OPEN(1000));
+	step++;
+
+	// ================= Pressure Detection ====================
+	// Step 7: Measure for Pressure Calculation
+	//         Y-Foil = low on AIN2
+	//         X-Foil = high on AIN1
+	//         Analog Sample from AIN0 to FIFO0
+	// Open Delay = 1000(us), no Sample Delay, 16 Averaging
+	stepconfig = STEPCONFIG_XNP | STEPCONFIG_YPN
+	           | STEPCONFIG_MODE_SW_ONE | STEPCONFIG_AVG_16;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), STEPDELAY_OPEN(1000));
+	step++;
+
+	// Step 8: Measure for Pressure Calculation
+	//         Y-Foil = low on AIN2
+	//         X-Foil = high on AIN1
+	//         Analog Sample from AIN3 to FIFO1
+	// Open Delay = 1000(us), no Sample Delay, 16 Averaging
+	stepconfig = STEPCONFIG_XNP | STEPCONFIG_YPN
+	           | STEPCONFIG_MODE_SW_ONE | STEPCONFIG_AVG_16
+	           | STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1;
+	titsc_writel(ts_dev, REG_STEPCONFIG(step), stepconfig);
+	titsc_writel(ts_dev, REG_STEPDELAY(step), STEPDELAY_OPEN(1000));
+	step++;
+
+	ts_dev->step_mask = (1 << (step + 1)) - 2;
 	am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
 }
 
-static void titsc_read_coordinates(struct titsc *ts_dev,
-		u32 *x, u32 *y, u32 *z1, u32 *z2)
+static void swap_samples(void *a, void *b, int size)
 {
-	unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
-	unsigned int prev_val_x = ~0, prev_val_y = ~0;
-	unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
-	unsigned int read, diff;
-	unsigned int i, channel;
-	unsigned int creads = ts_dev->coordinate_readouts;
-
-	*z1 = *z2 = 0;
-	if (fifocount % (creads * 2 + 2))
-		fifocount -= fifocount % (creads * 2 + 2);
-	/*
-	 * Delta filter is used to remove large variations in sampled
-	 * values from ADC. The filter tries to predict where the next
-	 * coordinate could be. This is done by taking a previous
-	 * coordinate and subtracting it form current one. Further the
-	 * algorithm compares the difference with that of a present value,
-	 * if true the value is reported to the sub system.
-	 */
-	for (i = 0; i < fifocount; i++) {
-		read = titsc_readl(ts_dev, REG_FIFO0);
-
-		channel = (read & 0xf0000) >> 16;
-		read &= 0xfff;
-		if (channel < creads) {
-			diff = abs(read - prev_val_x);
-			if (diff < prev_diff_x) {
-				prev_diff_x = diff;
-				*x = read;
-			}
-			prev_val_x = read;
+        unsigned short t = *(unsigned short *)a;
+        *(unsigned short *)a = *(unsigned short *)b;
+        *(unsigned short *)b = t;
+}
 
-		} else if (channel < creads * 2) {
-			diff = abs(read - prev_val_y);
-			if (diff < prev_diff_y) {
-				prev_diff_y = diff;
-				*y = read;
-			}
-			prev_val_y = read;
+static int comp_samples(const void *a, const void *b)
+{
+        return *(unsigned short *)a - *(unsigned short *)b;
+}
 
-		} else if (channel < creads * 2 + 1) {
-			*z1 = read;
+static void process_samples(struct titsc *ts_dev, unsigned int z1, unsigned int z2)
+{
+	struct input_dev *input_dev = ts_dev->input;
+	unsigned short *sp;
+	int val_x, val_y;
+	int diff_sx, diff_sy;
+	unsigned int z, fact_z;
+
+	/* sort samples */
+	sort(ts_dev->x_values, SORT_SAMPLES, sizeof(ts_dev->x_values[0]), comp_samples, swap_samples);
+	sort(ts_dev->y_values, SORT_SAMPLES, sizeof(ts_dev->y_values[0]), comp_samples, swap_samples);
+	/* calculate mean values */
+	sp = &ts_dev->x_values[(SORT_SAMPLES / 2) - 2];
+	val_x = *sp++;
+	val_x += *sp++;
+	val_x += *sp++;
+	val_x += *sp;
+	val_x >>= 2;
+	sp = &ts_dev->y_values[(SORT_SAMPLES / 2) - 2];
+	val_y = *sp++;
+	val_y += *sp++;
+	val_y += *sp++;
+	val_y += *sp;
+	val_y >>= 2;
+
+	/* z = resistance of pressure (0..MAX_12BIT) */
+	z = (z1 - z2) * (MAX_12BIT - val_x);
+	z /= (MAX_12BIT + 1 - z1);
+	if (z >= MAX_12BIT) {
+		z = 1;
+	} else {
+		z = MAX_12BIT - z;
+	}
 
-		} else if (channel < creads * 2 + 2) {
-			*z2 = read;
-		}
+	/* calculate screen coordinates */
+	val_x = ((val_x - ts_dev->x.offset) * ts_dev->x.scale) >> 12;
+	val_y = ((val_y - ts_dev->y.offset) * ts_dev->y.scale) >> 12;
+
+	/* Check for under/overflow */
+	if (val_x < ts_dev->x.min)
+		val_x = ts_dev->x.min;
+	else if (val_x > ts_dev->x.max)
+		val_x = ts_dev->x.max;
+	if (val_y < ts_dev->y.min)
+		val_y = ts_dev->y.min;
+	else if (val_y > ts_dev->y.max)
+		val_y = ts_dev->y.max;
+
+	/* Do not send the same coordinates twice.
+	(This will prevent CPU load in user space). */
+	diff_sx = val_x - ts_dev->old_sx;
+	if (diff_sx < 0)
+		diff_sx = -diff_sx;
+	diff_sy = val_y - ts_dev->old_sy;
+	if (diff_sy < 0)
+		diff_sy = -diff_sy;
+
+	fact_z = Z_FACT;
+	if (z && ts_dev->old_z) {
+		if (z >= ts_dev->old_z)
+			fact_z = z / ts_dev->old_z;
+		else
+			fact_z = ts_dev->old_z / z;
+	}
+
+	/* calculate sqr(move) */
+	diff_sx = (diff_sx * diff_sx) + (diff_sy * diff_sy);
+
+	/* Check for significant move & pressure change */
+	if ((diff_sx >= MOVE_DIFF) || (fact_z >= Z_FACT)) {
+		ts_dev->old_sx = val_x;
+		ts_dev->old_sy = val_y;
+		ts_dev->old_z = z;
+		// printk(KERN_ERR "Touch at X= %d, Y= %d, P = %d\n", val_x, val_y, z);
+		input_report_key(input_dev, BTN_TOUCH, 1);
+		input_report_abs(input_dev, ABS_X, val_x);
+		input_report_abs(input_dev, ABS_Y, val_y);
+		input_report_abs(input_dev, ABS_PRESSURE, z);
+		input_sync(input_dev);
 	}
+	/* Clear sample buffer */
+	ts_dev->x_index = 0;
+	ts_dev->y_index = 0;
 }
 
+
 static irqreturn_t titsc_irq(int irq, void *dev)
 {
 	struct titsc *ts_dev = dev;
 	struct input_dev *input_dev = ts_dev->input;
 	unsigned int status, irqclr = 0;
-	unsigned int x = 0, y = 0;
-	unsigned int z1, z2, z;
-	unsigned int diff, diff_x, diff_y;
-	unsigned int fsm;
 
 	status = titsc_readl(ts_dev, REG_IRQSTATUS);
-	/*
-	 * ADC and touchscreen share the IRQ line.
-	 * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
-	 */
-	if (status & IRQENB_FIFO0THRES) {
-
-		titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
-
-		if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
-			/*
-			 * Calculate pressure using formula
-			 * Resistance(touch) = x plate resistance *
-			 * x postion/4096 * ((z2 / z1) - 1)
-			 */
-			z = z1 - z2;
-			z *= x;
-			z *= ts_dev->x_plate_resistance;
-			z /= z2;
-			z = (z + 2047) >> 12;
-
-			/* convert to screen coordinates */
+	if (status & IRQENB_EOS) {
+		unsigned int sample_x;
+		unsigned int sample_y;
+		unsigned int z1;
+		unsigned int z2;
+
+		// FIFO0: discard first 2 Samples
+		titsc_readl(ts_dev, REG_FIFO0);
+		titsc_readl(ts_dev, REG_FIFO0);
+
+		// FIFO1: read samples 1 and 2
+		sample_x = titsc_readl(ts_dev, REG_FIFO1) & FIFOREAD_DATA_MASK;
+		sample_y = titsc_readl(ts_dev, REG_FIFO1) & FIFOREAD_DATA_MASK;
+
+		// Detect Pen UP/Down
+		if ((sample_x < PEN_DETECT_LEVEL) && (sample_y < PEN_DETECT_LEVEL)) {
+			// Pen down
+			if (ts_dev->pen > 0) {
+				dev_dbg(&input_dev->dev, "Pen down, pen=%d x=%d, y=%d\n", ts_dev->pen, sample_x, sample_y);
+				ts_dev->pen--;
+				if (ts_dev->pen == 0)
+					del_timer(&ts_dev->penfiltertimer);
+			}
+		} else {
+			// Pen up
+			if (ts_dev->pen < PEN_OFF) {
+				dev_dbg(&input_dev->dev, "Pen up, pen=%d x=%d, y=%d\n", ts_dev->pen, sample_x, sample_y);
+				ts_dev->pen++;
+				if (ts_dev->pen == PEN_OFF)
+					// Retrigger filter timer for pen up
+					mod_timer(&ts_dev->penfiltertimer, jiffies + ts_dev->penfilterjiffies);
+			}
+		}
+
+		// Get Samples for X and Y Position
+		sample_x = titsc_readl(ts_dev, REG_FIFO0) & FIFOREAD_DATA_MASK;
+		sample_y = titsc_readl(ts_dev, REG_FIFO1) & FIFOREAD_DATA_MASK;
+
+		// Get Samples for Pressure Calculation
+		z1 = titsc_readl(ts_dev, REG_FIFO0) & FIFOREAD_DATA_MASK;
+		z2 = titsc_readl(ts_dev, REG_FIFO1) & FIFOREAD_DATA_MASK;
+
+		// Use Samples if pen down
+		if (ts_dev->pen == 0) {
+
+			// Invert samples
 			if (ts_dev->x.inverted)
-				x = MAX_12BIT - x;
+				sample_x = MAX_12BIT - sample_x;
 			if (ts_dev->y.inverted)
-				y = MAX_12BIT - y;
-			x = ((x - ts_dev->x.offset) * ts_dev->x.scale) >> 12;
-			y = ((y - ts_dev->y.offset) * ts_dev->y.scale) >> 12;
-			/* check for over-/underflow */
-			if (x < ts_dev->x.min)
-				x = ts_dev->x.min;
-			else if (x > ts_dev->x.max)
-				x = ts_dev->x.max;
-			if (y < ts_dev->y.min)
-				y = ts_dev->y.min;
-			else if (y > ts_dev->y.max)
-				y = ts_dev->y.max;
-
-			diff_x = abs(x - ts_dev->last_x);
-			diff_y = abs(y - ts_dev->last_y);
-
-			diff = (diff_x * diff_x) + (diff_y * diff_y);
-
-			if (diff >= MOVE_DIFF && z <= MAX_12BIT) {
-				ts_dev->last_x = x;
-				ts_dev->last_y = y;
-				input_report_abs(input_dev, ABS_X, x);
-				input_report_abs(input_dev, ABS_Y, y);
-				input_report_abs(input_dev, ABS_PRESSURE, z);
-				input_report_key(input_dev, BTN_TOUCH, 1);
-				input_sync(input_dev);
+				sample_y = MAX_12BIT - sample_y;
+			dev_dbg(&input_dev->dev, "x=%d, y=%d, z1=%d, z2=%d\n", sample_x, sample_y, z1, z2);
+
+			// Store samples
+			if (ts_dev->x_index < SORT_SAMPLES)
+				ts_dev->x_values[ts_dev->x_index++] = sample_x;
+			if (ts_dev->y_index < SORT_SAMPLES)
+				ts_dev->y_values[ts_dev->y_index++] = sample_y;
+
+			if ((z1 != 0) && (z2 != 0) 
+					&& (ts_dev->x_index >= SORT_SAMPLES) 
+					&& (ts_dev->y_index >= SORT_SAMPLES)) {
+				process_samples(ts_dev, z1, z2);
 			}
 		}
-		irqclr |= IRQENB_FIFO0THRES;
-	}
 
-	/*
-	 * Time for sequencer to settle, to read
-	 * correct state of the sequencer.
-	 */
-	udelay(SEQ_SETTLE);
-
-	status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
-	if (status & IRQENB_PENUP) {
-		/* Pen up event */
-		fsm = titsc_readl(ts_dev, REG_ADCFSM);
-		if (fsm == ADCFSM_STEPID) {
-			ts_dev->last_x = -1000;
-			ts_dev->last_y = -1000;
-			ts_dev->pen_down = false;
-			input_report_key(input_dev, BTN_TOUCH, 0);
-			input_report_abs(input_dev, ABS_PRESSURE, 0);
-			input_sync(input_dev);
-		} else {
-			ts_dev->pen_down = true;
-		}
-		irqclr |= IRQENB_PENUP;
+		// Empty the FIFO from garbage
+		z1 = titsc_readl(ts_dev, REG_FIFO0CNT);
+		for ( ; z1; z1--)
+			titsc_readl(ts_dev, REG_FIFO0);
+		z1 = titsc_readl(ts_dev, REG_FIFO1CNT);
+		for ( ; z1; z1--)
+			titsc_readl(ts_dev, REG_FIFO1);
+
+		irqclr |= IRQENB_EOS;
+		/* start next transfer */
 	}
 
-	if (status & IRQENB_HW_PEN) {
+	titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+	am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
 
-		titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
-		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
-	}
+	return IRQ_HANDLED;
+}
 
-	if (irqclr) {
-		titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
-		am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
-		return IRQ_HANDLED;
-	}
-	return IRQ_NONE;
+static void pentimer_timeout(unsigned long context)
+{
+	struct titsc        *ts_dev = (struct titsc *)context;
+	struct input_dev    *input_dev = ts_dev->input;
+
+	ts_dev->old_sx = -1000;
+	ts_dev->old_sy = -1000;
+	ts_dev->old_z = 0;
+	ts_dev->x_index = 0;
+	ts_dev->y_index = 0;
+
+	// printk(KERN_ERR "Touch End\n");
+	input_report_key(input_dev, BTN_TOUCH, 0);
+	input_report_abs(input_dev, ABS_PRESSURE, 0);
+	input_sync(input_dev);
 }
 
 static int of_property_read_u16_index(const struct device_node *np,
@@ -507,23 +593,6 @@ static int titsc_parse_dt(struct platform_device *pdev,
 		return -EINVAL;
 	}
 
-	err = of_property_read_u32(node, "ti,x-plate-resistance",
-			&ts_dev->x_plate_resistance);
-	if (err < 0)
-		return err;
-
-	/*
-	 * Try with the new binding first. If it fails, try again with
-	 * bogus, miss-spelled version.
-	 */
-	err = of_property_read_u32(node, "ti,coordinate-readouts",
-			&ts_dev->coordinate_readouts);
-	if (err < 0) {
-		dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n");
-		err = of_property_read_u32(node, "ti,coordiante-readouts",
-				&ts_dev->coordinate_readouts);
-	}
-
 	if (err < 0)
 		return err;
 
@@ -585,15 +654,20 @@ static int titsc_probe(struct platform_device *pdev)
 		goto err_free_mem;
 	}
 
-	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
+	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
 	err = titsc_config_wires(ts_dev);
 	if (err) {
 		dev_err(&pdev->dev, "wrong i/p wire configuration\n");
 		goto err_free_irq;
 	}
+
+	ts_dev->penfilterjiffies = msecs_to_jiffies(PENFILTER_DEFAULT);
+	init_timer(&ts_dev->penfiltertimer);
+	ts_dev->penfiltertimer.function = pentimer_timeout;
+	ts_dev->penfiltertimer.data = (unsigned long) ts_dev;
+	ts_dev->pen = PEN_OFF;
+
 	titsc_step_config(ts_dev);
-	titsc_writel(ts_dev, REG_FIFO0THR,
-			ts_dev->coordinate_readouts * 2 + 2 - 1);
 
 	input_dev->name = "ti-tsc";
 	input_dev->dev.parent = &pdev->dev;
@@ -628,14 +702,10 @@ err_free_mem:
 static int titsc_remove(struct platform_device *pdev)
 {
 	struct titsc *ts_dev = platform_get_drvdata(pdev);
-	u32 steps;
 
 	free_irq(ts_dev->irq, ts_dev);
 
-	/* total steps followed by the enable mask */
-	steps = 2 * ts_dev->coordinate_readouts + 2;
-	steps = (1 << steps) - 1;
-	am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps);
+	am335x_tsc_se_clr(ts_dev->mfd_tscadc, ts_dev->step_mask);
 
 	input_unregister_device(ts_dev->input);
 
@@ -674,8 +744,6 @@ static int titsc_resume(struct device *dev)
 		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
 	}
 	titsc_step_config(ts_dev);
-	titsc_writel(ts_dev, REG_FIFO0THR,
-			ts_dev->coordinate_readouts * 2 + 2 - 1);
 	return 0;
 }
 
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index dd4bf58..2b75fd3 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -244,7 +244,7 @@ static	int ti_tscadc_probe(struct platform_device *pdev)
 	ctrl = CNTRLREG_STEPCONFIGWRT |
 			CNTRLREG_STEPID;
 	if (tsc_wires > 0)
-		ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
+		ctrl |= CNTRLREG_TSCENB;
 	tscadc_writel(tscadc, REG_CTRL, ctrl);
 
 	/* Set register bits for Idle Config Mode */
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index fb96c84..17415c5 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -52,6 +52,7 @@
 
 /* IRQ enable */
 #define IRQENB_HW_PEN		BIT(0)
+#define IRQENB_EOS		BIT(1)
 #define IRQENB_FIFO0THRES	BIT(2)
 #define IRQENB_FIFO0OVRRUN	BIT(3)
 #define IRQENB_FIFO0UNDRFLW	BIT(4)
@@ -63,6 +64,7 @@
 /* Step Configuration */
 #define STEPCONFIG_MODE_MASK	(3 << 0)
 #define STEPCONFIG_MODE(val)	((val) << 0)
+#define STEPCONFIG_MODE_SW_ONE	STEPCONFIG_MODE(0)
 #define STEPCONFIG_MODE_SWCNT	STEPCONFIG_MODE(1)
 #define STEPCONFIG_MODE_HWSYNC	STEPCONFIG_MODE(2)
 #define STEPCONFIG_AVG_MASK	(7 << 2)
@@ -79,6 +81,8 @@
 #define STEPCONFIG_INM_ADCREFM	STEPCONFIG_INM(8)
 #define STEPCONFIG_INP_MASK	(0xF << 19)
 #define STEPCONFIG_INP(val)	((val) << 19)
+#define STEPCONFIG_INP_AN2	STEPCONFIG_INP(2)
+#define STEPCONFIG_INP_AN3	STEPCONFIG_INP(3)
 #define STEPCONFIG_INP_AN4	STEPCONFIG_INP(4)
 #define STEPCONFIG_INP_ADCREFM	STEPCONFIG_INP(8)
 #define STEPCONFIG_FIFO1	BIT(26)
@@ -128,7 +132,7 @@
 /* Sequencer Status */
 #define SEQ_STATUS BIT(5)
 
-#define ADC_CLK			3000000
+#define ADC_CLK			2400000
 #define TOTAL_STEPS		16
 #define TOTAL_CHANNELS		8
 #define FIFO1_THRESHOLD		19
