From 55cf02b97a191496f3d777b402714a469b2e8399 Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Fri, 10 Jun 2011 10:45:22 +0800 Subject: PMIC: Add TSI/misc input Driver for Dialog DA9052 Add DA9052 TSI/misc input driver from Dialog. Modify Kconfig/Makefile for DA9052 TSI/misc input driver. Signed-off-by: Zhou Jingyu Acked-by: Lily Zhang Signed-off-by: Ying-Chun Liu (PaulLiu) --- drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 1 + drivers/input/misc/da9052_onkey.c | 133 ++ drivers/input/touchscreen/Kconfig | 7 + drivers/input/touchscreen/Makefile | 2 + drivers/input/touchscreen/da9052_tsi.c | 1446 ++++++++++++++++++++++ drivers/input/touchscreen/da9052_tsi_calibrate.c | 107 ++ drivers/input/touchscreen/da9052_tsi_filter.c | 489 ++++++++ 8 files changed, 2195 insertions(+) create mode 100644 drivers/input/misc/da9052_onkey.c create mode 100644 drivers/input/touchscreen/da9052_tsi.c create mode 100644 drivers/input/touchscreen/da9052_tsi_calibrate.c create mode 100644 drivers/input/touchscreen/da9052_tsi_filter.c (limited to 'drivers') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c9104bb4db0..88238d02a5b 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -527,4 +527,14 @@ config INPUT_XEN_KBDDEV_FRONTEND To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_DA9052_ONKEY + tristate "Dialog DA9052 Onkey" + depends on PMIC_DIALOG + help + Support the ONKEY of Dialog DA9052 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called da9052_onkey. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 299ad5edba8..cd319cfb375 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c new file mode 100644 index 00000000000..295b2005cbe --- /dev/null +++ b/drivers/input/misc/da9052_onkey.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "da9052-onkey" + +struct da9052_onkey_data { + struct da9052 *da9052; + struct da9052_eh_nb eh_data; + struct input_dev *input; +}; + +static void da9052_onkey_report_event(struct da9052_eh_nb *eh_data, + unsigned int event) +{ + struct da9052_onkey_data *da9052_onkey = + container_of(eh_data, struct da9052_onkey_data, eh_data); + struct da9052_ssc_msg msg; + unsigned int ret; + + /* Read the Evnet Register */ + msg.addr = DA9052_EVENTB_REG; + da9052_lock(da9052_onkey->da9052); + ret = da9052_onkey->da9052->read(da9052_onkey->da9052, &msg); + if (ret) { + da9052_unlock(da9052_onkey->da9052); + return; + } + da9052_unlock(da9052_onkey->da9052); + msg.data = msg.data & DA9052_EVENTB_ENONKEY; + + input_report_key(da9052_onkey->input, KEY_POWER, msg.data); + input_sync(da9052_onkey->input); + printk(KERN_INFO "DA9052 ONKEY EVENT REPORTED\n"); +} + +static int __devinit da9052_onkey_probe(struct platform_device *pdev) +{ + struct da9052_onkey_data *da9052_onkey; + int error; + + da9052_onkey = kzalloc(sizeof(*da9052_onkey), GFP_KERNEL); + da9052_onkey->input = input_allocate_device(); + if (!da9052_onkey->input) { + dev_err(&pdev->dev, "failed to allocate data device\n"); + error = -ENOMEM; + goto fail1; + } + da9052_onkey->da9052 = dev_get_drvdata(pdev->dev.parent); + + if (!da9052_onkey->input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + error = -ENOMEM; + goto fail2; + } + + da9052_onkey->input->evbit[0] = BIT_MASK(EV_KEY); + da9052_onkey->input->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + da9052_onkey->input->name = "da9052-onkey"; + da9052_onkey->input->phys = "da9052-onkey/input0"; + da9052_onkey->input->dev.parent = &pdev->dev; + + /* Set the EH structure */ + da9052_onkey->eh_data.eve_type = ONKEY_EVE; + da9052_onkey->eh_data.call_back = &da9052_onkey_report_event; + error = da9052_onkey->da9052->register_event_notifier( + da9052_onkey->da9052, + &da9052_onkey->eh_data); + if (error) + goto fail2; + + error = input_register_device(da9052_onkey->input); + if (error) { + dev_err(&pdev->dev, "Unable to register input " + "device,error: %d\n", error); + goto fail3; + } + + platform_set_drvdata(pdev, da9052_onkey); + + return 0; + +fail3: + da9052_onkey->da9052->unregister_event_notifier(da9052_onkey->da9052, + &da9052_onkey->eh_data); +fail2: + input_free_device(da9052_onkey->input); +fail1: + kfree(da9052_onkey); + return error; +} + +static int __devexit da9052_onkey_remove(struct platform_device *pdev) +{ + struct da9052_onkey_data *da9052_onkey = pdev->dev.platform_data; + da9052_onkey->da9052->unregister_event_notifier(da9052_onkey->da9052, + &da9052_onkey->eh_data); + input_unregister_device(da9052_onkey->input); + kfree(da9052_onkey); + + return 0; +} + +static struct platform_driver da9052_onkey_driver = { + .probe = da9052_onkey_probe, + .remove = __devexit_p(da9052_onkey_remove), + .driver = { + .name = "da9052-onkey", + .owner = THIS_MODULE, + } +}; + +static int __init da9052_onkey_init(void) +{ + return platform_driver_register(&da9052_onkey_driver); +} + +static void __exit da9052_onkey_exit(void) +{ + platform_driver_unregister(&da9052_onkey_driver); +} + +module_init(da9052_onkey_init); +module_exit(da9052_onkey_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("Onkey driver for DA9052"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index cabd9e54863..fd4366d6ada 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -726,4 +726,11 @@ 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_DIALOG + help + Say y here to support the touchscreen found on + Dialog Semiconductor DA9052 PMIC + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 282d6f76ae2..06471951723 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -6,6 +6,8 @@ wm97xx-ts-y := wm97xx-core.o +da9052-tsi-objs := da9052_tsi.o da9052_tsi_filter.o da9052_tsi_calibrate.o +obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052-tsi.o obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c new file mode 100644 index 00000000000..fa250ab1bf9 --- /dev/null +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -0,0 +1,1446 @@ +/* + * da9052_tsi.c -- TSI driver for Dialog DA9052 + * + * Copyright(c) 2009 Dialog Semiconductor Ltd. + * + * Author: Dialog Semiconductor Ltd + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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); +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: ", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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 ", __func__); + 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", __func__); + 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 ", __func__); + 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", __func__); + 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__, + __func__); + 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 ", __func__); + 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: ", __func__); + 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.thread_task = + kthread_run(da9052_tsi_reg_proc_thread, + priv, "da9052_tsi_reg"); + + init_completion(&priv->tsi_raw_proc_thread.notifier); + priv->tsi_raw_proc_thread.state = ACTIVE; + priv->tsi_raw_proc_thread.thread_task = + kthread_run(da9052_tsi_raw_proc_thread, + priv, "da9052_tsi_raw"); + + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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; + + 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", __func__); + + 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: ", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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: ", __func__); + 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:", __func__); + 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:", __func__); + 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:", __func__); + 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; + ssize_t ret = 0; + 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; + + if (da9052_tsi_disable_irq(priv, TSI_DATA_RDY)) + goto exit; + + 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; + + tsi_reg.tsi_state = WAIT_FOR_SAMPLING; + + if (ts->tsi_conf.state != TSI_AUTO_MODE) { + DA9052_DEBUG("DA9052_TSI: %s:", __func__); + DA9052_DEBUG(" Configure TSI to auto mode.\n"); + DA9052_DEBUG("DA9052_TSI: %s:", __func__); + 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_config_auto_mode(priv, ENABLE)) + goto fail; + ts->tsi_conf.auto_cont.tsi_cont_a.auto_tsi_en = SET; + + if (da9052_tsi_enable_irq(priv, TSI_DATA_RDY)) + goto fail; + + 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); + +#if 1 + 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); +#endif +/* printk(KERN_INFO "Exiting Data ready handler\n");*/ +} + +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", __func__); + return 0; +} + +static ssize_t da9052_tsi_resume(struct platform_device *dev) +{ + printk(KERN_INFO "%s: called\n", __func__); + 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(KERN_DEBUG "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 "); +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 00000000000..6208462c584 --- /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 + * + * 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 + +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(KERN_DEBUG "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 00000000000..c29dd016c34 --- /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 + * + * 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 +#include +#include + +#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) +{ + sema_init(&priv->tsi_raw_fifo.lock, 1); + sema_init(&priv->tsi_reg_fifo.lock, 1); + + 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", + __func__); + 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: ", __func__); + 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 ", __func__); + 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(KERN_DEBUG "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(KERN_DEBUG "%s: No RAW Lock !\n", __func__); + 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(KERN_DEBUG "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(KERN_DEBUG "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: ", __func__); + DA9052_DEBUG("calibration Failed\n"); + } + if (!range_ok) { + DA9052_DEBUG("%s: ", __func__); + 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(KERN_DEBUG "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(KERN_DEBUG "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(KERN_DEBUG "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; +} + -- cgit v1.2.3