diff options
author | Justin Waters <justin.waters@timesys.com> | 2012-03-21 13:28:20 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-03-21 13:28:20 -0400 |
commit | d0183eb2433e3332c2720637238b18b1fdff7946 (patch) | |
tree | 36be0be2c433789656750da0ca5991250fc7d3e7 /drivers/input/touchscreen | |
parent | 74fca6a42863ffacaf7ba6f1936a9f228950f657 (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/Kconfig | 40 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/imx_adc_ts.c | 114 | ||||
-rw-r--r-- | drivers/input/touchscreen/mxc_ts.c | 189 | ||||
-rw-r--r-- | drivers/input/touchscreen/mxs-ts.c | 462 | ||||
-rw-r--r-- | drivers/input/touchscreen/stmp3xxx_ts.c | 422 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2007.c | 55 |
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); |