summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
authorJustin Waters <justin.waters@timesys.com>2012-03-21 13:28:20 -0400
committerJustin Waters <justin.waters@timesys.com>2012-03-21 13:28:20 -0400
commitd0183eb2433e3332c2720637238b18b1fdff7946 (patch)
tree36be0be2c433789656750da0ca5991250fc7d3e7 /drivers/input/touchscreen
parent74fca6a42863ffacaf7ba6f1936a9f228950f657 (diff)
Add support for the i.MX28 EVK
This patch was originally put together in January 2011 by Roshni.
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig40
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/imx_adc_ts.c114
-rw-r--r--drivers/input/touchscreen/mxc_ts.c189
-rw-r--r--drivers/input/touchscreen/mxs-ts.c462
-rw-r--r--drivers/input/touchscreen/stmp3xxx_ts.c422
-rw-r--r--drivers/input/touchscreen/tsc2007.c55
7 files changed, 1251 insertions, 35 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 72e2712c7e2a..cd489febde11 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -225,6 +225,46 @@ config TOUCHSCREEN_HP7XX
To compile this driver as a module, choose M here: the
module will be called jornada720_ts.
+config TOUCHSCREEN_MXC
+ tristate "MXC touchscreen input driver"
+ depends on MXC_MC13783_ADC || MXC_MC13892_ADC
+ help
+ Say Y here if you have an MXC based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxc_ts.
+
+config TOUCHSCREEN_IMX_ADC
+ tristate "Freescale i.MX ADC touchscreen input driver"
+ depends on IMX_ADC
+ help
+ Say Y here if you have a Freescale i.MX based board with a
+ touchscreen interfaced to the processor's integrated ADC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx_adc_ts.
+
+config TOUCHSCREEN_STMP3XXX
+ tristate "STMP3XXX LRADC-based touchscreen"
+ depends on ARCH_STMP3XXX
+ select SERIO
+ help
+ Say Y here if you want to enable TMP3XXX LRADC-based touchscreen.
+ module will be called stmp3xxx_ts.
+
+config TOUCHSCREEN_MXS
+ tristate "MXS LRADC-based touchscreen"
+ depends on ARCH_MXS
+ select SERIO
+ help
+ Say Y here if you want to enable MXS LRADC-based touchscreen.
+ module will be called mxs-ts.
+
config TOUCHSCREEN_HTCPEN
tristate "HTC Shift X9500 touchscreen"
depends on ISA
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 3e1c5e0b952f..05589702fdaa 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -23,6 +23,10 @@ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
+obj-$(CONFIG_TOUCHSCREEN_MXC) += mxc_ts.o
+obj-$(CONFIG_TOUCHSCREEN_IMX_ADC) += imx_adc_ts.o
+obj-$(CONFIG_TOUCHSCREEN_STMP3XXX) += stmp3xxx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MXS) += mxs-ts.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
diff --git a/drivers/input/touchscreen/imx_adc_ts.c b/drivers/input/touchscreen/imx_adc_ts.c
new file mode 100644
index 000000000000..ec43a16213ae
--- /dev/null
+++ b/drivers/input/touchscreen/imx_adc_ts.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file imx_adc_ts.c
+ *
+ * @brief Driver for the Freescale Semiconductor i.MX ADC touchscreen.
+ *
+ * This touchscreen driver is designed as a standard input driver. It is a
+ * wrapper around the low level ADC driver. Much of the hardware configuration
+ * and touchscreen functionality is implemented in the low level ADC driver.
+ * During initialization, this driver creates a kernel thread. This thread
+ * then calls the ADC driver to obtain touchscreen values continously. These
+ * values are then passed to the input susbsystem.
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/imx_adc.h>
+
+#define IMX_ADC_TS_NAME "imx_adc_ts"
+
+static struct input_dev *imx_inputdev;
+static u32 input_ts_installed;
+
+static int ts_thread(void *arg)
+{
+ struct t_touch_screen ts_sample;
+ int wait = 0;
+ daemonize("imx_adc_ts");
+ while (input_ts_installed) {
+ try_to_freeze();
+
+ memset(&ts_sample, 0, sizeof(ts_sample));
+ if (0 != imx_adc_get_touch_sample(&ts_sample, !wait))
+ continue;
+
+ input_report_abs(imx_inputdev, ABS_X, ts_sample.x_position);
+ input_report_abs(imx_inputdev, ABS_Y, ts_sample.y_position);
+ input_report_abs(imx_inputdev, ABS_PRESSURE,
+ ts_sample.contact_resistance);
+ input_sync(imx_inputdev);
+ wait = ts_sample.contact_resistance;
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int __init imx_adc_ts_init(void)
+{
+ int retval;
+
+ if (!is_imx_adc_ready())
+ return -ENODEV;
+
+ imx_inputdev = input_allocate_device();
+ if (!imx_inputdev) {
+ pr_err("imx_ts_init: not enough memory for input device\n");
+ return -ENOMEM;
+ }
+
+ imx_inputdev->name = IMX_ADC_TS_NAME;
+ imx_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ imx_inputdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ imx_inputdev->absbit[0] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
+ retval = input_register_device(imx_inputdev);
+ if (retval < 0) {
+ input_free_device(imx_inputdev);
+ return retval;
+ }
+
+ input_ts_installed = 1;
+ kthread_run(ts_thread, NULL, "ts_thread");
+ pr_info("i.MX ADC input touchscreen loaded.\n");
+ return 0;
+}
+
+static void __exit imx_adc_ts_exit(void)
+{
+ input_ts_installed = 0;
+ input_unregister_device(imx_inputdev);
+
+ if (imx_inputdev) {
+ input_free_device(imx_inputdev);
+ imx_inputdev = NULL;
+ }
+}
+
+late_initcall(imx_adc_ts_init);
+module_exit(imx_adc_ts_exit);
+
+MODULE_DESCRIPTION("i.MX ADC input touchscreen driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mxc_ts.c b/drivers/input/touchscreen/mxc_ts.c
new file mode 100644
index 000000000000..0783188e8376
--- /dev/null
+++ b/drivers/input/touchscreen/mxc_ts.c
@@ -0,0 +1,189 @@
+/*
+ * Freescale touchscreen driver
+ *
+ * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*!
+ * @file mxc_ts.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC touchscreen with calibration support.
+ *
+ * The touchscreen driver is designed as a standard input driver which is a
+ * wrapper over low level PMIC driver. Most of the hardware configuration and
+ * touchscreen functionality is implemented in the low level PMIC driver. During
+ * initialization, this driver creates a kernel thread. This thread then calls
+ * PMIC driver to obtain touchscreen values continously. These values are then
+ * passed to the input susbsystem.
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_adc.h>
+#include <linux/kthread.h>
+
+#define MXC_TS_NAME "mxc_ts"
+
+static struct input_dev *mxc_inputdev;
+static struct task_struct *tstask;
+/**
+ * calibration array refers to
+ * (delta_x[0], delta_x[1], delta_x[2], delta_y[0], delta_y[1], delta_y[2], delta).
+ * Which generated by calibration service.
+ * In this driver when we got touch pointer (x', y') from PMIC ADC,
+ * we calculate the display pointer (x,y) by:
+ * x = (delta_x[0] * x' + delta_x[1] * y' + delta_x[2]) / delta;
+ * y = (delta_y[0] * x' + delta_y[1] * y' + delta_y[2]) / delta;
+ */
+static int calibration[7];
+module_param_array(calibration, int, NULL, S_IRUGO | S_IWUSR);
+
+static int ts_thread(void *arg)
+{
+ t_touch_screen ts_sample;
+ s32 wait = 0;
+
+ do {
+ int x, y;
+ static int last_x = -1, last_y = -1, last_press = -1;
+
+ memset(&ts_sample, 0, sizeof(t_touch_screen));
+ if (0 != pmic_adc_get_touch_sample(&ts_sample, !wait))
+ continue;
+ if (!(ts_sample.contact_resistance || wait))
+ continue;
+
+ if (ts_sample.x_position == 0 && ts_sample.y_position == 0 &&
+ ts_sample.contact_resistance == 0) {
+ x = last_x;
+ y = last_y;
+ } else if (calibration[6] == 0) {
+ x = ts_sample.x_position;
+ y = ts_sample.y_position;
+ } else {
+ x = calibration[0] * (int)ts_sample.x_position +
+ calibration[1] * (int)ts_sample.y_position +
+ calibration[2];
+ x /= calibration[6];
+ if (x < 0)
+ x = 0;
+ y = calibration[3] * (int)ts_sample.x_position +
+ calibration[4] * (int)ts_sample.y_position +
+ calibration[5];
+ y /= calibration[6];
+ if (y < 0)
+ y = 0;
+ }
+
+ if (x != last_x) {
+ input_report_abs(mxc_inputdev, ABS_X, x);
+ last_x = x;
+ }
+ if (y != last_y) {
+ input_report_abs(mxc_inputdev, ABS_Y, y);
+ last_y = y;
+ }
+
+ /* report pressure */
+ input_report_abs(mxc_inputdev, ABS_PRESSURE,
+ ts_sample.contact_resistance);
+#ifdef CONFIG_MXC_PMIC_MC13892
+ /* workaround for aplite ADC resistance large range value */
+ if (ts_sample.contact_resistance > 22)
+ ts_sample.contact_resistance = 1;
+ else
+ ts_sample.contact_resistance = 0;
+#endif
+ /* report the BTN_TOUCH */
+ if (ts_sample.contact_resistance != last_press)
+ input_event(mxc_inputdev, EV_KEY,
+ BTN_TOUCH, ts_sample.contact_resistance);
+
+ input_sync(mxc_inputdev);
+ last_press = ts_sample.contact_resistance;
+
+ wait = ts_sample.contact_resistance;
+ msleep(20);
+
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int __init mxc_ts_init(void)
+{
+ int retval;
+
+ if (!is_pmic_adc_ready())
+ return -ENODEV;
+
+ mxc_inputdev = input_allocate_device();
+ if (!mxc_inputdev) {
+ printk(KERN_ERR
+ "mxc_ts_init: not enough memory\n");
+ return -ENOMEM;
+ }
+
+ mxc_inputdev->name = MXC_TS_NAME;
+ mxc_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ mxc_inputdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ mxc_inputdev->absbit[0] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
+ retval = input_register_device(mxc_inputdev);
+ if (retval < 0) {
+ input_free_device(mxc_inputdev);
+ return retval;
+ }
+
+ tstask = kthread_run(ts_thread, NULL, "mxc_ts");
+ if (IS_ERR(tstask)) {
+ printk(KERN_ERR
+ "mxc_ts_init: failed to create kthread");
+ tstask = NULL;
+ return -1;
+ }
+ printk("mxc input touchscreen loaded\n");
+ return 0;
+}
+
+static void __exit mxc_ts_exit(void)
+{
+ if (tstask)
+ kthread_stop(tstask);
+
+ input_unregister_device(mxc_inputdev);
+
+ if (mxc_inputdev) {
+ input_free_device(mxc_inputdev);
+ mxc_inputdev = NULL;
+ }
+}
+
+late_initcall(mxc_ts_init);
+module_exit(mxc_ts_exit);
+
+MODULE_DESCRIPTION("MXC touchscreen driver with calibration");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mxs-ts.c b/drivers/input/touchscreen/mxs-ts.c
new file mode 100644
index 000000000000..a2ea102290a8
--- /dev/null
+++ b/drivers/input/touchscreen/mxs-ts.c
@@ -0,0 +1,462 @@
+/*
+ * Freesclae MXS Touchscreen driver
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <mach/lradc.h>
+#include <mach/hardware.h>
+#include <mach/device.h>
+#include <mach/regs-lradc.h>
+
+#define TOUCH_DEBOUNCE_TOLERANCE 100
+
+struct mxs_ts_info {
+ int touch_irq;
+ int device_irq;
+ unsigned int base;
+ u8 x_plus_chan;
+ u8 x_minus_chan;
+ u8 y_plus_chan;
+ u8 y_minus_chan;
+
+ unsigned int x_plus_val;
+ unsigned int x_minus_val;
+ unsigned int y_plus_val;
+ unsigned int y_minus_val;
+ unsigned int x_plus_mask;
+ unsigned int x_minus_mask;
+ unsigned int y_plus_mask;
+ unsigned int y_minus_mask;
+
+ struct input_dev *idev;
+ enum {
+ TS_STATE_DISABLED,
+ TS_STATE_TOUCH_DETECT,
+ TS_STATE_TOUCH_VERIFY,
+ TS_STATE_X_PLANE,
+ TS_STATE_Y_PLANE,
+ } state;
+ u16 x;
+ u16 y;
+ int sample_count;
+};
+
+static inline void enter_state_touch_detect(struct mxs_ts_info *info)
+{
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_minus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_minus_chan));
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ info->base + HW_LRADC_CTRL1_CLR);
+ /*
+ * turn off the yplus and yminus pullup and pulldown, and turn off touch
+ * detect (enables yminus, and xplus through a resistor.On a press,
+ * xplus is pulled down)
+ */
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_SET);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_TOUCH_DETECT;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_disabled(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_CLR);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_DISABLED;
+ info->sample_count = 0;
+}
+
+
+static inline void enter_state_x_plane(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(info->y_minus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_CLR);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+
+ info->state = TS_STATE_X_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_y_plane(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(info->x_minus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_CLR);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->state = TS_STATE_Y_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_touch_verify(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_SET);
+ info->state = TS_STATE_TOUCH_VERIFY;
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->sample_count = 0;
+}
+
+static void process_lradc(struct mxs_ts_info *info, u16 x, u16 y,
+ int pressure)
+{
+ switch (info->state) {
+ case TS_STATE_X_PLANE:
+ pr_debug("%s: x plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->x = x;
+ info->sample_count++;
+ } else {
+ if (abs(info->x - x) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 x_c = info->x * (info->sample_count - 1);
+ info->x = (x_c + x) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_y_plane(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_Y_PLANE:
+ pr_debug("%s: y plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->y = y;
+ info->sample_count++;
+ } else {
+ if (abs(info->y - y) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 y_c = info->y * (info->sample_count - 1);
+ info->y = (y_c + y) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_touch_verify(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_TOUCH_VERIFY:
+ pr_debug("%s: touch verify state, sample_count %d\n", __func__,
+ info->sample_count);
+ pr_debug("%s: x %d, y %d\n", __func__, info->x, info->y);
+ input_report_abs(info->idev, ABS_X, info->x);
+ input_report_abs(info->idev, ABS_Y, info->y);
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ input_sync(info->idev);
+ /* fall through */
+ case TS_STATE_TOUCH_DETECT:
+ pr_debug("%s: touch detect state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (pressure) {
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ enter_state_x_plane(info);
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ } else
+ enter_state_touch_detect(info);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown touchscreen state %d\n", __func__,
+ info->state);
+ }
+}
+
+static irqreturn_t ts_handler(int irq, void *dev_id)
+{
+ struct mxs_ts_info *info = dev_id;
+ u16 x_plus, y_plus;
+ int pressure = 0;
+
+ if (irq == info->touch_irq)
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ info->base + HW_LRADC_CTRL1_CLR);
+ else if (irq == info->device_irq)
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+
+ /* get x, y values */
+ x_plus = __raw_readl(info->base + HW_LRADC_CHn(info->x_plus_chan)) &
+ BM_LRADC_CHn_VALUE;
+ y_plus = __raw_readl(info->base + HW_LRADC_CHn(info->y_plus_chan)) &
+ BM_LRADC_CHn_VALUE;
+
+ /* pressed? */
+ if (__raw_readl(info->base + HW_LRADC_STATUS) &
+ BM_LRADC_STATUS_TOUCH_DETECT_RAW)
+ pressure = 1;
+
+ pr_debug("%s: irq %d, x_plus %d, y_plus %d, pressure %d\n",
+ __func__, irq, x_plus, y_plus, pressure);
+
+ process_lradc(info, x_plus, y_plus, pressure);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mxs_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *idev;
+ struct mxs_ts_info *info;
+ int ret = 0;
+ struct resource *res;
+ struct mxs_touchscreen_plat_data *plat_data;
+
+ plat_data = (struct mxs_touchscreen_plat_data *)pdev->dev.platform_data;
+ if (plat_data == NULL)
+ return -ENODEV;
+
+ idev = input_allocate_device();
+ if (idev == NULL)
+ return -ENOMEM;
+
+ info = kzalloc(sizeof(struct mxs_ts_info), GFP_KERNEL);
+ if (info == NULL) {
+ ret = -ENOMEM;
+ goto out_nomem_info;
+ }
+
+ idev->name = "MXS touchscreen";
+ idev->evbit[0] = BIT(EV_ABS);
+ input_set_abs_params(idev, ABS_X, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret)
+ goto out_nomem;
+
+ info->idev = idev;
+ info->x_plus_chan = plat_data->x_plus_chan;
+ info->x_minus_chan = plat_data->x_minus_chan;
+ info->y_plus_chan = plat_data->y_plus_chan;
+ info->y_minus_chan = plat_data->y_minus_chan;
+ info->x_plus_val = plat_data->x_plus_val;
+ info->x_minus_val = plat_data->x_minus_val;
+ info->y_plus_val = plat_data->y_plus_val;
+ info->y_minus_val = plat_data->y_minus_val;
+ info->x_plus_mask = plat_data->x_plus_mask;
+ info->x_minus_mask = plat_data->x_minus_mask;
+ info->y_plus_mask = plat_data->y_plus_mask;
+ info->y_minus_mask = plat_data->y_minus_mask;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get MEM resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->base = (unsigned int)IO_ADDRESS(res->start);
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->touch_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->device_irq = res->start;
+
+ ret = request_irq(info->touch_irq, ts_handler, IRQF_DISABLED,
+ "mxs_ts_touch", info);
+ if (ret)
+ goto out_nodev;
+
+ ret = request_irq(info->device_irq, ts_handler, IRQF_DISABLED,
+ "mxs_ts_dev", info);
+ if (ret) {
+ free_irq(info->touch_irq, info);
+ goto out_nodev;
+ }
+ enter_state_touch_detect(info);
+
+ hw_lradc_use_channel(info->x_plus_chan);
+ hw_lradc_use_channel(info->x_minus_chan);
+ hw_lradc_use_channel(info->y_plus_chan);
+ hw_lradc_use_channel(info->y_minus_chan);
+ hw_lradc_configure_channel(info->x_plus_chan, 0, 0, 0);
+ hw_lradc_configure_channel(info->x_minus_chan, 0, 0, 0);
+ hw_lradc_configure_channel(info->y_plus_chan, 0, 0, 0);
+ hw_lradc_configure_channel(info->y_minus_chan, 0, 0, 0);
+
+ /* Clear the accumulator & NUM_SAMPLES for the channels */
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_minus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_minus_chan));
+
+ hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_TOUCHSCREEN,
+ 0x3c, 0, 0, 8);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ info->base + HW_LRADC_CTRL1_CLR);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_SET);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ info->base + HW_LRADC_CTRL1_SET);
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+ goto out;
+
+out_nodev:
+ input_free_device(idev);
+out_nomem:
+ kfree(info);
+out_nomem_info:
+ kfree(idev);
+out:
+ return ret;
+}
+
+static int __devexit mxs_ts_remove(struct platform_device *pdev)
+{
+ struct mxs_ts_info *info = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ hw_lradc_unuse_channel(info->x_plus_chan);
+ hw_lradc_unuse_channel(info->x_minus_chan);
+ hw_lradc_unuse_channel(info->y_plus_chan);
+ hw_lradc_unuse_channel(info->y_minus_chan);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ info->base + HW_LRADC_CTRL1_CLR);
+
+ free_irq(info->device_irq, info);
+ free_irq(info->touch_irq, info);
+ input_free_device(info->idev);
+
+ enter_state_disabled(info);
+ kfree(info->idev);
+ kfree(info);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxs_ts_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct mxs_ts_info *info = platform_get_drvdata(pdev);
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_unuse_channel(info->x_plus_chan);
+ hw_lradc_unuse_channel(info->x_minus_chan);
+ hw_lradc_unuse_channel(info->y_plus_chan);
+ hw_lradc_unuse_channel(info->y_minus_chan);
+ }
+ return 0;
+}
+
+static int mxs_ts_resume(struct platform_device *pdev)
+{
+ struct mxs_ts_info *info = platform_get_drvdata(pdev);
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_use_channel(info->x_plus_chan);
+ hw_lradc_use_channel(info->x_minus_chan);
+ hw_lradc_use_channel(info->y_plus_chan);
+ hw_lradc_use_channel(info->y_minus_chan);
+ }
+ return 0;
+}
+#endif
+
+static struct platform_driver mxs_ts_driver = {
+ .probe = mxs_ts_probe,
+ .remove = __devexit_p(mxs_ts_remove),
+#ifdef CONFIG_PM
+ .suspend = mxs_ts_suspend,
+ .resume = mxs_ts_resume,
+#endif
+ .driver = {
+ .name = "mxs-ts",
+ },
+};
+
+static int __init mxs_ts_init(void)
+{
+ return platform_driver_register(&mxs_ts_driver);
+}
+
+static void __exit mxs_ts_exit(void)
+{
+ platform_driver_unregister(&mxs_ts_driver);
+}
+
+module_init(mxs_ts_init);
+module_exit(mxs_ts_exit);
diff --git a/drivers/input/touchscreen/stmp3xxx_ts.c b/drivers/input/touchscreen/stmp3xxx_ts.c
new file mode 100644
index 000000000000..4e6ab20ea780
--- /dev/null
+++ b/drivers/input/touchscreen/stmp3xxx_ts.c
@@ -0,0 +1,422 @@
+/*
+ * Freesclae STMP37XX/STMP378X Touchscreen driver
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/* #define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <mach/lradc.h>
+#include <mach/hardware.h>
+#include <mach/platform.h>
+#include <mach/regs-lradc.h>
+
+#define TOUCH_DEBOUNCE_TOLERANCE 100
+
+struct stmp3xxx_ts_info {
+ int touch_irq;
+ int device_irq;
+ struct input_dev *idev;
+ enum {
+ TS_STATE_DISABLED,
+ TS_STATE_TOUCH_DETECT,
+ TS_STATE_TOUCH_VERIFY,
+ TS_STATE_X_PLANE,
+ TS_STATE_Y_PLANE,
+ } state;
+ u16 x;
+ u16 y;
+ int sample_count;
+};
+
+static inline void enter_state_touch_detect(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(2));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(3));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(4));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(5));
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ /*
+ * turn off the yplus and yminus pullup and pulldown, and turn off touch
+ * detect (enables yminus, and xplus through a resistor.On a press,
+ * xplus is pulled down)
+ */
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_TOUCH_DETECT;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_disabled(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_DISABLED;
+ info->sample_count = 0;
+}
+
+
+static inline void enter_state_x_plane(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+
+ info->state = TS_STATE_X_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_y_plane(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->state = TS_STATE_Y_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_touch_verify(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+
+ info->state = TS_STATE_TOUCH_VERIFY;
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->sample_count = 0;
+}
+
+static void process_lradc(struct stmp3xxx_ts_info *info, u16 x, u16 y,
+ int pressure)
+{
+ switch (info->state) {
+ case TS_STATE_X_PLANE:
+ pr_debug("%s: x plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->x = x;
+ info->sample_count++;
+ } else {
+ if (abs(info->x - x) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 x_c = info->x * (info->sample_count - 1);
+ info->x = (x_c + x) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_y_plane(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_Y_PLANE:
+ pr_debug("%s: y plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->y = y;
+ info->sample_count++;
+ } else {
+ if (abs(info->y - y) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 y_c = info->y * (info->sample_count - 1);
+ info->y = (y_c + y) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_touch_verify(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_TOUCH_VERIFY:
+ pr_debug("%s: touch verify state, sample_count %d\n", __func__,
+ info->sample_count);
+ pr_debug("%s: x %d, y %d\n", __func__, info->x, info->y);
+ input_report_abs(info->idev, ABS_X, info->x);
+ input_report_abs(info->idev, ABS_Y, info->y);
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ input_sync(info->idev);
+ /* fall through */
+ case TS_STATE_TOUCH_DETECT:
+ pr_debug("%s: touch detect state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (pressure) {
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ enter_state_x_plane(info);
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ } else
+ enter_state_touch_detect(info);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown touchscreen state %d\n", __func__,
+ info->state);
+ }
+}
+
+static irqreturn_t ts_handler(int irq, void *dev_id)
+{
+ struct stmp3xxx_ts_info *info = dev_id;
+ u16 x_plus, y_plus;
+ int pressure = 0;
+
+ if (irq == info->touch_irq)
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ else if (irq == info->device_irq)
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+
+ /* get x, y values */
+ x_plus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(LRADC_TOUCH_X_PLUS)) & BM_LRADC_CHn_VALUE;
+ y_plus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(LRADC_TOUCH_Y_PLUS)) & BM_LRADC_CHn_VALUE;
+
+ /* pressed? */
+ if (__raw_readl(REGS_LRADC_BASE + HW_LRADC_STATUS) & BM_LRADC_STATUS_TOUCH_DETECT_RAW)
+ pressure = 1;
+
+ pr_debug("%s: irq %d, x_plus %d, y_plus %d, pressure %d\n",
+ __func__, irq, x_plus, y_plus, pressure);
+
+ process_lradc(info, x_plus, y_plus, pressure);
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *idev;
+ struct stmp3xxx_ts_info *info;
+ int ret = 0;
+ struct resource *res;
+
+ idev = input_allocate_device();
+ info = kzalloc(sizeof(struct stmp3xxx_ts_info), GFP_KERNEL);
+ if (idev == NULL || info == NULL) {
+ ret = -ENOMEM;
+ goto out_nomem;
+ }
+
+ idev->name = "STMP3XXX touchscreen";
+ idev->evbit[0] = BIT(EV_ABS);
+ input_set_abs_params(idev, ABS_X, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret)
+ goto out_nomem;
+
+ info->idev = idev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->touch_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->device_irq = res->start;
+
+ ret = request_irq(info->touch_irq, ts_handler, IRQF_DISABLED,
+ "stmp3xxx_ts_touch", info);
+ if (ret)
+ goto out_nodev;
+
+ ret = request_irq(info->device_irq, ts_handler, IRQF_DISABLED,
+ "stmp3xxx_ts_dev", info);
+ if (ret) {
+ free_irq(info->touch_irq, info);
+ goto out_nodev;
+ }
+ enter_state_touch_detect(info);
+
+ hw_lradc_use_channel(LRADC_CH2);
+ hw_lradc_use_channel(LRADC_CH3);
+ hw_lradc_use_channel(LRADC_CH5);
+ hw_lradc_configure_channel(LRADC_CH2, 0, 0, 0);
+ hw_lradc_configure_channel(LRADC_CH3, 0, 0, 0);
+ hw_lradc_configure_channel(LRADC_CH5, 0, 0, 0);
+
+ /* Clear the accumulator & NUM_SAMPLES for the channels */
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH2));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH3));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH5));
+
+ hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_TOUCHSCREEN,
+ 0x3c, 0, 0, 8);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_SET);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_SET);
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+ goto out;
+
+out_nodev:
+ input_free_device(idev);
+out_nomem:
+ kfree(idev);
+ kfree(info);
+out:
+ return ret;
+}
+
+static int stmp3xxx_ts_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_ts_info *info = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ hw_lradc_unuse_channel(LRADC_CH2);
+ hw_lradc_unuse_channel(LRADC_CH3);
+ hw_lradc_unuse_channel(LRADC_CH5);
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+
+ free_irq(info->device_irq, info);
+ free_irq(info->touch_irq, info);
+ input_free_device(info->idev);
+
+ enter_state_disabled(info);
+ kfree(info->idev);
+ kfree(info);
+ return 0;
+}
+
+static int stmp3xxx_ts_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+#ifdef CONFIG_PM
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_unuse_channel(LRADC_CH2);
+ hw_lradc_unuse_channel(LRADC_CH3);
+ hw_lradc_unuse_channel(LRADC_CH5);
+ }
+#endif
+ return 0;
+}
+
+static int stmp3xxx_ts_resume(struct platform_device *pdev)
+{
+#ifdef CONFIG_PM
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_use_channel(LRADC_CH2);
+ hw_lradc_use_channel(LRADC_CH3);
+ hw_lradc_use_channel(LRADC_CH5);
+ }
+#endif
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_ts_driver = {
+ .probe = stmp3xxx_ts_probe,
+ .remove = stmp3xxx_ts_remove,
+ .suspend = stmp3xxx_ts_suspend,
+ .resume = stmp3xxx_ts_resume,
+ .driver = {
+ .name = "stmp3xxx_ts",
+ },
+};
+
+static int __init stmp3xxx_ts_init(void)
+{
+ return platform_driver_register(&stmp3xxx_ts_driver);
+}
+
+static void __exit stmp3xxx_ts_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_ts_driver);
+}
+
+module_init(stmp3xxx_ts_init);
+module_exit(stmp3xxx_ts_exit);
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 880f58c6a7c4..fd5a503ffb93 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -21,15 +21,13 @@
*/
#include <linux/module.h>
-#include <linux/hrtimer.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/tsc2007.h>
-#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */
-#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */
+#define TS_POLL_PERIOD msecs_to_jiffies(1) /* ms delay between samples */
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4)
@@ -70,13 +68,11 @@ struct ts_event {
struct tsc2007 {
struct input_dev *input;
char phys[32];
- struct hrtimer timer;
+ struct delayed_work work;
struct ts_event tc;
struct i2c_client *client;
- spinlock_t lock;
-
u16 model;
u16 x_plate_ohms;
@@ -142,8 +138,7 @@ static void tsc2007_send_event(void *tsc)
if (rt > MAX_12BIT) {
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
return;
}
@@ -153,7 +148,7 @@ static void tsc2007_send_event(void *tsc)
* in some cases may not even settle at the expected value.
*
* The only safe way to check for the pen up condition is in the
- * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
+ * work function by reading the pen signal state (it's a GPIO and IRQ).
*/
if (rt) {
struct input_dev *input = ts->input;
@@ -175,8 +170,7 @@ static void tsc2007_send_event(void *tsc)
x, y, rt);
}
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
}
static int tsc2007_read_values(struct tsc2007 *tsc)
@@ -197,13 +191,10 @@ static int tsc2007_read_values(struct tsc2007 *tsc)
return 0;
}
-static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
+static void tsc2007_work(struct work_struct *work)
{
- struct tsc2007 *ts = container_of(handle, struct tsc2007, timer);
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
-
+ struct tsc2007 *ts =
+ container_of(to_delayed_work(work), struct tsc2007, work);
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
struct input_dev *input = ts->input;
@@ -222,30 +213,20 @@ static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
tsc2007_read_values(ts);
tsc2007_send_event(ts);
}
-
- spin_unlock_irqrestore(&ts->lock, flags);
-
- return HRTIMER_NORESTART;
}
static irqreturn_t tsc2007_irq(int irq, void *handle)
{
struct tsc2007 *ts = handle;
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
if (likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq);
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work, 0);
}
if (ts->clear_penirq)
ts->clear_penirq();
- spin_unlock_irqrestore(&ts->lock, flags);
-
return IRQ_HANDLED;
}
@@ -278,11 +259,6 @@ static int tsc2007_probe(struct i2c_client *client,
ts->input = input_dev;
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- ts->timer.function = tsc2007_timer;
-
- spin_lock_init(&ts->lock);
-
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->get_pendown_state = pdata->get_pendown_state;
@@ -290,6 +266,11 @@ static int tsc2007_probe(struct i2c_client *client,
pdata->init_platform_hw();
+ if (tsc2007_xfer(ts, PWRDOWN) < 0) {
+ err = -ENODEV;
+ goto err_no_dev;
+ }
+
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -308,6 +289,8 @@ static int tsc2007_probe(struct i2c_client *client,
ts->irq = client->irq;
+ INIT_DELAYED_WORK(&ts->work, tsc2007_work);
+
err = request_irq(ts->irq, tsc2007_irq, 0,
client->dev.driver->name, ts);
if (err < 0) {
@@ -325,9 +308,10 @@ static int tsc2007_probe(struct i2c_client *client,
err_free_irq:
free_irq(ts->irq, ts);
- hrtimer_cancel(&ts->timer);
err_free_mem:
input_free_device(input_dev);
+ err_no_dev:
+ pdata->exit_platform_hw();
kfree(ts);
return err;
}
@@ -337,11 +321,12 @@ static int tsc2007_remove(struct i2c_client *client)
struct tsc2007 *ts = i2c_get_clientdata(client);
struct tsc2007_platform_data *pdata;
+ cancel_delayed_work_sync(&ts->work);
+
pdata = client->dev.platform_data;
pdata->exit_platform_hw();
free_irq(ts->irq, ts);
- hrtimer_cancel(&ts->timer);
input_unregister_device(ts->input);
kfree(ts);