summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/imx233/touchscreen-imx233.c
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2012-05-19 13:42:49 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2012-05-19 16:10:52 +0200
commit85d5c19347c1665b765708cdb67f1d996c7f7fa4 (patch)
tree14cc20dbd694e408ece18c7d9788c2a74cef042e /firmware/target/arm/imx233/touchscreen-imx233.c
parent247e611f4945d048f280471d78b7b83a96b35454 (diff)
downloadrockbox-85d5c19347c1665b765708cdb67f1d996c7f7fa4.tar.gz
rockbox-85d5c19347c1665b765708cdb67f1d996c7f7fa4.tar.bz2
rockbox-85d5c19347c1665b765708cdb67f1d996c7f7fa4.zip
imx233: add touchscreen driver
Change-Id: I406076d110e33cdae871222191d82262fabcf16a
Diffstat (limited to 'firmware/target/arm/imx233/touchscreen-imx233.c')
-rw-r--r--firmware/target/arm/imx233/touchscreen-imx233.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/touchscreen-imx233.c b/firmware/target/arm/imx233/touchscreen-imx233.c
new file mode 100644
index 0000000000..f8cbf76896
--- /dev/null
+++ b/firmware/target/arm/imx233/touchscreen-imx233.c
@@ -0,0 +1,193 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2012 by Amaury Pouly
+ *
+ * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
+ * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "touchscreen-imx233.h"
+#include "stdlib.h"
+
+/* Description:
+ * the driver basically has 2 modes:
+ * - wait mode: use the hardware touch detect mechanism to wait for an edge
+ * - measurement mode: use pull up/down and adc to measure X/Y
+ * In measurement mode, we start by measuring X then Y then we check if
+ * the hardware still detect a touch. When no touch is detected anymore, we
+ * go back to wait mode.
+ * For each axis, we handle stabilization by repeatedly measuring the position
+ * until it is stable. We consider a set of measures stable when
+ * no measure is further away from the average than DEBOUNCE_THRESHOLD and
+ * we have at least SAMPLES_THRESHOLD measures. This avoids using magic
+ * timing constants specific to the touchscreen stabilization time and
+ * it is much more robust.
+ *
+ * Inspired by linux touchscreen driver for the stmp37xx.
+ */
+
+enum touch_state_t
+{
+ TOUCH_STATE_WAIT, /* wait for touch */
+ TOUCH_STATE_MEASURE_X, /* measure x */
+ TOUCH_STATE_MEASURE_Y, /* measure y */
+ TOUCH_STATE_VERIFY, /* verify touch */
+};
+
+#define DEBOUNCE_THRESHOLD 100
+#define SAMPLES_THRESHOLD 4
+
+static enum touch_state_t touch_state;
+static int touch_chan = -1;
+static int touch_delay = -1;
+static int touch_x, touch_y, nr_samples;
+/* once a touch is confirmed, the parameters are copied to these value for
+ * instant readout by button code. */
+static bool old_touch_detect = false;
+static int old_touch_x, old_touch_y;
+
+static void kick_measure(bool pull_x, bool pull_y, bool detect, int lradc_phys)
+{
+ /* setup measurement: x- pull down and x+ pull up */
+ imx233_lradc_setup_touch(pull_x, pull_y, pull_x, pull_y, detect);
+ imx233_lradc_enable_touch_detect_irq(false);
+ imx233_lradc_enable_channel_irq(touch_chan, true);
+ /* measure channel, no accumulation */
+ imx233_lradc_setup_channel(touch_chan, false, false, 0, lradc_phys);
+ /* use a delay */
+ imx233_lradc_setup_delay(touch_delay, 1 << touch_chan, 0, 0, 8);
+ imx233_lradc_kick_delay(touch_delay);
+}
+
+static void enter_state(enum touch_state_t state)
+{
+ touch_state = state;
+ switch(state)
+ {
+ case TOUCH_STATE_WAIT:
+ imx233_lradc_setup_touch(false, false, false, false, true);
+ imx233_lradc_enable_channel_irq(touch_chan, false);
+ imx233_lradc_enable_touch_detect_irq(true);
+ break;
+ case TOUCH_STATE_MEASURE_X:
+ kick_measure(true, false, false, HW_LRADC_CHANNEL_YPLUS);
+ break;
+ case TOUCH_STATE_MEASURE_Y:
+ kick_measure(false, true, false, HW_LRADC_CHANNEL_XPLUS);
+ break;
+ case TOUCH_STATE_VERIFY:
+ kick_measure(false, false, true, HW_LRADC_CHANNEL_YPLUS);
+ break;
+ }
+}
+
+static void process(void)
+{
+ int val;
+ switch(touch_state)
+ {
+ case TOUCH_STATE_WAIT:
+ /* a touch has happened, check if it's still valid */
+ if(imx233_lradc_read_touch_detect())
+ enter_state(TOUCH_STATE_MEASURE_X);
+ else
+ {
+ old_touch_detect = false;
+ /* clear detect interrupt */
+ imx233_lradc_clear_touch_detect_irq();
+ }
+ break;
+ case TOUCH_STATE_MEASURE_X:
+ /* read value */
+ val = imx233_lradc_read_channel(touch_chan);
+ /* if value is too far from average, restart */
+ if(nr_samples > 0 && abs(val - touch_x) > DEBOUNCE_THRESHOLD)
+ nr_samples = 0;
+ touch_x = (touch_x * nr_samples + val) / (nr_samples + 1);
+ nr_samples++;
+ /* if we have enough samples, measure Y */
+ if(nr_samples > SAMPLES_THRESHOLD)
+ enter_state(TOUCH_STATE_MEASURE_Y);
+ else
+ imx233_lradc_kick_delay(touch_delay);
+ break;
+ case TOUCH_STATE_MEASURE_Y:
+ /* read value */
+ val = imx233_lradc_read_channel(touch_chan);
+ /* if value is too far from average, restart */
+ if(nr_samples > 0 && abs(val - touch_y) > DEBOUNCE_THRESHOLD)
+ nr_samples = 0;
+ touch_y = (touch_y * nr_samples + val) / (nr_samples + 1);
+ nr_samples++;
+ /* if we have enough samples, verify touch */
+ if(nr_samples > SAMPLES_THRESHOLD)
+ enter_state(TOUCH_STATE_VERIFY);
+ else
+ imx233_lradc_kick_delay(touch_delay);
+ break;
+ case TOUCH_STATE_VERIFY:
+ if(imx233_lradc_read_touch_detect())
+ {
+ old_touch_detect = true;
+ old_touch_x = touch_x;
+ old_touch_y = touch_y;
+ enter_state(TOUCH_STATE_MEASURE_X);
+ }
+ else
+ {
+ old_touch_detect = false;
+ enter_state(TOUCH_STATE_WAIT);
+ }
+ break;
+ }
+}
+
+void INT_TOUCH_DETECT(void)
+{
+ process();
+}
+
+static void touch_channel_irq(int chan)
+{
+ (void) chan;
+ process();
+}
+
+void imx233_touchscreen_init(void)
+{
+ touch_chan = imx233_lradc_acquire_channel(TIMEOUT_NOBLOCK);
+ touch_delay = imx233_lradc_acquire_delay(TIMEOUT_NOBLOCK);
+ if(touch_chan < 0 || touch_delay < 0)
+ panicf("Cannot acquire channel and delays for touchscreen measurement");
+ imx233_touchscreen_enable(false);
+}
+
+void imx233_touchscreen_enable(bool enable)
+{
+ enter_state(TOUCH_STATE_WAIT);
+ imx233_lradc_set_channel_irq_callback(touch_chan, &touch_channel_irq);
+ imx233_icoll_enable_interrupt(INT_SRC_LRADC_CHx(touch_chan), enable);
+ imx233_icoll_enable_interrupt(INT_SRC_TOUCH_DETECT, enable);
+}
+
+bool imx233_touchscreen_get_touch(int *raw_x, int *raw_y)
+{
+ if(raw_x) *raw_x = old_touch_x;
+ if(raw_y) *raw_y = old_touch_y;
+ return old_touch_detect;
+}