summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig59
-rw-r--r--drivers/input/touchscreen/Makefile6
-rw-r--r--drivers/input/touchscreen/da9052_tsi.c1449
-rw-r--r--drivers/input/touchscreen/da9052_tsi_calibrate.c107
-rw-r--r--drivers/input/touchscreen/da9052_tsi_filter.c489
-rw-r--r--drivers/input/touchscreen/imx_adc_ts.c114
-rw-r--r--drivers/input/touchscreen/max11801_ts.c351
-rw-r--r--drivers/input/touchscreen/mxc_ts.c189
-rw-r--r--drivers/input/touchscreen/mxs-ts.c464
-rw-r--r--drivers/input/touchscreen/tsc2007.c9
10 files changed, 3237 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3b9d5e2105d7..2f43f6fb2360 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -268,6 +268,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
@@ -603,4 +643,23 @@ config TOUCHSCREEN_TPS6507X
To compile this driver as a module, choose M here: the
module will be called tps6507x_ts.
+config TOUCHSCREEN_DA9052
+ tristate "Dialog DA9052 TSI"
+ depends on PMIC_DA9052
+ help
+ Say y here to support the touchscreen found on
+ Dialog Semiconductor DA9052 PMIC
+
+config TOUCHSCREEN_MAX11801
+ tristate "MAXIM MAX11801 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a MAX11801 touchscreen
+ controller
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max11801_ts
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 497964a7a214..5a46b99a6907 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -27,6 +27,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_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
@@ -47,3 +51,5 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o da9052_tsi_filter.o da9052_tsi_calibrate.o
+obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c
new file mode 100644
index 000000000000..a0c7aa9a1de9
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi.c
@@ -0,0 +1,1449 @@
+/*
+ * da9052_tsi.c -- TSI driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/tsi_cfg.h>
+#include <linux/mfd/da9052/tsi.h>
+#include <linux/mfd/da9052/gpio.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define WAIT_FOR_PEN_DOWN 0
+#define WAIT_FOR_SAMPLING 1
+#define SAMPLING_ACTIVE 2
+
+static ssize_t __init da9052_tsi_create_input_dev(struct input_dev **ip_dev,
+ u8 n);
+static ssize_t read_da9052_reg(struct da9052 *da9052, u8 reg_addr);
+static ssize_t write_da9052_reg(struct da9052 *da9052, u8 reg_addr, u8 data);
+
+static void da9052_tsi_reg_pendwn_event(struct da9052_ts_priv *priv);
+static void da9052_tsi_reg_datardy_event(struct da9052_ts_priv *priv);
+static ssize_t da9052_tsi_config_delay(struct da9052_ts_priv *priv,
+ enum TSI_DELAY delay);
+static ssize_t da9052_tsi_config_measure_seq(struct da9052_ts_priv *priv,
+ enum TSI_MEASURE_SEQ seq);
+static ssize_t da9052_tsi_config_state(struct da9052_ts_priv *ts,
+ enum TSI_STATE state);
+static ssize_t da9052_tsi_set_sampling_mode(struct da9052_ts_priv *priv,
+ u8 interval);
+static ssize_t da9052_tsi_config_skip_slots(struct da9052_ts_priv *priv,
+ enum TSI_SLOT_SKIP skip);
+static ssize_t da9052_tsi_config_pen_detect(struct da9052_ts_priv *priv,
+ u8 flag);
+static ssize_t da9052_tsi_disable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq);
+static ssize_t da9052_tsi_enable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq);
+static ssize_t da9052_tsi_config_manual_mode(struct da9052_ts_priv *priv,
+ u8 coordinate);
+static ssize_t da9052_tsi_config_auto_mode(struct da9052_ts_priv *priv,
+ u8 state);
+static ssize_t da9052_tsi_config_gpio(struct da9052_ts_priv *priv);
+static ssize_t da9052_tsi_config_power_supply(struct da9052_ts_priv *priv,
+ u8 state);
+static struct da9052_tsi_info *get_tsi_drvdata(void);
+static void da9052_tsi_penup_event(struct da9052_ts_priv *priv);
+static s32 da9052_tsi_get_rawdata(struct da9052_tsi_reg *buf, u8 cnt);
+static ssize_t da9052_tsi_reg_proc_thread(void *ptr);
+static ssize_t da9052_tsi_resume(struct platform_device *dev);
+static ssize_t da9052_tsi_suspend(struct platform_device *dev,
+ pm_message_t state);
+/* void tsi_reg_proc_work(struct work_struct *work); */
+
+struct da9052_tsi tsi_reg;
+struct da9052_tsi_info gda9052_tsi_info;
+
+static ssize_t write_da9052_reg(struct da9052 *da9052, u8 reg_addr, u8 data)
+{
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = reg_addr;
+ ssc_msg.data = data;
+ ret = da9052->write(da9052, &ssc_msg);
+ if (ret) {
+ DA9052_DEBUG("%s: ", __func__);
+ DA9052_DEBUG("da9052_ssc_write Failed %d\n", ret);
+ }
+
+ return ret;
+}
+
+static ssize_t read_da9052_reg(struct da9052 *da9052, u8 reg_addr)
+{
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = reg_addr;
+ ssc_msg.data = 0;
+ ret = da9052->read(da9052, &ssc_msg);
+ if (ret) {
+ DA9052_DEBUG("%s: ",__FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_read Failed => %d\n" ,ret);
+ return -ret;
+ }
+ return ssc_msg.data;
+}
+
+static struct da9052_tsi_info *get_tsi_drvdata(void)
+{
+ return &gda9052_tsi_info;
+}
+
+static ssize_t da9052_tsi_config_measure_seq(struct da9052_ts_priv *priv,
+ enum TSI_MEASURE_SEQ seq)
+{
+ ssize_t ret = 0;
+ u8 data = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (seq > 1)
+ return -EINVAL;
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = (u8)ret;
+
+ if (seq == XYZP_MODE)
+ data = enable_xyzp_mode(data);
+ else if (seq == XP_MODE)
+ data = enable_xp_mode(data);
+ else {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid Value passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_set_sampling_mode(struct da9052_ts_priv *priv,
+ u8 mode)
+{
+ u8 data = 0;
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_ADCCONT_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ data = (u8)ret;
+
+ if (mode == ECONOMY_MODE)
+ data = adc_mode_economy_mode(data);
+ else if (mode == FAST_MODE)
+ data = adc_mode_fast_mode(data);
+ else {
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid interval passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_ADCCONT_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ switch (mode) {
+ case ECONOMY_MODE:
+ priv->tsi_reg_data_poll_interval =
+ TSI_ECO_MODE_REG_DATA_PROCESSING_INTERVAL;
+ priv->tsi_raw_data_poll_interval =
+ TSI_ECO_MODE_RAW_DATA_PROCESSING_INTERVAL;
+ break;
+ case FAST_MODE:
+ priv->tsi_reg_data_poll_interval =
+ TSI_FAST_MODE_REG_DATA_PROCESSING_INTERVAL;
+ priv->tsi_raw_data_poll_interval =
+ TSI_FAST_MODE_RAW_DATA_PROCESSING_INTERVAL;
+ break;
+ default:
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid interval passed \n" );
+ return -EINVAL;
+ }
+
+ ts->tsi_penup_count =
+ (u32)priv->tsi_pdata->pen_up_interval /
+ priv->tsi_reg_data_poll_interval;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_delay(struct da9052_ts_priv *priv,
+ enum TSI_DELAY delay)
+{
+ ssize_t ret = 0;
+ u8 data = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (delay > priv->tsi_pdata->max_tsi_delay) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" invalid value for tsi delay!!!\n" );
+ return -EINVAL;
+ }
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if(ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = clear_bits((u8)ret, DA9052_TSICONTA_TSIDELAY);
+
+ data = set_bits(data, (delay << priv->tsi_pdata->tsi_delay_bit_shift));
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+
+ return 0;
+}
+
+ssize_t da9052_tsi_config_skip_slots(struct da9052_ts_priv *priv,
+ enum TSI_SLOT_SKIP skip)
+{
+ ssize_t ret = 0;
+ u8 data = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (skip > priv->tsi_pdata->max_tsi_skip_slot) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" invalid value for tsi skip slots!!!\n" );
+ return -EINVAL;
+ }
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = clear_bits((u8)ret, DA9052_TSICONTA_TSISKIP);
+
+ data = set_bits(data, (skip << priv->tsi_pdata->tsi_skip_bit_shift));
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_config_skip_slots:");
+ DA9052_DEBUG(" write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_state(struct da9052_ts_priv *priv,
+ enum TSI_STATE state)
+{
+ s32 ret;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (ts->tsi_conf.state == state)
+ return 0;
+
+ switch (state) {
+ case TSI_AUTO_MODE:
+ ts->tsi_zero_data_cnt = 0;
+ priv->early_data_flag = TRUE;
+ priv->debounce_over = FALSE;
+ priv->win_reference_valid = FALSE;
+
+ clean_tsi_fifos(priv);
+
+ ret = da9052_tsi_config_auto_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_manual_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_power_supply(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_enable_irq(priv, TSI_PEN_DWN);
+ if (ret)
+ return ret;
+ ts->tsi_conf.tsi_pendown_irq_mask = RESET;
+
+ ret = da9052_tsi_disable_irq(priv, TSI_DATA_RDY);
+ if (ret)
+ return ret;
+ ts->tsi_conf.tsi_ready_irq_mask = SET;
+
+ da9052_tsi_reg_pendwn_event(priv);
+ da9052_tsi_reg_datardy_event(priv);
+
+ ret = da9052_tsi_config_pen_detect(priv, ENABLE);
+ if (ret)
+ return ret;
+ break;
+
+ case TSI_IDLE:
+ ts->pen_dwn_event = RESET;
+
+ ret = da9052_tsi_config_pen_detect(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_auto_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_manual_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_power_supply(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ if (ts->pd_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->pd_nb);
+ ts->pd_reg_status = RESET;
+ }
+ break;
+
+ default:
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Invalid state passed");
+ return -EINVAL;
+ }
+
+ ts->tsi_conf.state = state;
+
+ return 0;
+}
+
+static void da9052_tsi_reg_pendwn_event(struct da9052_ts_priv *priv)
+{
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (ts->pd_reg_status) {
+ DA9052_DEBUG("%s: Pen down ",__FUNCTION__);
+ DA9052_DEBUG("Registeration is already done \n");
+ return;
+ }
+
+ priv->pd_nb.eve_type = PEN_DOWN_EVE;
+ priv->pd_nb.call_back = &da9052_tsi_pen_down_handler;
+
+ ret = priv->da9052->register_event_notifier(priv->da9052, &priv->pd_nb);
+ if (ret) {
+ DA9052_DEBUG("%s: EH Registeration",__FUNCTION__);
+ DA9052_DEBUG(" Failed: ret = %d\n",ret );
+ ts->pd_reg_status = RESET;
+ } else
+ ts->pd_reg_status = SET;
+
+ priv->os_data_cnt = 0;
+ priv->raw_data_cnt = 0;
+
+ return;
+}
+
+static void da9052_tsi_reg_datardy_event(struct da9052_ts_priv *priv)
+{
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if(ts->datardy_reg_status)
+ {
+ DA9052_DEBUG("%s: Data Ready ",__FUNCTION__);
+ DA9052_DEBUG("Registeration is already done \n");
+ return;
+ }
+
+ priv->datardy_nb.eve_type = TSI_READY_EVE;
+ priv->datardy_nb.call_back = &da9052_tsi_data_ready_handler;
+
+ ret = priv->da9052->register_event_notifier(priv->da9052,
+ &priv->datardy_nb);
+
+ if(ret)
+ {
+ DA9052_DEBUG("%s: EH Registeration",__FUNCTION__);
+ DA9052_DEBUG(" Failed: ret = %d\n",ret );
+ ts->datardy_reg_status = RESET;
+ } else
+ ts->datardy_reg_status = SET;
+
+ return;
+}
+
+static ssize_t __init da9052_tsi_create_input_dev(struct input_dev **ip_dev,
+ u8 n)
+{
+ u8 i;
+ s32 ret;
+ struct input_dev *dev = NULL;
+
+ if (!n)
+ return -EINVAL;
+
+ for (i = 0; i < n; i++) {
+ dev = input_allocate_device();
+ if (!dev) {
+ DA9052_DEBUG(KERN_ERR "%s:%s():memory allocation for \
+ inputdevice failed\n", __FILE__,
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ ip_dev[i] = dev;
+ switch (i) {
+ case TSI_INPUT_DEVICE_OFF:
+ dev->name = DA9052_TSI_INPUT_DEV;
+ dev->phys = "input(tsi)";
+ break;
+ default:
+ break;
+ }
+ }
+ dev->id.vendor = DA9052_VENDOR_ID;
+ dev->id.product = DA9052_PRODUCT_ID;
+ dev->id.bustype = BUS_RS232;
+ dev->id.version = TSI_VERSION;
+ dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ dev->evbit[0] = (BIT_MASK(EV_SYN) |
+ BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_ABS));
+
+ input_set_abs_params(dev, ABS_X, 0, DA9052_DISPLAY_X_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, DA9052_DISPLAY_Y_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, DA9052_TOUCH_PRESSURE_MAX,
+ 0, 0);
+
+ ret = input_register_device(dev);
+ if (ret) {
+ DA9052_DEBUG(KERN_ERR "%s: Could ", __FUNCTION__);
+ DA9052_DEBUG("not register input device(touchscreen)!\n");
+ ret = -EIO;
+ goto fail;
+ }
+ return 0;
+
+fail:
+ for (; i-- != 0; )
+ input_free_device(ip_dev[i]);
+ return -EINVAL;
+}
+
+static ssize_t __init da9052_tsi_init_drv(struct da9052_ts_priv *priv)
+{
+ u8 cnt = 0;
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if ((DA9052_GPIO_PIN_3 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_4 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_5 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_6 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_7 != DA9052_GPIO_CONFIG_TSI)) {
+ printk(KERN_ERR"DA9052_TSI: Configure DA9052 GPIO ");
+ printk(KERN_ERR"pins for TSI\n");
+ return -EINVAL;
+ }
+
+ ret = da9052_tsi_config_gpio(priv);
+
+ ret = da9052_tsi_config_state(priv, TSI_IDLE);
+ ts->tsi_conf.state = TSI_IDLE;
+
+ da9052_tsi_config_measure_seq(priv, TSI_MODE_VALUE);
+
+ da9052_tsi_config_skip_slots(priv, TSI_SLOT_SKIP_VALUE);
+
+ da9052_tsi_config_delay(priv, TSI_DELAY_VALUE);
+
+ da9052_tsi_set_sampling_mode(priv, DEFAULT_TSI_SAMPLING_MODE);
+
+ ts->tsi_calib = get_calib_config();
+
+ ret = da9052_tsi_create_input_dev(ts->input_devs, NUM_INPUT_DEVS);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s: ", __FUNCTION__);
+ DA9052_DEBUG("da9052_tsi_create_input_dev Failed \n" );
+ return ret;
+ }
+
+ da9052_init_tsi_fifos(priv);
+
+ init_completion(&priv->tsi_reg_proc_thread.notifier);
+ priv->tsi_reg_proc_thread.state = ACTIVE;
+ priv->tsi_reg_proc_thread.pid =
+ kernel_thread(da9052_tsi_reg_proc_thread,
+ priv, CLONE_KERNEL | SIGCHLD);
+
+ init_completion(&priv->tsi_raw_proc_thread.notifier);
+ priv->tsi_raw_proc_thread.state = ACTIVE;
+ priv->tsi_raw_proc_thread.pid =
+ kernel_thread(da9052_tsi_raw_proc_thread,
+ priv, CLONE_KERNEL | SIGCHLD);
+
+ ret = da9052_tsi_config_state(priv, DEFAULT_TSI_STATE);
+ if (ret) {
+ for (cnt = 0; cnt < NUM_INPUT_DEVS; cnt++) {
+ if (ts->input_devs[cnt] != NULL)
+ input_free_device(ts->input_devs[cnt]);
+ }
+ }
+
+ return 0;
+}
+
+u32 da9052_tsi_get_input_dev(u8 off)
+{
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (off > NUM_INPUT_DEVS-1)
+ return -EINVAL;
+
+ return (u32)ts->input_devs[off];
+}
+
+static ssize_t da9052_tsi_config_pen_detect(struct da9052_ts_priv *priv,
+ u8 flag)
+{
+ u8 data;
+ u32 ret;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("%s:", __FUNCTION__);
+ DA9052_DEBUG(" read_da9052_reg Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ if (flag == ENABLE)
+ data = set_bits((u8)ret, DA9052_TSICONTA_PENDETEN);
+ else if (flag == DISABLE)
+ data = clear_bits((u8)ret, DA9052_TSICONTA_PENDETEN);
+ else {
+ DA9052_DEBUG("%s:", __FUNCTION__);
+ DA9052_DEBUG(" Invalid flag passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret < 0) {
+ DA9052_DEBUG("%s:", __FUNCTION__);
+ DA9052_DEBUG(" write_da9052_reg Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+ return 0;
+}
+
+static ssize_t da9052_tsi_disable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq)
+{
+ u8 data = 0;
+ ssize_t ret =0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_disable_irq:");
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ data = ret;
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ data = mask_pendwn_irq(data);
+ break;
+ case TSI_DATA_RDY:
+ data = mask_tsi_rdy_irq(data);
+ break;
+ default:
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_disable_irq:");
+ DA9052_DEBUG("Invalid IRQ passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+ ret = write_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_disable_irq:");
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ ts->tsi_conf.tsi_pendown_irq_mask = SET;
+ break;
+ case TSI_DATA_RDY:
+ ts->tsi_conf.tsi_ready_irq_mask = SET;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+static ssize_t da9052_tsi_enable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq)
+{
+ u8 data =0;
+ ssize_t ret =0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_enable_irq:");
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = ret;
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ data = unmask_pendwn_irq(data);
+ break;
+ case TSI_DATA_RDY:
+ data = unmask_tsi_rdy_irq(data);
+ break;
+ default:
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_enable_irq:");
+ DA9052_DEBUG("Invalid IRQ passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+ ret = write_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_enable_irq:");
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ ts->tsi_conf.tsi_pendown_irq_mask = RESET;
+ break;
+ case TSI_DATA_RDY:
+ ts->tsi_conf.tsi_ready_irq_mask = RESET;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+static ssize_t da9052_tsi_config_gpio(struct da9052_ts_priv *priv)
+{
+ u8 idx = 0;
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg[priv->tsi_pdata->num_gpio_tsi_register];
+
+ ssc_msg[idx++].addr = DA9052_GPIO0203_REG;
+ ssc_msg[idx++].addr = DA9052_GPIO0405_REG;
+ ssc_msg[idx++].addr = DA9052_GPIO0607_REG;
+
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read_many(priv->da9052, ssc_msg,idx);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_read_many Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ idx = 0;
+ ssc_msg[idx].data = clear_bits(ssc_msg[idx].data,
+ DA9052_GPIO0203_GPIO3PIN);
+ idx++;
+ ssc_msg[idx].data = clear_bits(ssc_msg[idx].data,
+ (DA9052_GPIO0405_GPIO4PIN | DA9052_GPIO0405_GPIO5PIN));
+ idx++;
+ ssc_msg[idx].data = clear_bits(ssc_msg[idx].data,
+ (DA9052_GPIO0607_GPIO6PIN | DA9052_GPIO0607_GPIO7PIN));
+ idx++;
+
+ ret = priv->da9052->write_many(priv->da9052, ssc_msg,idx);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_read_many Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+s32 da9052_pm_configure_ldo(struct da9052_ts_priv *priv,
+ struct da9052_ldo_config ldo_config)
+{
+ struct da9052_ssc_msg msg;
+ u8 reg_num;
+ u8 ldo_volt;
+ u8 ldo_volt_bit = 0;
+ u8 ldo_conf_bit = 0;
+ u8 ldo_en_bit = 0;
+ s8 ldo_pd_bit = -1;
+ s32 ret = 0;
+
+ DA9052_DEBUG("I am in function: %s\n", __FUNCTION__);
+ if (validate_ldo9_mV(ldo_config.ldo_volt))
+ return INVALID_LDO9_VOLT_VALUE;
+
+ ldo_volt = ldo9_mV_to_reg(ldo_config.ldo_volt);
+
+ reg_num = DA9052_LDO9_REG;
+ ldo_volt_bit = DA9052_LDO9_VLDO9;
+ ldo_conf_bit = DA9052_LDO9_LDO9CONF;
+ ldo_en_bit = DA9052_LDO9_LDO9EN;
+
+ da9052_lock(priv->da9052);
+
+ msg.addr = reg_num;
+
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+ msg.data = ldo_volt |
+ (ldo_config.ldo_conf ? ldo_conf_bit : 0) |
+ (msg.data & ldo_en_bit);
+
+ ret = priv->da9052->write(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ if (-1 != ldo_pd_bit) {
+ msg.addr = DA9052_PULLDOWN_REG;
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ msg.data = (ldo_config.ldo_pd ?
+ set_bits(msg.data, ldo_pd_bit) :
+ clear_bits(msg.data, ldo_pd_bit));
+
+ ret = priv->da9052->write(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+
+s32 da9052_pm_set_ldo(struct da9052_ts_priv *priv, u8 ldo_num, u8 flag)
+{
+ struct da9052_ssc_msg msg;
+ u8 reg_num = 0;
+ u8 value = 0;
+ s32 ret = 0;
+
+ DA9052_DEBUG("I am in function: %s\n", __FUNCTION__);
+
+ reg_num = DA9052_LDO9_REG;
+ value = DA9052_LDO9_LDO9EN;
+ da9052_lock(priv->da9052);
+
+ msg.addr = reg_num;
+
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ msg.data = flag ?
+ set_bits(msg.data, value) :
+ clear_bits(msg.data, value);
+
+ ret = priv->da9052->write(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_power_supply(struct da9052_ts_priv *priv,
+ u8 state)
+{
+ struct da9052_ldo_config ldo_config;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (state != ENABLE && state != DISABLE) {
+ DA9052_DEBUG("DA9052_TSI: %s: ", __FUNCTION__);
+ DA9052_DEBUG("Invalid state Passed\n" );
+ return -EINVAL;
+ }
+
+ ldo_config.ldo_volt = priv->tsi_pdata->tsi_supply_voltage;
+ ldo_config.ldo_num = priv->tsi_pdata->tsi_ref_source;
+ ldo_config.ldo_conf = RESET;
+
+ if (da9052_pm_configure_ldo(priv, ldo_config))
+ return -EINVAL;
+
+ if (da9052_pm_set_ldo(priv, priv->tsi_pdata->tsi_ref_source, state))
+ return -EINVAL;
+
+ if (state == ENABLE)
+ ts->tsi_conf.ldo9_en = SET;
+ else
+ ts->tsi_conf.ldo9_en = RESET;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_auto_mode(struct da9052_ts_priv *priv,
+ u8 state)
+{
+ u8 data;
+ s32 ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (state != ENABLE && state != DISABLE)
+ return -EINVAL;
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = (u8)ret;
+
+ if (state == ENABLE)
+ data = set_auto_tsi_en(data);
+ else if (state == DISABLE)
+ data = reset_auto_tsi_en(data);
+ else {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid Parameter Passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Failed to configure Auto TSI mode\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_manual_mode(struct da9052_ts_priv *priv,
+ u8 state)
+{
+ u8 data = 0;
+ ssize_t ret=0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (state != ENABLE && state != DISABLE) {
+ DA9052_DEBUG("DA9052_TSI: %s: ", __FUNCTION__);
+ DA9052_DEBUG("Invalid state Passed\n" );
+ return -EINVAL;
+ }
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTB_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = (u8)ret;
+ if (state == DISABLE)
+ data = disable_tsi_manual_mode(data);
+ else
+ data = enable_tsi_manual_mode(data);
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTB_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ if (state == DISABLE)
+ ts->tsi_conf.man_cont.tsi_cont_b.tsi_man = RESET;
+ else
+ ts->tsi_conf.man_cont.tsi_cont_b.tsi_man = SET;
+
+ data = 0;
+ data = set_bits(data, DA9052_ADC_TSI);
+
+ ret = write_da9052_reg(priv->da9052, DA9052_ADCMAN_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("ADC write Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+static u32 da9052_tsi_get_reg_data(struct da9052_ts_priv *priv)
+{
+ u32 free_cnt, copy_cnt, cnt;
+
+ if (down_interruptible(&priv->tsi_reg_fifo.lock))
+ return 0;
+
+ copy_cnt = 0;
+
+ if ((priv->tsi_reg_fifo.head - priv->tsi_reg_fifo.tail) > 1) {
+ free_cnt = get_reg_free_space_cnt(priv);
+ if (free_cnt > TSI_POLL_SAMPLE_CNT)
+ free_cnt = TSI_POLL_SAMPLE_CNT;
+
+ cnt = da9052_tsi_get_rawdata(
+ &priv->tsi_reg_fifo.data[priv->tsi_reg_fifo.tail],
+ free_cnt);
+
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+
+ copy_cnt = cnt;
+
+ while (cnt--)
+ incr_with_wrap_reg_fifo(priv->tsi_reg_fifo.tail);
+
+ } else if ((priv->tsi_reg_fifo.head - priv->tsi_reg_fifo.tail) <= 0) {
+
+ free_cnt = (TSI_REG_DATA_BUF_SIZE - priv->tsi_reg_fifo.tail);
+ if (free_cnt > TSI_POLL_SAMPLE_CNT) {
+ free_cnt = TSI_POLL_SAMPLE_CNT;
+
+ cnt = da9052_tsi_get_rawdata(
+ &priv->tsi_reg_fifo.data[priv->tsi_reg_fifo.tail],
+ free_cnt);
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+ copy_cnt = cnt;
+
+ while (cnt--)
+ incr_with_wrap_reg_fifo(
+ priv->tsi_reg_fifo.tail);
+ } else {
+ if (free_cnt) {
+ cnt = da9052_tsi_get_rawdata(
+ &priv->
+ tsi_reg_fifo.data[priv->
+ tsi_reg_fifo.tail],
+ free_cnt
+ );
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+ copy_cnt = cnt;
+ while (cnt--)
+ incr_with_wrap_reg_fifo(
+ priv->tsi_reg_fifo.tail);
+ }
+ free_cnt = priv->tsi_reg_fifo.head;
+ if (free_cnt > TSI_POLL_SAMPLE_CNT - copy_cnt)
+ free_cnt = TSI_POLL_SAMPLE_CNT - copy_cnt;
+ if (free_cnt) {
+ cnt = da9052_tsi_get_rawdata(
+ &priv->tsi_reg_fifo.data[priv->
+ tsi_reg_fifo.tail], free_cnt
+ );
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+
+ copy_cnt += cnt;
+
+ while (cnt--)
+ incr_with_wrap_reg_fifo(
+ priv->tsi_reg_fifo.tail);
+ }
+ }
+ } else
+ copy_cnt = 0;
+
+ up(&priv->tsi_reg_fifo.lock);
+
+ return copy_cnt;
+}
+
+
+static ssize_t da9052_tsi_reg_proc_thread(void *ptr)
+{
+ u32 data_cnt;
+ struct da9052_tsi_info *ts;
+ struct da9052_ts_priv *priv = (struct da9052_ts_priv *)ptr;
+
+ set_freezable();
+
+ while (priv->tsi_reg_proc_thread.state == ACTIVE) {
+
+ try_to_freeze();
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(priv->
+ tsi_reg_data_poll_interval));
+
+ ts = get_tsi_drvdata();
+
+ if (!ts->pen_dwn_event)
+ continue;
+
+ data_cnt = da9052_tsi_get_reg_data(priv);
+
+ da9052_tsi_process_reg_data(priv);
+
+ if (data_cnt)
+ ts->tsi_zero_data_cnt = 0;
+ else {
+ if ((++(ts->tsi_zero_data_cnt)) >
+ ts->tsi_penup_count) {
+ ts->pen_dwn_event = RESET;
+ da9052_tsi_penup_event(priv);
+ }
+ }
+ }
+
+ complete_and_exit(&priv->tsi_reg_proc_thread.notifier, 0);
+ return 0;
+}
+
+
+static void da9052_tsi_penup_event(struct da9052_ts_priv *priv)
+{
+
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+ struct input_dev *ip_dev =
+ (struct input_dev *)da9052_tsi_get_input_dev(
+ (u8)TSI_INPUT_DEVICE_OFF);
+
+ if (da9052_tsi_config_auto_mode(priv, DISABLE))
+ goto exit;
+
+ ts->tsi_conf.auto_cont.tsi_cont_a.auto_tsi_en = RESET;
+
+ if (da9052_tsi_config_power_supply(priv, ENABLE))
+ goto exit;
+
+ ts->tsi_conf.ldo9_en = RESET;
+
+ if (da9052_tsi_enable_irq(priv, TSI_PEN_DWN))
+ goto exit;
+
+ ts->tsi_conf.tsi_pendown_irq_mask = RESET;
+
+ tsi_reg.tsi_state = WAIT_FOR_PEN_DOWN;
+
+ ts->tsi_zero_data_cnt = 0;
+ priv->early_data_flag = TRUE;
+ priv->debounce_over = FALSE;
+ priv->win_reference_valid = FALSE;
+
+ printk(KERN_INFO "The raw data count is %d \n", priv->raw_data_cnt);
+ printk(KERN_INFO "The OS data count is %d \n", priv->os_data_cnt);
+ printk(KERN_INFO "PEN UP DECLARED \n");
+ input_report_abs(ip_dev, BTN_TOUCH, 0);
+ input_sync(ip_dev);
+ priv->os_data_cnt = 0;
+ priv->raw_data_cnt = 0;
+
+exit:
+ clean_tsi_fifos(priv);
+ return;
+}
+
+void da9052_tsi_pen_down_handler(struct da9052_eh_nb *eh_data, u32 event)
+{
+ ssize_t ret = 0;
+ struct da9052_ts_priv *priv =
+ container_of(eh_data, struct da9052_ts_priv, pd_nb);
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+ struct input_dev *ip_dev =
+ (struct input_dev*)da9052_tsi_get_input_dev(
+ (u8)TSI_INPUT_DEVICE_OFF);
+
+ if (tsi_reg.tsi_state != WAIT_FOR_PEN_DOWN)
+ return;
+ DA9052_DEBUG("EH notified the pendown event 0x%x\n", event);
+
+ tsi_reg.tsi_state = WAIT_FOR_SAMPLING;
+
+ if (ts->tsi_conf.state != TSI_AUTO_MODE) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Configure TSI to auto mode.\n" );
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Then call this API.\n" );
+ goto fail;
+ }
+
+ if (da9052_tsi_config_power_supply(priv, ENABLE))
+ goto fail;
+
+ if (da9052_tsi_disable_irq(priv, TSI_PEN_DWN))
+ goto fail;
+
+ if (da9052_tsi_enable_irq(priv, TSI_DATA_RDY))
+ goto fail;
+
+ if (da9052_tsi_config_auto_mode(priv, ENABLE))
+ goto fail;
+ ts->tsi_conf.auto_cont.tsi_cont_a.auto_tsi_en = SET;
+
+ input_sync(ip_dev);
+
+ ts->tsi_rdy_event = (DA9052_EVENTB_ETSIREADY & (event>>8));
+ ts->pen_dwn_event = (DA9052_EVENTB_EPENDOWN & (event>>8));
+
+ tsi_reg.tsi_state = SAMPLING_ACTIVE;
+
+ goto success;
+
+fail:
+ if (ts->pd_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->pd_nb);
+ ts->pd_reg_status = RESET;
+
+ priv->da9052->register_event_notifier(priv->da9052,
+ &priv->datardy_nb);
+ da9052_tsi_reg_pendwn_event(priv);
+ }
+
+success:
+ ret = 0;
+ printk(KERN_INFO "Exiting PEN DOWN HANDLER \n");
+}
+
+void da9052_tsi_data_ready_handler(struct da9052_eh_nb *eh_data, u32 event)
+{
+ struct da9052_ssc_msg tsi_data[4];
+ s32 ret;
+ struct da9052_ts_priv *priv =
+ container_of(eh_data, struct da9052_ts_priv, datardy_nb);
+
+ if (tsi_reg.tsi_state != SAMPLING_ACTIVE)
+ return;
+
+ tsi_data[0].addr = DA9052_TSIXMSB_REG;
+ tsi_data[1].addr = DA9052_TSIYMSB_REG;
+ tsi_data[2].addr = DA9052_TSILSB_REG;
+ tsi_data[3].addr = DA9052_TSIZMSB_REG;
+
+ tsi_data[0].data = 0;
+ tsi_data[1].data = 0;
+ tsi_data[2].data = 0;
+ tsi_data[3].data = 0;
+
+ da9052_lock(priv->da9052);
+
+ ret = priv->da9052->read_many(priv->da9052, tsi_data, 4);
+ if (ret) {
+ DA9052_DEBUG("Error in reading TSI data \n" );
+ da9052_unlock(priv->da9052);
+ return;
+ }
+ da9052_unlock(priv->da9052);
+
+ mutex_lock(&tsi_reg.tsi_fifo_lock);
+
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].x_msb = tsi_data[0].data;
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].y_msb = tsi_data[1].data;
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].lsb = tsi_data[2].data;
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].z_msb = tsi_data[3].data;
+ incr_with_wrap(tsi_reg.tsi_fifo_end);
+
+ if (tsi_reg.tsi_fifo_end == tsi_reg.tsi_fifo_start)
+ tsi_reg.tsi_fifo_start++;
+
+ mutex_unlock(&tsi_reg.tsi_fifo_lock);
+
+}
+
+static s32 da9052_tsi_get_rawdata(struct da9052_tsi_reg *buf, u8 cnt) {
+ u32 data_cnt = 0;
+ u32 rem_data_cnt = 0;
+
+ mutex_lock(&tsi_reg.tsi_fifo_lock);
+
+ if (tsi_reg.tsi_fifo_start < tsi_reg.tsi_fifo_end) {
+ data_cnt = (tsi_reg.tsi_fifo_end - tsi_reg.tsi_fifo_start);
+
+ if (cnt < data_cnt)
+ data_cnt = cnt;
+
+ memcpy(buf, &tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_start],
+ sizeof(struct da9052_tsi_reg) * data_cnt);
+
+ tsi_reg.tsi_fifo_start += data_cnt;
+
+ if (tsi_reg.tsi_fifo_start == tsi_reg.tsi_fifo_end) {
+ tsi_reg.tsi_fifo_start = 0;
+ tsi_reg.tsi_fifo_end = 0;
+ }
+ } else if (tsi_reg.tsi_fifo_start > tsi_reg.tsi_fifo_end) {
+ data_cnt = ((TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start)
+ + tsi_reg.tsi_fifo_end);
+
+ if (cnt < data_cnt)
+ data_cnt = cnt;
+
+ if (data_cnt <= (TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start)) {
+ memcpy(buf, &tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_start],
+ sizeof(struct da9052_tsi_reg) * data_cnt);
+
+ tsi_reg.tsi_fifo_start += data_cnt;
+ if (tsi_reg.tsi_fifo_start >= TSI_FIFO_SIZE)
+ tsi_reg.tsi_fifo_start = 0;
+ } else {
+ memcpy(buf, &tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_start],
+ sizeof(struct da9052_tsi_reg)
+ * (TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start));
+
+ rem_data_cnt = (data_cnt -
+ (TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start));
+
+ memcpy(buf, &tsi_reg.tsi_fifo[0],
+ sizeof(struct da9052_tsi_reg) * rem_data_cnt);
+
+ tsi_reg.tsi_fifo_start = rem_data_cnt;
+ }
+
+ if (tsi_reg.tsi_fifo_start == tsi_reg.tsi_fifo_end) {
+ tsi_reg.tsi_fifo_start = 0;
+ tsi_reg.tsi_fifo_end = 0;
+ }
+ } else
+ data_cnt = 0;
+
+ mutex_unlock(&tsi_reg.tsi_fifo_lock);
+
+ return data_cnt;
+}
+
+static ssize_t da9052_tsi_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ printk(KERN_INFO "%s: called\n", __FUNCTION__);
+ return 0;
+}
+
+static ssize_t da9052_tsi_resume(struct platform_device *dev)
+{
+ printk(KERN_INFO "%s: called\n", __FUNCTION__);
+ return 0;
+}
+
+static s32 __devinit da9052_tsi_probe(struct platform_device *pdev)
+{
+
+ struct da9052_ts_priv *priv;
+ struct da9052_tsi_platform_data *pdata = pdev->dev.platform_data;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, priv);
+
+ priv->tsi_pdata = pdata;
+
+ if (da9052_tsi_init_drv(priv))
+ return -EFAULT;
+
+ mutex_init(&tsi_reg.tsi_fifo_lock);
+ tsi_reg.tsi_state = WAIT_FOR_PEN_DOWN;
+
+ printk(KERN_INFO "TSI Drv Successfully Inserted %s\n",
+ DA9052_TSI_DEVICE_NAME);
+ return 0;
+}
+
+static int __devexit da9052_tsi_remove(struct platform_device *pdev)
+{
+ struct da9052_ts_priv *priv = platform_get_drvdata(pdev);
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+ s32 ret = 0, i = 0;
+
+ ret = da9052_tsi_config_state(priv, TSI_IDLE);
+ if (!ret)
+ return -EINVAL;
+
+ if (ts->pd_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->pd_nb);
+ ts->pd_reg_status = RESET;
+ }
+
+ if (ts->datardy_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->datardy_nb);
+ ts->datardy_reg_status = RESET;
+ }
+
+ mutex_destroy(&tsi_reg.tsi_fifo_lock);
+
+ priv->tsi_reg_proc_thread.state = INACTIVE;
+ wait_for_completion(&priv->tsi_reg_proc_thread.notifier);
+
+ priv->tsi_raw_proc_thread.state = INACTIVE;
+ wait_for_completion(&priv->tsi_raw_proc_thread.notifier);
+
+ for (i = 0; i < NUM_INPUT_DEVS; i++) {
+ input_unregister_device(ts->input_devs[i]);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ DA9052_DEBUG(KERN_DEBUG "Removing %s \n", DA9052_TSI_DEVICE_NAME);
+
+ return 0;
+}
+
+static struct platform_driver da9052_tsi_driver = {
+ .probe = da9052_tsi_probe,
+ .remove = __devexit_p(da9052_tsi_remove),
+ .suspend = da9052_tsi_suspend,
+ .resume = da9052_tsi_resume,
+ .driver = {
+ .name = DA9052_TSI_DEVICE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_tsi_init(void)
+{
+ printk("DA9052 TSI Device Driver, v1.0\n");
+ return platform_driver_register(&da9052_tsi_driver);
+}
+module_init(da9052_tsi_init);
+
+static void __exit da9052_tsi_exit(void)
+{
+ printk(KERN_ERR "TSI Driver %s Successfully Removed \n",
+ DA9052_TSI_DEVICE_NAME);
+ return;
+
+}
+module_exit(da9052_tsi_exit);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/input/touchscreen/da9052_tsi_calibrate.c b/drivers/input/touchscreen/da9052_tsi_calibrate.c
new file mode 100644
index 000000000000..4ffd0bbe7483
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi_calibrate.c
@@ -0,0 +1,107 @@
+/*
+ * da9052_tsi_calibrate.c -- TSI Calibration driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/mfd/da9052/tsi.h>
+
+static struct Calib_xform_matrix_t xform = {
+ .An = DA9052_TSI_CALIB_AN,
+ .Bn = DA9052_TSI_CALIB_BN,
+ .Cn = DA9052_TSI_CALIB_CN,
+ .Dn = DA9052_TSI_CALIB_DN,
+ .En = DA9052_TSI_CALIB_EN,
+ .Fn = DA9052_TSI_CALIB_FN,
+ .Divider = DA9052_TSI_CALIB_DIVIDER
+};
+
+static struct calib_cfg_t calib = {
+ .calibrate_flag = TSI_USE_CALIBRATION,
+};
+
+struct calib_cfg_t *get_calib_config(void)
+{
+ return &calib;
+}
+
+ssize_t da9052_tsi_set_calib_matrix(struct da9052_tsi_data *displayPtr,
+ struct da9052_tsi_data *screenPtr)
+{
+
+ int retValue = SUCCESS ;
+
+ xform.Divider = ((screenPtr[0].x - screenPtr[1].x)
+ * (screenPtr[1].y - screenPtr[2].y))
+ - ((screenPtr[1].x - screenPtr[2].x)
+ * (screenPtr[0].y - screenPtr[1].y));
+
+ if (xform.Divider == 0)
+ retValue = -FAILURE;
+ else {
+ xform.An = ((displayPtr[0].x - displayPtr[1].x)
+ * (screenPtr[1].y - screenPtr[2].y))
+ - ((displayPtr[1].x - displayPtr[2].x)
+ * (screenPtr[0].y - screenPtr[1].y));
+
+ xform.Bn = ((displayPtr[1].x - displayPtr[2].x)
+ * (screenPtr[0].x - screenPtr[1].x))
+ - ((screenPtr[1].x - screenPtr[2].x)
+ * (displayPtr[0].x - displayPtr[1].x));
+
+ xform.Cn = (displayPtr[0].x * xform.Divider)
+ - (screenPtr[0].x * xform.An)
+ - (screenPtr[0].y * xform.Bn);
+
+ xform.Dn = ((displayPtr[0].y - displayPtr[1].y)
+ * (screenPtr[1].y - screenPtr[2].y))
+ - ((displayPtr[1].y - displayPtr[2].y)
+ * (screenPtr[0].y - screenPtr[1].y));
+
+ xform.En = ((displayPtr[1].y - displayPtr[2].y)
+ * (screenPtr[0].x - screenPtr[1].x))
+ - ((screenPtr[1].x - screenPtr[2].x)
+ * (displayPtr[0].y - displayPtr[1].y));
+
+ xform.Fn = (displayPtr[0].y * xform.Divider)
+ - (screenPtr[0].x * xform.Dn)
+ - (screenPtr[0].y * xform.En);
+ }
+
+ return retValue;
+}
+
+ssize_t da9052_tsi_get_calib_display_point(struct da9052_tsi_data *displayPtr)
+{
+ int retValue = TRUE;
+ struct da9052_tsi_data screen_coord;
+
+ screen_coord = *displayPtr;
+ if (xform.Divider != 0) {
+ displayPtr->x = ((xform.An * screen_coord.x) +
+ (xform.Bn * screen_coord.y) +
+ xform.Cn
+ ) / xform.Divider;
+
+ displayPtr->y = ((xform.Dn * screen_coord.x) +
+ (xform.En * screen_coord.y) +
+ xform.Fn
+ ) / xform.Divider;
+ } else
+ retValue = FALSE;
+
+#if DA9052_TSI_CALIB_DATA_PROFILING
+ printk("C\tX\t%4d\tY\t%4d\n",
+ displayPtr->x,
+ displayPtr->y);
+#endif
+ return retValue;
+}
diff --git a/drivers/input/touchscreen/da9052_tsi_filter.c b/drivers/input/touchscreen/da9052_tsi_filter.c
new file mode 100644
index 000000000000..16467edf386f
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi_filter.c
@@ -0,0 +1,489 @@
+/*
+ * da9052_tsi_filter.c -- TSI filter driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/freezer.h>
+#include <linux/mfd/da9052/tsi.h>
+
+#define get_abs(x) (x < 0 ? ((-1) * x) : (x))
+
+#define WITHIN_WINDOW(x, y) ((get_abs(x) <= TSI_X_WINDOW_SIZE) && \
+ (get_abs(y) <= TSI_Y_WINDOW_SIZE))
+
+#define FIRST_SAMPLE 0
+
+#define incr_with_wrap_raw_fifo(x) \
+ if (++x >= TSI_RAW_DATA_BUF_SIZE) \
+ x = 0
+
+#define decr_with_wrap_raw_fifo(x) \
+ if (--x < 0) \
+ x = (TSI_RAW_DATA_BUF_SIZE-1)
+
+static u32 get_raw_data_cnt(struct da9052_ts_priv *priv);
+static void da9052_tsi_convert_reg_to_coord(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data);
+static inline void clean_tsi_reg_fifo(struct da9052_ts_priv *priv);
+static inline void clean_tsi_raw_fifo(struct da9052_ts_priv *priv);
+
+#if (ENABLE_AVERAGE_FILTER)
+static void da9052_tsi_avrg_filter(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *tsi_avg_data);
+
+#endif
+#if (ENABLE_TSI_DEBOUNCE)
+static s32 da9052_tsi_calc_debounce_data(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data);
+
+#endif
+# if (ENABLE_WINDOW_FILTER)
+static s32 diff_within_window(struct da9052_tsi_data *prev_raw_data,
+ struct da9052_tsi_data *cur_raw_data);
+#endif
+static s32 da9052_tsi_window_filter(struct da9052_ts_priv *ts,
+ struct da9052_tsi_data *raw_data);
+
+void clean_tsi_fifos(struct da9052_ts_priv *priv)
+{
+ clean_tsi_raw_fifo(priv);
+ clean_tsi_reg_fifo(priv);
+}
+
+void __init da9052_init_tsi_fifos(struct da9052_ts_priv *priv)
+{
+ init_MUTEX(&priv->tsi_raw_fifo.lock);
+ init_MUTEX(&priv->tsi_reg_fifo.lock);
+
+ clean_tsi_raw_fifo(priv);
+ clean_tsi_reg_fifo(priv);
+}
+
+u32 get_reg_data_cnt(struct da9052_ts_priv *priv)
+{
+ u8 reg_data_cnt;
+
+ if (priv->tsi_reg_fifo.head <= priv->tsi_reg_fifo.tail) {
+ reg_data_cnt = (priv->tsi_reg_fifo.tail -
+ priv->tsi_reg_fifo.head);
+ } else {
+ reg_data_cnt = (priv->tsi_reg_fifo.tail +
+ (TSI_REG_DATA_BUF_SIZE -
+ priv->tsi_reg_fifo.head));
+ }
+
+ return reg_data_cnt;
+}
+
+u32 get_reg_free_space_cnt(struct da9052_ts_priv *priv)
+{
+ u32 free_cnt;
+
+ if (priv->tsi_reg_fifo.head <= priv->tsi_reg_fifo.tail) {
+ free_cnt = ((TSI_REG_DATA_BUF_SIZE - 1) -
+ (priv->tsi_reg_fifo.tail - priv->tsi_reg_fifo.head));
+ } else
+ free_cnt = ((priv->tsi_reg_fifo.head - priv->tsi_reg_fifo.tail)
+ - 1);
+
+ return free_cnt;
+}
+
+void da9052_tsi_process_reg_data(struct da9052_ts_priv *priv)
+{
+ s32 ret;
+ struct da9052_tsi_data tmp_raw_data;
+ u32 reg_data_cnt;
+
+ if (down_interruptible(&priv->tsi_reg_fifo.lock))
+ return;
+
+ reg_data_cnt = get_reg_data_cnt(priv);
+
+ while (reg_data_cnt-- > 0) {
+
+ ret = 0;
+
+ if (get_raw_data_cnt(priv) >= (TSI_RAW_DATA_BUF_SIZE - 1)) {
+ DA9052_DEBUG("%s: RAW data FIFO is full\n",
+ __FUNCTION__);
+ break;
+ }
+
+ da9052_tsi_convert_reg_to_coord(priv, &tmp_raw_data);
+
+ if ((tmp_raw_data.x < TS_X_MIN) ||
+ (tmp_raw_data.x > TS_X_MAX) ||
+ (tmp_raw_data.y < TS_Y_MIN) ||
+ (tmp_raw_data.y > TS_Y_MAX)) {
+ DA9052_DEBUG("%s: ", __FUNCTION__);
+ DA9052_DEBUG("sample beyond touchscreen panel ");
+ DA9052_DEBUG("dimensions\n");
+ continue;
+ }
+
+#if (ENABLE_TSI_DEBOUNCE)
+ if (debounce_over == FALSE) {
+ ret =
+ da9052_tsi_calc_debounce_data(priv, &tmp_raw_data);
+ if (ret != SUCCESS)
+ continue;
+ }
+#endif
+
+# if (ENABLE_WINDOW_FILTER)
+ ret = da9052_tsi_window_filter(priv, &tmp_raw_data);
+ if (ret != SUCCESS)
+ continue;
+#endif
+ priv->early_data_flag = FALSE;
+
+ if (down_interruptible(&priv->tsi_raw_fifo.lock)) {
+ DA9052_DEBUG("%s: Failed to ", __FUNCTION__);
+ DA9052_DEBUG("acquire RAW FIFO Lock!\n");
+
+ up(&priv->tsi_reg_fifo.lock);
+ return;
+ }
+
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = tmp_raw_data;
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+ up(&priv->tsi_raw_fifo.lock);
+ }
+
+
+ up(&priv->tsi_reg_fifo.lock);
+
+ return;
+}
+
+static u32 get_raw_data_cnt(struct da9052_ts_priv *priv)
+{
+ u32 raw_data_cnt;
+
+ if (priv->tsi_raw_fifo.head <= priv->tsi_raw_fifo.tail)
+ raw_data_cnt =
+ (priv->tsi_raw_fifo.tail - priv->tsi_raw_fifo.head);
+ else
+ raw_data_cnt =
+ (priv->tsi_raw_fifo.tail + (TSI_RAW_DATA_BUF_SIZE -
+ priv->tsi_raw_fifo.head));
+
+ return raw_data_cnt;
+}
+
+static void da9052_tsi_convert_reg_to_coord(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data)
+{
+
+ struct da9052_tsi_reg *src;
+ struct da9052_tsi_data *dst = raw_data;
+
+ src = &priv->tsi_reg_fifo.data[priv->tsi_reg_fifo.head];
+
+ dst->x = (src->x_msb << X_MSB_SHIFT);
+ dst->x |= (src->lsb & X_LSB_MASK) >> X_LSB_SHIFT;
+
+ dst->y = (src->y_msb << Y_MSB_SHIFT);
+ dst->y |= (src->lsb & Y_LSB_MASK) >> Y_LSB_SHIFT;
+
+ dst->z = (src->z_msb << Z_MSB_SHIFT);
+ dst->z |= (src->lsb & Z_LSB_MASK) >> Z_LSB_SHIFT;
+
+#if DA9052_TSI_RAW_DATA_PROFILING
+ printk("R\tX\t%4d\tY\t%4d\tZ\t%4d\n",
+ (u16)dst->x,
+ (u16)dst->y,
+ (u16)dst->z);
+#endif
+ priv->raw_data_cnt++;
+ incr_with_wrap_reg_fifo(priv->tsi_reg_fifo.head);
+}
+
+#if (ENABLE_AVERAGE_FILTER)
+static void da9052_tsi_avrg_filter(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *tsi_avg_data)
+{
+ u8 cnt;
+
+ if (down_interruptible(&priv->tsi_raw_fifo.lock)) {
+ printk("%s: No RAW Lock !\n", __FUNCTION__);
+ return;
+ }
+
+ (*tsi_avg_data) = priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head];
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+
+ for (cnt = 2; cnt <= TSI_AVERAGE_FILTER_SIZE; cnt++) {
+
+ tsi_avg_data->x +=
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].x;
+ tsi_avg_data->y +=
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].y;
+ tsi_avg_data->z +=
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].z;
+
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+ }
+
+ up(&priv->tsi_raw_fifo.lock);
+
+ tsi_avg_data->x /= TSI_AVERAGE_FILTER_SIZE;
+ tsi_avg_data->y /= TSI_AVERAGE_FILTER_SIZE;
+ tsi_avg_data->z /= TSI_AVERAGE_FILTER_SIZE;
+
+#if DA9052_TSI_AVG_FLT_DATA_PROFILING
+ printk("A\tX\t%4d\tY\t%4d\tZ\t%4d\n",
+ (u16)tsi_avg_data->x,
+ (u16)tsi_avg_data->y,
+ (u16)tsi_avg_data->z);
+#endif
+
+ return;
+}
+#endif
+
+s32 da9052_tsi_raw_proc_thread(void *ptr)
+{
+ struct da9052_tsi_data coord;
+ u8 calib_ok, range_ok;
+ struct calib_cfg_t *tsi_calib = get_calib_config();
+ struct input_dev *ip_dev = (struct input_dev *)
+ da9052_tsi_get_input_dev(
+ (u8)TSI_INPUT_DEVICE_OFF);
+ struct da9052_ts_priv *priv = (struct da9052_ts_priv *)ptr;
+
+ set_freezable();
+
+ while (priv->tsi_raw_proc_thread.state == ACTIVE) {
+
+ try_to_freeze();
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(
+ msecs_to_jiffies(priv->tsi_raw_data_poll_interval));
+
+ if (priv->early_data_flag || (get_raw_data_cnt(priv) == 0))
+ continue;
+
+ calib_ok = TRUE;
+ range_ok = TRUE;
+
+#if (ENABLE_AVERAGE_FILTER)
+
+ if (get_raw_data_cnt(priv) < TSI_AVERAGE_FILTER_SIZE)
+ continue;
+
+ da9052_tsi_avrg_filter(priv, &coord);
+
+#else
+
+ if (down_interruptible(&priv->tsi_raw_fifo.lock))
+ continue;
+
+ coord = priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head];
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+
+ up(&priv->tsi_raw_fifo.lock);
+
+#endif
+
+ if (tsi_calib->calibrate_flag) {
+ calib_ok = da9052_tsi_get_calib_display_point(&coord);
+
+ if ((coord.x < DISPLAY_X_MIN) ||
+ (coord.x > DISPLAY_X_MAX) ||
+ (coord.y < DISPLAY_Y_MIN) ||
+ (coord.y > DISPLAY_Y_MAX))
+ range_ok = FALSE;
+ }
+
+ if (calib_ok && range_ok) {
+ input_report_abs(ip_dev, BTN_TOUCH, 1);
+ input_report_abs(ip_dev, ABS_X, coord.x);
+ input_report_abs(ip_dev, ABS_Y, coord.y);
+ input_sync(ip_dev);
+
+ priv->os_data_cnt++;
+
+#if DA9052_TSI_OS_DATA_PROFILING
+ printk("O\tX\t%4d\tY\t%4d\tZ\t%4d\n", (u16)coord.x,
+ (u16)coord.y, (u16)coord.z);
+#endif
+ } else {
+ if (!calib_ok) {
+ DA9052_DEBUG("%s: ", __FUNCTION__);
+ DA9052_DEBUG("calibration Failed\n");
+ }
+ if (!range_ok) {
+ DA9052_DEBUG("%s: ", __FUNCTION__);
+ DA9052_DEBUG("sample beyond display ");
+ DA9052_DEBUG("panel dimension\n");
+ }
+ }
+ }
+ priv->tsi_raw_proc_thread.thread_task = NULL;
+ complete_and_exit(&priv->tsi_raw_proc_thread.notifier, 0);
+ return 0;
+
+}
+
+#if (ENABLE_TSI_DEBOUNCE)
+static s32 da9052_tsi_calc_debounce_data(struct da9052_tsi_data *raw_data)
+{
+#if (TSI_DEBOUNCE_DATA_CNT)
+ u8 cnt;
+ struct da9052_tsi_data temp = {.x = 0, .y = 0, .z = 0};
+
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = (*raw_data);
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+ if (get_raw_data_cnt(priv) <= TSI_DEBOUNCE_DATA_CNT)
+ return -FAILURE;
+
+ for (cnt = 1; cnt <= TSI_DEBOUNCE_DATA_CNT; cnt++) {
+ temp.x += priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].x;
+ temp.y += priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].y;
+ temp.z += priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].z;
+
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+ }
+
+ temp.x /= TSI_DEBOUNCE_DATA_CNT;
+ temp.y /= TSI_DEBOUNCE_DATA_CNT;
+ temp.z /= TSI_DEBOUNCE_DATA_CNT;
+
+ priv->tsi_raw_fifo.tail = priv->tsi_raw_fifo.head;
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = temp;
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+#if DA9052_TSI_PRINT_DEBOUNCED_DATA
+ printk("D: X: %d Y: %d Z: %d\n",
+ (u16)temp.x, (u16)temp.y, (u16)temp.z);
+#endif
+
+#endif
+ priv->debounce_over = TRUE;
+
+ return 0;
+}
+#endif
+
+# if (ENABLE_WINDOW_FILTER)
+static s32 diff_within_window(struct da9052_tsi_data *prev_raw_data,
+ struct da9052_tsi_data *cur_raw_data)
+{
+ s32 ret = -EINVAL;
+ s32 x, y;
+ x = ((cur_raw_data->x) - (prev_raw_data->x));
+ y = ((cur_raw_data->y) - (prev_raw_data->y));
+
+ if (WITHIN_WINDOW(x, y)) {
+
+#if DA9052_TSI_WIN_FLT_DATA_PROFILING
+ printk("W\tX\t%4d\tY\t%4d\tZ\t%4d\n",
+ (u16)cur_raw_data->x,
+ (u16)cur_raw_data->y,
+ (u16)cur_raw_data->z);
+#endif
+ ret = 0;
+ }
+ return ret;
+}
+
+static s32 da9052_tsi_window_filter(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data)
+{
+ u8 ref_found;
+ u32 cur, next;
+ s32 ret = -EINVAL;
+ static struct da9052_tsi_data prev_raw_data;
+
+ if (priv->win_reference_valid == TRUE) {
+
+#if DA9052_TSI_PRINT_PREVIOUS_DATA
+ printk("P: X: %d Y: %d Z: %d\n",
+ (u16)prev_raw_data.x, (u16)prev_raw_data.y,
+ (u16)prev_raw_data.z);
+#endif
+ ret = diff_within_window(&prev_raw_data, raw_data);
+ if (!ret)
+ prev_raw_data = (*raw_data);
+ } else {
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = (*raw_data);
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+ if (get_raw_data_cnt(priv) == SAMPLE_CNT_FOR_WIN_REF) {
+
+ ref_found = FALSE;
+
+ next = cur = priv->tsi_raw_fifo.head;
+ incr_with_wrap_raw_fifo(next);
+
+ while (next <= priv->tsi_raw_fifo.tail) {
+ ret = diff_within_window(
+ &priv->tsi_raw_fifo.data[cur],
+ &priv->tsi_raw_fifo.data[next]
+ );
+ if (ret == SUCCESS) {
+ ref_found = TRUE;
+ break;
+ }
+ incr_with_wrap_raw_fifo(cur);
+ incr_with_wrap_raw_fifo(next);
+
+ }
+
+ if (ref_found == FALSE)
+ priv->tsi_raw_fifo.tail =
+ priv->tsi_raw_fifo.head;
+ else {
+ prev_raw_data = priv->tsi_raw_fifo.data[cur];
+
+ prev_raw_data.x +=
+ priv->tsi_raw_fifo.data[next].x;
+ prev_raw_data.y +=
+ priv->tsi_raw_fifo.data[next].y;
+ prev_raw_data.z +=
+ priv->tsi_raw_fifo.data[next].z;
+
+ prev_raw_data.x = prev_raw_data.x / 2;
+ prev_raw_data.y = prev_raw_data.y / 2;
+ prev_raw_data.z = prev_raw_data.z / 2;
+
+ (*raw_data) = prev_raw_data;
+
+ priv->tsi_raw_fifo.tail =
+ priv->tsi_raw_fifo.head;
+
+ priv->win_reference_valid = TRUE;
+ ret = SUCCESS;
+ }
+ }
+ }
+ return ret;
+}
+#endif
+static inline void clean_tsi_reg_fifo(struct da9052_ts_priv *priv)
+{
+ priv->tsi_reg_fifo.head = FIRST_SAMPLE;
+ priv->tsi_reg_fifo.tail = FIRST_SAMPLE;
+}
+
+static inline void clean_tsi_raw_fifo(struct da9052_ts_priv *priv)
+{
+ priv->tsi_raw_fifo.head = FIRST_SAMPLE;
+ priv->tsi_raw_fifo.tail = FIRST_SAMPLE;
+}
+
diff --git a/drivers/input/touchscreen/imx_adc_ts.c b/drivers/input/touchscreen/imx_adc_ts.c
new file mode 100644
index 000000000000..797eff5672dc
--- /dev/null
+++ b/drivers/input/touchscreen/imx_adc_ts.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2009-2010 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/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
new file mode 100644
index 000000000000..7e3dc09ca73f
--- /dev/null
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -0,0 +1,351 @@
+/*
+ * max11801_ts.c - Driver for MAXI MAX11801 - A Resistive touch screen
+ * controller with i2c interface
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/* This driver is aim for support the serial of MAXI touch chips
+ * max11801-max11803, the main different of this 4 chips can get from
+ * this table
+ * ----------------------------------------------
+ * | CHIP | AUTO MODE SUPPORT | INTERFACE |
+ * |--------------------------------------------|
+ * | max11800 | YES | SPI |
+ * | max11801 | YES | I2C |
+ * | max11802 | NO | SPI |
+ * | max11803 | NO | I2C |
+ * ----------------------------------------------
+ *
+ * Currently, this driver only support max11801.
+ * */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+/* Register Address define */
+#define GENERNAL_STATUS_REG 0x00
+#define GENERNAL_CONF_REG 0x01
+#define MESURE_RES_CONF_REG 0x02
+#define MESURE_AVER_CONF_REG 0x03
+#define ADC_SAMPLE_TIME_CONF_REG 0x04
+#define PANEL_SETUPTIME_CONF_REG 0x05
+#define DELAY_CONVERSION_CONF_REG 0x06
+#define TOUCH_DETECT_PULLUP_CONF_REG 0x07
+#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */
+#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */
+#define AUX_MESURE_CONF_REG 0x0a
+#define OP_MODE_CONF_REG 0x0b
+
+#define FIFO_RD_CMD (0x50 << 1)
+#define MAX11801_FIFO_INT (1 << 2)
+#define MAX11801_FIFO_OVERFLOW (1 << 3)
+
+#define READ_BUFFER_XYZ1Z2_SIZE 16
+#define XY_BUFSIZE 4
+
+#define MAX11801_MAX_XC 0xfff
+#define MAX11801_MAX_YC 0xfff
+
+#define MEASURE_TAG_OFFSET 2
+#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET)
+#define EVENT_TAG_OFFSET 0
+#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET)
+#define EVENT_X_TAG (0 << MEASURE_TAG_OFFSET)
+#define EVENT_Y_TAG (1 << MEASURE_TAG_OFFSET)
+
+enum {
+ EVENT_INIT,
+ EVENT_MIDDLE,
+ EVENT_RELEASE,
+ EVENT_FIFO_END
+};
+
+#ifdef CALIBRATION
+/**
+ * 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);
+#endif
+
+struct max11801_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct delayed_work work;
+ struct workqueue_struct *workq;
+};
+
+static u8 read_register(struct i2c_client *client, int addr)
+{
+ /* This chip ignore LSB of register address */
+ return i2c_smbus_read_byte_data(client, addr << 1);
+}
+
+static int max11801_write_reg(struct i2c_client *client, int addr, int data)
+{
+ /* This chip ignore LSB of register address */
+ return i2c_smbus_write_byte_data(client, addr << 1, data);
+}
+
+#ifdef CALIBRATION
+static void calibration_pointer(int *x_orig, int *y_orig)
+{
+ int x, y;
+ if (calibration[6] == 0)
+ return;
+ x = calibration[0] * (*x_orig) +
+ calibration[1] * (*y_orig) +
+ calibration[2];
+ x /= calibration[6];
+ if (x < 0)
+ x = 0;
+ y = calibration[3] * (*x_orig) +
+ calibration[4] * (*y_orig) +
+ calibration[5];
+ y /= calibration[6];
+ if (y < 0)
+ y = 0;
+
+ *x_orig = x;
+ *y_orig = y;
+}
+#else
+static void calibration_pointer(int *x_orig, int *y_orig)
+{
+ /* Currently, calibration algorithm will be overflow in XGA
+ * resolution. */
+ /* Here work around for android touch screen and LCD 's rotation */
+ /* Will remove after calibration algorithm ok. */
+ int x, y;
+ /* Swap x, y */
+ x = *y_orig;
+ y = *x_orig;
+ /* Swap X */
+ x = MAX11801_MAX_XC - x;
+ *x_orig = x;
+ *y_orig = y;
+}
+#endif
+
+static void maxi_ts_work(struct work_struct *work)
+{
+ struct max11801_data *data = container_of(to_delayed_work(work),
+ struct max11801_data, work);
+ struct i2c_client *client = data->client;
+ unsigned int x, y;
+ int status, i, ret;
+ u8 buf[XY_BUFSIZE];
+
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+ while (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+ XY_BUFSIZE, buf);
+ if (ret < 4) {
+ dev_err(&client->dev, "event not enough\n");
+ continue;
+ }
+
+ for (i = 0; i < XY_BUFSIZE; i += 2) {
+ if ((buf[i+1] & MEASURE_TAG_MASK) == EVENT_X_TAG)
+ x = (buf[i] << 4) + (buf[i+1] >> 4);
+ else if ((buf[i+1] & MEASURE_TAG_MASK) == EVENT_Y_TAG)
+ y = (buf[i] << 4) + (buf[i+1] >> 4);
+ }
+
+ if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
+ continue;
+
+ switch (buf[1] & EVENT_TAG_MASK) {
+ case EVENT_INIT:
+ case EVENT_MIDDLE:
+ dev_dbg(&client->dev, "before cali: x :%d y: %d\n",
+ x, y);
+ calibration_pointer(&x, &y);
+ dev_dbg(&client->dev, "after cali: x :%d y: %d\n",
+ x, y);
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_report_abs(data->input_dev, ABS_PRESSURE, 1);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_sync(data->input_dev);
+ break;
+ case EVENT_RELEASE:
+ input_report_abs(data->input_dev, ABS_PRESSURE, 0);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+ case EVENT_FIFO_END:
+ break;
+ }
+ }
+ enable_irq(client->irq);
+}
+
+static void max11801_ts_phy_init(struct max11801_data *data)
+{
+ struct i2c_client *client = data->client;
+
+ /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+ /* X,Y panel setup time set to 20us */
+ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+ /* Rough pullup time (2uS), Fine pullup time (10us) */
+ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+ /* Auto mode init period = 5ms , scan period = 5ms*/
+ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+ /* Aperture X,Y set to +- 4LSB */
+ max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+ /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+}
+
+static irqreturn_t max11801_irq(int irq, void *dev)
+{
+ struct max11801_data *data = dev;
+ disable_irq_nosync(irq);
+ queue_delayed_work(data->workq, &data->work, 0);
+ return IRQ_HANDLED;
+}
+
+static int __devinit max11801_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max11801_data *data;
+ struct input_dev *input_dev;
+ int ret;
+
+ data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+#ifdef CALIBRATION
+ memset(calibration, 0, sizeof(calibration));
+#endif
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ max11801_ts_phy_init(data);
+ INIT_DELAYED_WORK(&data->work, maxi_ts_work);
+ data->workq = create_singlethread_workqueue("max11801_ts");
+ if (!data->workq) {
+ dev_err(&client->dev, "Failed to create workqueue\n");
+ ret = -ENOMEM;
+ goto err_free_wq;
+ }
+
+ input_dev->name = "max11801_ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+#ifndef CALIBRATION
+ input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_YC, 0, 0);
+#endif
+ input_set_drvdata(input_dev, data);
+
+ ret = request_irq(client->irq, max11801_irq,
+ IRQF_TRIGGER_LOW, "max11801_ts", data);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_dev;
+ }
+
+ ret = input_register_device(data->input_dev);
+ if (ret < 0)
+ goto err_free_irq;
+ i2c_set_clientdata(client, data);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_wq:
+ destroy_workqueue(data->workq);
+err_free_dev:
+ input_free_device(input_dev);
+err_free_mem:
+ kfree(data);
+ return ret;
+}
+
+static __devexit int max11801_ts_remove(struct i2c_client *client)
+{
+ struct max11801_data *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ cancel_delayed_work_sync(&data->work);
+ destroy_workqueue(data->workq);
+ input_unregister_device(data->input_dev);
+ input_free_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id max11801_ts_id[] = {
+ {"max11801", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+
+static struct i2c_driver max11801_ts_driver = {
+ .probe = max11801_ts_probe,
+ .remove = __devexit_p(max11801_ts_remove),
+ .driver = {
+ .name = "max11801_ts",
+ },
+ .id_table = max11801_ts_id,
+};
+
+static int __init max11801_ts_init(void)
+{
+ return i2c_add_driver(&max11801_ts_driver);
+}
+
+static void __exit max11801_ts_exit(void)
+{
+ i2c_del_driver(&max11801_ts_driver);
+}
+
+module_init(max11801_ts_init);
+module_exit(max11801_ts_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
+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..fce77fd49e0f
--- /dev/null
+++ b/drivers/input/touchscreen/mxs-ts.c
@@ -0,0 +1,464 @@
+/*
+ * 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/slab.h>
+#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 <linux/fsl_devices.h>
+
+#include <mach/hardware.h>
+#include <mach/lradc.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/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index be23780e8a3e..cf8a4daee39a 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -295,6 +295,13 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
+ 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));
@@ -338,6 +345,8 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
pdata->exit_platform_hw();
err_free_mem:
input_free_device(input_dev);
+ err_no_dev:
+ pdata->exit_platform_hw();
kfree(ts);
return err;
}