/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2012 by Amaury Pouly * * 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 "config.h" #include "kernel.h" #include "touchscreen-imx233.h" #include "stdlib.h" #ifdef SAMSUNG_YPZ5 #include "pinctrl-imx233.h" #endif /* 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 NR_SAMPLES 10 #define DELAY 1 static enum touch_state_t touch_state; static int touch_chan = -1; static int touch_delay = -1; static int touch_x, touch_y; /* 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 process(void); void INT_TOUCH_DETECT(void) { process(); } static void touch_channel_irq(int chan) { imx233_icoll_enable_interrupt(INT_SRC_LRADC_CHx(chan), false); imx233_lradc_enable_channel_irq(chan, false); process(); } #ifdef SAMSUNG_YPZ5 /* On this target we need to manually setup pulldown pins, * using specific GPIO lines */ static void pulldown_setup(bool xminus_enable, bool yminus_enable, bool xplus_enable, bool yplus_enable) { /* TX+ */ imx233_pinctrl_set_gpio(0, 25, xplus_enable); /* TX- */ imx233_pinctrl_set_gpio(3, 15, xminus_enable); /* TY+ */ imx233_pinctrl_set_gpio(0, 26, yplus_enable); /* TY- */ imx233_pinctrl_set_gpio(1, 21, yminus_enable); } #endif static void kick_measure(bool pull_x, bool pull_y, bool detect, int src) { if(touch_chan >= 0) imx233_lradc_release_channel(touch_chan); touch_chan = imx233_lradc_acquire_channel(src, TIMEOUT_NOBLOCK); if(touch_chan < 0) panicf("touchscreen: cannot get adc channel"); /* enable interrupt */ imx233_lradc_set_channel_irq_callback(touch_chan, &touch_channel_irq); imx233_icoll_enable_interrupt(INT_SRC_LRADC_CHx(touch_chan), true); imx233_lradc_enable_channel_irq(touch_chan, true); /* setup measurement: x- pull down and x+ pull up */ #ifdef SAMSUNG_YPZ5 pulldown_setup(pull_x, pull_y, pull_x, pull_y); #endif 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_source(touch_chan, false, src); imx233_lradc_setup_sampling(touch_chan, true, NR_SAMPLES - 1); imx233_lradc_clear_channel(touch_chan); /* use a delay */ imx233_lradc_setup_delay(touch_delay, 1 << touch_chan, 1 << touch_delay, NR_SAMPLES -1, DELAY); imx233_lradc_kick_delay(touch_delay); } static void enter_state(enum touch_state_t state) { touch_state = state; switch(state) { case TOUCH_STATE_WAIT: #ifdef SAMSUNG_YPZ5 pulldown_setup(false, false, false, false); #endif imx233_lradc_setup_touch(false, false, false, false, true); imx233_lradc_enable_touch_detect_irq(true); break; case TOUCH_STATE_MEASURE_X: kick_measure(true, false, false, LRADC_SRC_YPLUS); break; case TOUCH_STATE_MEASURE_Y: kick_measure(false, true, false, LRADC_SRC_XPLUS); break; case TOUCH_STATE_VERIFY: kick_measure(false, false, true, LRADC_SRC_YPLUS); break; } } static void process(void) { 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: touch_x = imx233_lradc_read_channel(touch_chan) / NR_SAMPLES; enter_state(TOUCH_STATE_MEASURE_Y); break; case TOUCH_STATE_MEASURE_Y: touch_y = imx233_lradc_read_channel(touch_chan) / NR_SAMPLES; enter_state(TOUCH_STATE_VERIFY); 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 imx233_touchscreen_init(void) { touch_delay = imx233_lradc_acquire_delay(TIMEOUT_NOBLOCK); if(touch_delay < 0) panicf("Cannot acquire channel and delays for touchscreen measurement"); imx233_touchscreen_enable(false); } void imx233_touchscreen_enable(bool enable) { if(enable) enter_state(TOUCH_STATE_WAIT); 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; }