From 76d6ab9228efca87d3e6db46e918c7bbf19f6540 Mon Sep 17 00:00:00 2001
From: Andy Green <andy@openmoko.com>
Date: Fri, 25 Jul 2008 23:06:16 +0100
Subject: [PATCH] touchscreen-meddling.patch

Touchscreen on GTA01-02 experiences noise on the channel that serves the
"tall axis" of the LCM.  The sample quality of the other axis is good.
The bad samples have a characteristic of one shot excursions that can
reach +/- 20% or more of the sample average.

Previously, we had a simple averaging scheme going in the touchscreen
driver that summed up 32 x and ys and then divided it by 32.  This patch
first tidies up the existing code for style, then adds a new "running
average" concept with a FIFO.  The running average is separate from the
summing average mentioned above, and is accurate for the last n samples
sample-by-sample, where n is set by 1 << excursion_filter_len_bits in the
machine / platform stuff.

The heuristic the patch implements for the filtering is to accept all
samples, but tag the *previous* sample with a flag if it differed from
the running average by more than reject_threshold_vs_avg in either
axis.  The next sample time, a beauty contest is held if the flag was
set to decide if we think the previous sample was a one-shot excursion
(detected by the new sample being closer to the average than to the
flagged previous sample), or if we believe we are moving (detected by
the new sample being closer to the flagged previous sample than the
average.  In the case that we believe the previous sample was an
excursion, we simply overwrite it with the new data and adjust the
summing average to use the new data instead of the excursion data.

I only tested this by eyeballing the output of ts_print_raw, but it
seemed to be quite a bit better.  Gross movement appeared to be
tracked fine too.  If folks want to try different heuristics on top
of this patch, be my guest; either way feedback on what it looks like
with a graphical app would be good.

Signed-off-by: Andy Green <andy@openmoko.com>
---
 arch/arm/mach-s3c2440/mach-gta02.c     |   10 +-
 drivers/input/touchscreen/s3c2410_ts.c |  256 ++++++++++++++++++++++++--------
 include/asm-arm/arch-s3c2410/ts.h      |    8 +-
 3 files changed, 205 insertions(+), 69 deletions(-)

diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index c32bb2a..afe8039 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -897,10 +897,18 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = {
 
 static struct s3c2410_ts_mach_info gta02_ts_cfg = {
 	.delay = 10000,
-	.presc = 65,
+	.presc = 50000000 / 1000000, /* 50 MHz PCLK / 1MHz */
+	/* simple averaging, 2^n samples */
 	.oversampling_shift = 5,
+	 /* averaging filter length, 2^n */
+	.excursion_filter_len_bits = 5,
+	/* flagged for beauty contest on next sample if differs from
+	 * average more than this
+	 */
+	.reject_threshold_vs_avg = 2,
 };
 
+
 /* SPI: LCM control interface attached to Glamo3362 */
 
 static void gta02_jbt6k74_reset(int devidx, int level)
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index 1251454..b1ba73d 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -36,6 +36,9 @@
  *
  * 2007-05-23: Harald Welte <laforge@openmoko.org>
  * 	- Add proper support for S32440
+ *
+ * 2008-06-18: Andy Green <andy@openmoko.com>
+ *      - Outlier removal
  */
 
 #include <linux/errno.h>
@@ -62,11 +65,16 @@
 #define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
 
 #define WAIT4INT(x)  (((x)<<8) | \
-		     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
+		     S3C2410_ADCTSC_YM_SEN | \
+		     S3C2410_ADCTSC_YP_SEN | \
+		     S3C2410_ADCTSC_XP_SEN | \
 		     S3C2410_ADCTSC_XY_PST(3))
 
-#define AUTOPST	     (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
-		     S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
+#define AUTOPST	     (S3C2410_ADCTSC_YM_SEN | \
+		      S3C2410_ADCTSC_YP_SEN | \
+		      S3C2410_ADCTSC_XP_SEN | \
+		      S3C2410_ADCTSC_AUTO_PST | \
+		      S3C2410_ADCTSC_XY_PST(0))
 
 #define DEBUG_LVL    KERN_DEBUG
 
@@ -85,17 +93,46 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen";
  * Per-touchscreen data.
  */
 
+struct s3c2410ts_sample {
+	int x;
+	int y;
+};
+
 struct s3c2410ts {
 	struct input_dev *dev;
 	long xp;
 	long yp;
 	int count;
 	int shift;
+	int extent;  /* 1 << shift */
+
+	/* the raw sample fifo is a lightweight way to track a running average
+	 * of all taken samples.  "running average" here means that it gives
+	 * correct average for each sample, not only at the end of block of
+	 * samples
+	 */
+	int excursion_filter_len;
+	struct s3c2410ts_sample *raw_sample_fifo;
+	int head_raw_fifo;
+	int tail_raw_fifo;
+	struct s3c2410ts_sample raw_running_avg;
+	int reject_threshold_vs_avg;
+	int flag_previous_exceeded_threshold;
 };
 
 static struct s3c2410ts ts;
 static void __iomem *base_addr;
 
+static void clear_raw_fifo(void)
+{
+	ts.head_raw_fifo = 0;
+	ts.tail_raw_fifo = 0;
+	ts.raw_running_avg.x = 0;
+	ts.raw_running_avg.y = 0;
+	ts.flag_previous_exceeded_threshold = 0;
+}
+
+
 static inline void s3c2410_ts_connect(void)
 {
 	s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
@@ -110,47 +147,52 @@ static void touch_timer_fire(unsigned long data)
   	unsigned long data1;
 	int updown;
 
-  	data0 = readl(base_addr+S3C2410_ADCDAT0);
-  	data1 = readl(base_addr+S3C2410_ADCDAT1);
+	data0 = readl(base_addr + S3C2410_ADCDAT0);
+	data1 = readl(base_addr + S3C2410_ADCDAT1);
 
- 	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
+					    (!(data1 & S3C2410_ADCDAT0_UPDOWN));
 
- 	if (updown) {
- 		if (ts.count != 0) {
- 			ts.xp >>= ts.shift;
- 			ts.yp >>= ts.shift;
+	if (updown) {
+		if (ts.count != 0) {
+			ts.xp >>= ts.shift;
+			ts.yp >>= ts.shift;
 
 #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
- 			{
- 				struct timeval tv;
- 				do_gettimeofday(&tv);
- 				printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
- 			}
+			{
+				struct timeval tv;
+
+				do_gettimeofday(&tv);
+				printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n",
+						 (int)tv.tv_usec, ts.xp, ts.yp);
+			}
 #endif
 
- 			input_report_abs(ts.dev, ABS_X, ts.xp);
- 			input_report_abs(ts.dev, ABS_Y, ts.yp);
+			input_report_abs(ts.dev, ABS_X, ts.xp);
+			input_report_abs(ts.dev, ABS_Y, ts.yp);
 
- 			input_report_key(ts.dev, BTN_TOUCH, 1);
- 			input_report_abs(ts.dev, ABS_PRESSURE, 1);
- 			input_sync(ts.dev);
- 		}
+			input_report_key(ts.dev, BTN_TOUCH, 1);
+			input_report_abs(ts.dev, ABS_PRESSURE, 1);
+			input_sync(ts.dev);
+		}
 
- 		ts.xp = 0;
- 		ts.yp = 0;
- 		ts.count = 0;
+		ts.xp = 0;
+		ts.yp = 0;
+		ts.count = 0;
 
- 		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
- 		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
- 	} else {
- 		ts.count = 0;
+		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+						      base_addr+S3C2410_ADCTSC);
+		writel(readl(base_addr+S3C2410_ADCCON) |
+			 S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
+	} else {
+		ts.count = 0;
 
- 		input_report_key(ts.dev, BTN_TOUCH, 0);
- 		input_report_abs(ts.dev, ABS_PRESSURE, 0);
- 		input_sync(ts.dev);
+		input_report_key(ts.dev, BTN_TOUCH, 0);
+		input_report_abs(ts.dev, ABS_PRESSURE, 0);
+		input_sync(ts.dev);
 
- 		writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
- 	}
+		writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+	}
 }
 
 static struct timer_list touch_timer =
@@ -165,7 +207,8 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
 	da<style>pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="gd">--- a/drivers/usb/host/ehci-hcd.c</span>
<span class="gi">+++ b/drivers/usb/host/ehci-hcd.c</span>
<span class="gu">@@ -252,6 +252,37 @@ int ehci_reset(struct ehci_hcd *ehci)</span>
 	command |= CMD_RESET;
 	dbg_cmd (ehci, &quot;reset&quot;, command);
 	ehci_writel(ehci, command, &amp;ehci-&gt;regs-&gt;command);
<span class="gi">+</span>
<span class="gi">+	if (ehci-&gt;qca_force_host_mode) {</span>
<span class="gi">+		u32 usbmode;</span>
<span class="gi">+</span>
<span class="gi">+		udelay(1000);</span>
<span class="gi">+</span>
<span class="gi">+		usbmode = ehci_readl(ehci, &amp;ehci-&gt;regs-&gt;usbmode);</span>
<span class="gi">+		usbmode |= USBMODE_CM_HC | (1 &lt;&lt; 4);</span>
<span class="gi">+		ehci_writel(ehci, usbmode, &amp;ehci-&gt;regs-&gt;usbmode);</span>
<span class="gi">+</span>
<span class="gi">+		ehci_dbg(ehci, &quot;forced host mode, usbmode: %08x\n&quot;,</span>
<span class="gi">+			 ehci_readl(ehci, &amp;ehci-&gt;regs-&gt;usbmode));</span>
<span class="gi">+	}</span>
<span class="gi">+</span>
<span class="gi">+	if (ehci-&gt;qca_force_16bit_ptw) {</span>
<span class="gi">+		u32 port_status;</span>
<span class="gi">+</span>
<span class="gi">+		udelay(1000);</span>
<span class="gi">+</span>
<span class="gi">+		/* enable 16-bit UTMI interface */</span>
<span class="gi">+		port_status = ehci_readl(ehci, &amp;ehci-&gt;regs-&gt;port_status[0]);</span>
<span class="gi">+		port_status |= BIT(28);</span>
<span class="gi">+		ehci_writel(ehci, port_status, &amp;ehci-&gt;regs-&gt;port_status[0]);</span>
<span class="gi">+</span>
<span class="gi">+		ehci_dbg(ehci, &quot;16-bit UTMI interface enabled, status: %08x\n&quot;,</span>
<span class="gi">+			 ehci_readl(ehci, &amp;ehci-&gt;regs-&gt;port_status[0]));</span>
<span class="gi">+	}</span>
<span class="gi">+</span>
<span class="gi">+	if (ehci-&gt;reset_notifier)</span>
<span class="gi">+		ehci-&gt;reset_notifier(ehci_to_hcd(ehci));</span>
<span class="gi">+</span>
 	ehci-&gt;rh_state = EHCI_RH_HALTED;
 	ehci-&gt;next_statechange = jiffies;
 	retval = ehci_handshake(ehci, &amp;ehci-&gt;regs-&gt;command,
<span class="gd">--- a/drivers/usb/host/ehci.h</span>
<span class="gi">+++ b/drivers/usb/host/ehci.h</span>
<span class="gu">@@ -228,6 +228,10 @@ struct ehci_hcd {			/* one per controlle</span>
 	unsigned		need_oc_pp_cycle:1; /* MPC834X port power */
 	unsigned		imx28_write_fix:1; /* For Freescale i.MX28 */
 	unsigned		ignore_oc:1;
<span class="gi">+	unsigned		qca_force_host_mode:1;</span>
<span class="gi">+	unsigned		qca_force_16bit_ptw:1; /* force 16 bit UTMI */</span>
<span class="gi">+</span>
<span class="gi">+	void (*reset_notifier)(struct usb_hcd *hcd);</span>
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 &lt;&lt; 6)
<span class="gd">--- a/include/linux/usb/ehci_pdriver.h</span>
<span class="gi">+++ b/include/linux/usb/ehci_pdriver.h</span>
<span class="gu">@@ -50,6 +50,8 @@ struct usb_ehci_pdata {</span>
 	unsigned	reset_on_resume:1;
 	unsigned	dma_mask_64:1;
 	unsigned	ignore_oc:1;
<span class="gi">+	unsigned	qca_force_host_mode:1;</span>
<span class="gi">+	unsigned	qca_force_16bit_ptw:1;</span>
 
 	/* Turn on all power and clocks */
 	int (*power_on)(struct platform_device *pdev);
<span class="gu">@@ -59,6 +61,7 @@ struct usb_ehci_pdata {</span>
 	 * turn off everything else */
 	void (*power_suspend)(struct platform_device *pdev);
 	int (*pre_setup)(struct usb_hcd *hcd);
<span class="gi">+	void (*reset_notifier)(struct platform_device *pdev);</span>
 };
 
 #endif /* __USB_CORE_EHCI_PDRIVER_H */
<span class="gd">--- a/drivers/usb/host/ehci-platform.c</span>
<span class="gi">+++ b/drivers/usb/host/ehci-platform.c</span>
<span class="gu">@@ -51,6 +51,14 @@ struct ehci_platform_priv {</span>
 
 static const char hcd_name[] = &quot;ehci-platform&quot;;
 
<span class="gi">+static void ehci_platform_reset_notifier(struct usb_hcd *hcd)</span>
<span class="gi">+{</span>
<span class="gi">+	struct platform_device *pdev = to_platform_device(hcd-&gt;self.controller);</span>
<span class="gi">+	struct usb_ehci_pdata *pdata = pdev-&gt;dev.platform_data;</span>
<span class="gi">+</span>
<span class="gi">+	pdata-&gt;reset_notifier(pdev);</span>
<span class="gi">+}</span>
<span class="gi">+</span>
 static int ehci_platform_reset(struct usb_hcd *hcd)
 {
 	struct platform_device *pdev = to_platform_device(hcd-&gt;self.controller);
<span class="gu">@@ -256,6 +264,13 @@ static int ehci_platform_probe(struct pl</span>
 		priv-&gt;reset_on_resume = true;
 	if (pdata-&gt;ignore_oc)
 		ehci-&gt;ignore_oc = 1;
<span class="gi">+	if (pdata-&gt;qca_force_host_mode)</span>
<span class="gi">+		ehci-&gt;qca_force_host_mode = 1;</span>
<span class="gi">+	if (pdata-&gt;qca_force_16bit_ptw)</span>
<span class="gi">+		ehci-&gt;qca_force_16bit_ptw = 1;</span>
<span class="gi">+</span>
<span class="gi">+	if (pdata-&gt;reset_notifier)</span>
<span class="gi">+		ehci-&gt;reset_notifier = ehci_platform_reset_notifier;</span>
 
 #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
 	if (ehci-&gt;big_endian_mmio) {
</pre></div>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.3</a> (<a href='https://git-scm.com/'>git 2.25.1</a>) at 2025-03-12 04:45:54 +0000</div>
</div> <!-- id=cgit -->
</body>
</html>