summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2013-10-02 15:36:25 +0200
committerStefan Agner <stefan.agner@toradex.com>2013-10-02 15:36:25 +0200
commit821308d4f18d849f2666c66acd9e7c09dc7d4b19 (patch)
treee6bd4670e96fe8e8206344ccec650a6fdeba4c32
parent1000a638ba153fd88146742d2f4702c028251329 (diff)
mvf_adc: Add touch detection support
In order to be more energy efficient sample the ADC channels only if touch is detected. Do the pinmux in the platform specific code, extended the platform data with those helper functions.
-rw-r--r--arch/arm/mach-mvf/board-colibri_vf50.c37
-rw-r--r--arch/arm/mach-mvf/devices-mvf.h4
-rw-r--r--arch/arm/plat-mxc/devices/platform-mvf-adc.c12
-rw-r--r--arch/arm/plat-mxc/include/mach/colibri-ts.h30
-rwxr-xr-xarch/arm/plat-mxc/include/mach/devices-common.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mvf.h7
-rw-r--r--drivers/misc/mvf_adc.c187
7 files changed, 210 insertions, 72 deletions
diff --git a/arch/arm/mach-mvf/board-colibri_vf50.c b/arch/arm/mach-mvf/board-colibri_vf50.c
index de76b3612ce1..0928cd9ce9ab 100644
--- a/arch/arm/mach-mvf/board-colibri_vf50.c
+++ b/arch/arm/mach-mvf/board-colibri_vf50.c
@@ -73,6 +73,7 @@
#include <mach/mipi_csi2.h>
#include <mach/fsl_l2_switch.h>
#include <mach/mxc.h>
+#include <mach/colibri-ts.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
@@ -254,6 +255,40 @@ static inline void mvf_vf700_init_uart(void)
mvf_add_imx_uart(2, &mvf_uart2_pdata);
}
+static int colibri_ts_mux_pen_interrupt(struct platform_device *pdev)
+{
+ mxc_iomux_v3_setup_pad(MVF600_PAD8_PTA18);
+ mxc_iomux_v3_setup_pad(MVF600_PAD9_PTA19);
+
+ dev_dbg(&pdev->dev, "Muxed XP/XM as GPIO\n");
+
+ return 0;
+}
+
+static int colibri_ts_mux_adc(struct platform_device *pdev)
+{
+ mxc_iomux_v3_setup_pad(MVF600_PAD8_PTA18_ADC0_SE0);
+ mxc_iomux_v3_setup_pad(MVF600_PAD9_PTA19_ADC0_SE1);
+
+ dev_dbg(&pdev->dev, "Muxed XP/XM for ADC mode\n");
+
+ return 0;
+}
+
+
+static struct colibri_ts_platform_data colibri_ts_pdata = {
+ .mux_pen_interrupt = &colibri_ts_mux_pen_interrupt,
+ .mux_adc = &colibri_ts_mux_adc,
+ .gpio_pen = 8, /* PAD8 */
+};
+
+struct platform_device *__init colibri_add_touchdev(
+ const struct colibri_ts_platform_data *pdata)
+{
+ return imx_add_platform_device("mvf-adc-ts", 0, NULL, 0,
+ pdata, sizeof(*pdata));
+}
+
static struct fec_platform_data fec_data __initdata = {
.phy = PHY_INTERFACE_MODE_RMII,
};
@@ -392,7 +427,6 @@ static void __init mvf_init_adc(void)
{
mvf_add_adc(0);
mvf_add_adc(1);
- mvf_add_adc_touch(0);
}
/*!
@@ -411,6 +445,7 @@ static void __init mvf_board_init(void)
mvf_add_snvs_rtc();
mvf_init_adc();
+ colibri_add_touchdev(&colibri_ts_pdata);
mvf_add_pm_imx(0, &mvf_vf600_pm_data);
diff --git a/arch/arm/mach-mvf/devices-mvf.h b/arch/arm/mach-mvf/devices-mvf.h
index 29949074cf44..c434834ba0b2 100644
--- a/arch/arm/mach-mvf/devices-mvf.h
+++ b/arch/arm/mach-mvf/devices-mvf.h
@@ -148,10 +148,6 @@ extern const struct mvf_adc_data mvfa5_adc_data[] __initconst;
#define mvf_add_adc(id) \
mvf_add_adcdev(&mvfa5_adc_data[id])
-extern const struct mvf_adc_touch mvfa5_adc_touch[] __initconst;
-#define mvf_add_adc_touch(id) \
- mvf_add_adc_touchdev(&mvfa5_adc_touch[id])
-
extern const struct imx_imx2_wdt_data fsl_imx2_wdt_data[] __initconst;
#define mvf_add_wdt(id) \
imx_add_imx2_wdt(&fsl_imx2_wdt_data[id])
diff --git a/arch/arm/plat-mxc/devices/platform-mvf-adc.c b/arch/arm/plat-mxc/devices/platform-mvf-adc.c
index 259251c91fb8..085bc4508a31 100644
--- a/arch/arm/plat-mxc/devices/platform-mvf-adc.c
+++ b/arch/arm/plat-mxc/devices/platform-mvf-adc.c
@@ -28,12 +28,6 @@ const struct mvf_adc_data mvfa5_adc_data[] __initconst = {
mvf_adc_data_entry(MVF, 0, SZ_4K),
mvf_adc_data_entry(MVF, 1, SZ_4K),
};
-
-const struct mvf_adc_touch mvfa5_adc_touch[] __initconst = {
- [0] = {
- .id = 0
- }
-};
#endif
struct platform_device *__init mvf_add_adcdev(
@@ -56,9 +50,3 @@ struct platform_device *__init mvf_add_adcdev(
ARRAY_SIZE(res), NULL, 0);
}
-struct platform_device *__init mvf_add_adc_touchdev(
- const struct mvf_adc_touch *data)
-{
- return imx_add_platform_device("mvf-adc-ts", data->id, NULL, 0,
- NULL, 0);
-}
diff --git a/arch/arm/plat-mxc/include/mach/colibri-ts.h b/arch/arm/plat-mxc/include/mach/colibri-ts.h
new file mode 100644
index 000000000000..dde7c3ce8687
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/colibri-ts.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 by Stefan Agner <stefan.agner@toradex.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.
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef ASMARM_ARCH_COLIBRI_TS_H
+#define ASMARM_ARCH_COLIBRI_TS_H
+
+struct colibri_ts_platform_data {
+ int (*init)(struct platform_device *pdev);
+ void (*exit)(struct platform_device *pdev);
+ unsigned int gpio_pen;
+ int (*mux_pen_interrupt)(struct platform_device *pdev);
+ int (*mux_adc)(struct platform_device *pdev);
+};
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index 0de55332cff6..0e2a2a1d13e9 100755
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -97,11 +97,6 @@ struct mvf_adc_data {
};
struct platform_device *__init mvf_add_adcdev(
const struct mvf_adc_data *data);
-struct mvf_adc_touch {
- int id;
-};
-struct platform_device *__init mvf_add_adc_touchdev(
- const struct mvf_adc_touch *data);
struct imx_imxdi_rtc_data {
resource_size_t iobase;
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mvf.h b/arch/arm/plat-mxc/include/mach/iomux-mvf.h
index 3d69c7c7ef7b..0d1ff6abb048 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mvf.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mvf.h
@@ -280,6 +280,13 @@ typedef enum iomux_config {
IOMUX_PAD(0x0034, 0x0034, 0, 0x0370, 0, \
MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE)
+/*Touchscreen touch detection*/
+#define MVF600_PAD8_PTA18 \
+ IOMUX_PAD(0x0020, 0x0020, 0, 0x0370, 0, \
+ MVF600_TS_PAD_CTRL | PAD_CTL_IBE_ENABLE)
+#define MVF600_PAD9_PTA19 \
+ IOMUX_PAD(0x0024, 0x0024, 0, 0x0370, 0, \
+ MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE)
/*DCU0*/
diff --git a/drivers/misc/mvf_adc.c b/drivers/misc/mvf_adc.c
index ca430f8d5821..30c417c98622 100644
--- a/drivers/misc/mvf_adc.c
+++ b/drivers/misc/mvf_adc.c
@@ -28,6 +28,7 @@
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <mach/colibri-ts.h>
#define DRIVER_NAME "mvf-adc"
#define DRIVER_TS_NAME "mvf-adc-ts"
@@ -43,44 +44,59 @@
#define COL_TS_GPIO_XM 1
#define COL_TS_GPIO_YP 2
#define COL_TS_GPIO_YM 3
+#define COL_TS_GPIO_TOUCH 4
+#define COL_TS_GPIO_PULLUP 5
-#define COL_TOUCH_GPIOS 4
+#define COL_TS_WIRE_XP 0
+#define COL_TS_WIRE_XM 1
+#define COL_TS_WIRE_YP 2
+#define COL_TS_WIRE_YM 3
struct col_ts_driver {
int enable_state;
- struct gpio control_gpio;
+ struct gpio *control_gpio;
};
/* GPIO */
-static struct col_ts_driver col_ts_drivers[COL_TOUCH_GPIOS] = {
- [COL_TS_GPIO_XP] = {
+static struct gpio col_ts_gpios[] = {
+ [COL_TS_GPIO_XP] = { 13, GPIOF_IN, "Touchscreen PX" },
+ [COL_TS_GPIO_XM] = { 5, GPIOF_IN, "Touchscreen MX" },
+ [COL_TS_GPIO_YP] = { 12, GPIOF_IN, "Touchscreen PY" },
+ [COL_TS_GPIO_YM] = { 4, GPIOF_IN, "Touchscreen MY" },
+ [COL_TS_GPIO_TOUCH] = { 8, GPIOF_IN, "Touchscreen (Touch interrupt)" },
+ [COL_TS_GPIO_PULLUP] = { 9, GPIOF_IN, "Touchscreen (Pull-up)" },
+};
+
+/* GPIOs and their FET configuration */
+static struct col_ts_driver col_ts_drivers[] = {
+ [COL_TS_WIRE_XP] = {
.enable_state = 0,
- .control_gpio = { 13, GPIOF_IN, "Touchscreen PX" },
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_XP],
},
- [COL_TS_GPIO_XM] = {
+ [COL_TS_WIRE_XM] = {
.enable_state = 1,
- .control_gpio = { 5, GPIOF_IN, "Touchscreen MX" },
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_XM],
},
- [COL_TS_GPIO_YP] = {
+ [COL_TS_WIRE_YP] = {
.enable_state = 0,
- .control_gpio = { 12, GPIOF_IN, "Touchscreen PY" },
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_YP],
},
- [COL_TS_GPIO_YM] = {
+ [COL_TS_WIRE_YM] = {
.enable_state = 1,
- .control_gpio = { 4, GPIOF_IN, "Touchscreen MY" },
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_YM],
},
};
#define col_ts_init_wire(ts_gpio) \
- gpio_direction_output(col_ts_drivers[ts_gpio].control_gpio.gpio, \
+ gpio_direction_output(col_ts_drivers[ts_gpio].control_gpio->gpio, \
!col_ts_drivers[ts_gpio].enable_state)
#define col_ts_enable_wire(ts_gpio) \
- gpio_set_value(col_ts_drivers[ts_gpio].control_gpio.gpio, \
+ gpio_set_value(col_ts_drivers[ts_gpio].control_gpio->gpio, \
col_ts_drivers[ts_gpio].enable_state)
#define col_ts_disable_wire(ts_gpio) \
- gpio_set_value(col_ts_drivers[ts_gpio].control_gpio.gpio, \
+ gpio_set_value(col_ts_drivers[ts_gpio].control_gpio->gpio, \
!col_ts_drivers[ts_gpio].enable_state)
/*
@@ -121,6 +137,7 @@ struct adc_touch_device {
bool stop_touchscreen;
+ int pen_irq;
struct input_dev *ts_input;
struct workqueue_struct *ts_workqueue;
struct work_struct ts_work;
@@ -720,6 +737,18 @@ static int adc_ts_measure(int plate1, int plate2, int adc, int adc_channel)
return value;
}
+static void adc_ts_enable_touch_detection(struct adc_touch_device *adc_ts)
+{
+ struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data;
+
+ /* Enable plate YM (needs to be strong 0) */
+ col_ts_enable_wire(COL_TS_WIRE_YM);
+
+ /* Let the platform mux to GPIO in order to enable Pull-Up on GPIO */
+ if (pdata->mux_pen_interrupt)
+ pdata->mux_pen_interrupt(adc_ts->pdev);
+}
+
static void adc_ts_work(struct work_struct *ts_work)
{
struct adc_touch_device *adc_ts = container_of(ts_work,
@@ -743,32 +772,26 @@ static void adc_ts_work(struct work_struct *ts_work)
adc_initiate(adc_devices[1]);
adc_set(adc_devices[1], &feature);
- /* Initialize GPIOs, close FETs by default */
- col_ts_init_wire(COL_TS_GPIO_XP);
- col_ts_init_wire(COL_TS_GPIO_XM);
- col_ts_init_wire(COL_TS_GPIO_YP);
- col_ts_init_wire(COL_TS_GPIO_YM);
-
while (!adc_ts->stop_touchscreen)
{
/* X-Direction */
- val_x = adc_ts_measure(COL_TS_GPIO_XP, COL_TS_GPIO_XM, 1, 0);
+ val_x = adc_ts_measure(COL_TS_WIRE_XP, COL_TS_WIRE_XM, 1, 0);
if (val_x < 0)
continue;
/* Y-Direction */
- val_y = adc_ts_measure(COL_TS_GPIO_YP, COL_TS_GPIO_YM, 0, 0);
+ val_y = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_YM, 0, 0);
if (val_y < 0)
continue;
/* Touch pressure
* Measure on XP/YM
*/
- val_z1 = adc_ts_measure(COL_TS_GPIO_YP, COL_TS_GPIO_XM, 0, 1);
+ val_z1 = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_XM, 0, 1);
if (val_z1 < 0)
continue;
- val_z2 = adc_ts_measure(COL_TS_GPIO_YP, COL_TS_GPIO_XM, 1, 2);
+ val_z2 = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_XM, 1, 2);
if (val_z2 < 0)
continue;
@@ -786,59 +809,125 @@ static void adc_ts_work(struct work_struct *ts_work)
val_p = 2000;
}
/*
- dev_info(dev, "Measured values: x: %d, y: %d, z1: %d, z2: %d, "
+ dev_dbg(dev, "Measured values: x: %d, y: %d, z1: %d, z2: %d, "
"p: %d\n", val_x, val_y, val_z1, val_z2, val_p);
*/
/* If difference of the AD levels of the two plates is near
* the maximum, there is no touch..
*/
- if (val_p < 1100) {
- input_report_abs(adc_ts->ts_input, ABS_X, MVF_ADC_MAX - val_x);
- input_report_abs(adc_ts->ts_input, ABS_Y, MVF_ADC_MAX - val_y);
- input_report_abs(adc_ts->ts_input, ABS_PRESSURE, 2000 - val_p);
+ /*
+ * If touch pressure is too low, stop measuring and reenable
+ * touch detection
+ */
+ if (val_p > 1050)
+ break;
- input_report_key(adc_ts->ts_input, BTN_TOUCH, 1);
- } else {
- input_report_abs(adc_ts->ts_input, ABS_PRESSURE, 0);
- input_report_key(adc_ts->ts_input, BTN_TOUCH, 0);
- }
+ /* Report touch position and sleep for next measurement */
+ input_report_abs(adc_ts->ts_input, ABS_X, MVF_ADC_MAX - val_x);
+ input_report_abs(adc_ts->ts_input, ABS_Y, MVF_ADC_MAX - val_y);
+ input_report_abs(adc_ts->ts_input, ABS_PRESSURE, 2000 - val_p);
+ input_report_key(adc_ts->ts_input, BTN_TOUCH, 1);
input_sync(adc_ts->ts_input);
+
msleep(10);
}
+ /* Report no more touch, reenable touch detection */
+ input_report_abs(adc_ts->ts_input, ABS_PRESSURE, 0);
+ input_report_key(adc_ts->ts_input, BTN_TOUCH, 0);
+ input_sync(adc_ts->ts_input);
+
+ /* Wait the pull-up to be stable on high */
+ adc_ts_enable_touch_detection(adc_ts);
+ msleep(10);
+
+ /* Reenable IRQ to detect touch */
+ enable_irq(adc_ts->pen_irq);
+
+ dev_dbg(dev, "Reenabled touch detection interrupt\n");
}
-static int adc_ts_open(struct input_dev *dev)
+static irqreturn_t adc_tc_touched(int irq, void *dev_id)
{
- struct adc_touch_device *adc_ts = input_get_drvdata(dev);
+ struct adc_touch_device *adc_ts = (struct adc_touch_device *)dev_id;
+ struct device *dev = &adc_ts->pdev->dev;
+ struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data;
- adc_ts->stop_touchscreen = false;
+ dev_dbg(dev, "Touch detected, start worker thread\n");
+
+ /* Stop IRQ */
+ disable_irq_nosync(irq);
+
+ /* Disable the touch detection plates */
+ col_ts_disable_wire(COL_TS_WIRE_YM);
+
+ /* Let the platform mux to GPIO in order to enable Pull-Up on GPIO */
+ if (pdata->mux_adc)
+ pdata->mux_adc(adc_ts->pdev);
/* Start worker thread */
queue_work(adc_ts->ts_workqueue, &adc_ts->ts_work);
- dev_info(&dev->dev, "Touchscreen open\n");
+ return IRQ_HANDLED;
+}
+
+static int adc_ts_open(struct input_dev *dev_input)
+{
+ int ret;
+ struct adc_touch_device *adc_ts = input_get_drvdata(dev_input);
+ struct device *dev = &adc_ts->pdev->dev;
+ struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data;
+
+ dev_dbg(dev, "Input device %s opened, starting touch detection\n",
+ dev_input->name);
+
+ adc_ts->stop_touchscreen = false;
+
+ /* Initialize GPIOs, leave FETs closed by default */
+ col_ts_init_wire(COL_TS_WIRE_XP);
+ col_ts_init_wire(COL_TS_WIRE_XM);
+ col_ts_init_wire(COL_TS_WIRE_YP);
+ col_ts_init_wire(COL_TS_WIRE_YM);
+
+ /* Mux detection before request IRQ */
+ adc_ts_enable_touch_detection(adc_ts);
+
+ adc_ts->pen_irq = gpio_to_irq(pdata->gpio_pen);
+ if (adc_ts->pen_irq < 0) {
+ dev_err(dev, "Unable to get IRQ for GPIO %d\n", pdata->gpio_pen);
+ return adc_ts->pen_irq;
+ }
+
+ ret = request_irq(adc_ts->pen_irq, adc_tc_touched, IRQF_TRIGGER_FALLING,
+ "touch detected", adc_ts);
+ if (ret < 0) {
+ dev_err(dev, "Unable to request IRQ %d\n", adc_ts->pen_irq);
+ return ret;
+ }
return 0;
}
-static void adc_ts_close(struct input_dev *dev)
+static void adc_ts_close(struct input_dev *dev_input)
{
- struct adc_touch_device *adc_ts = input_get_drvdata(dev);
+ struct adc_touch_device *adc_ts = input_get_drvdata(dev_input);
+ struct device *dev = &adc_ts->pdev->dev;
+
+ free_irq(gpio_to_irq(col_ts_gpios[COL_TS_GPIO_TOUCH].gpio), adc_ts);
adc_ts->stop_touchscreen = true;
/* Wait until touchscreen thread finishes any possible remnants. */
cancel_work_sync(&adc_ts->ts_work);
- dev_info(&dev->dev, "Touchscreen close\n");
+ dev_dbg(dev, "Input device %s closed, disable touch detection\n",
+ dev_input->name);
}
static int __devinit adc_ts_probe(struct platform_device *pdev)
{
int ret = 0;
- int i = 0;
struct device *dev = &pdev->dev;
struct input_dev *input;
struct adc_touch_device *adc_ts;
@@ -879,6 +968,7 @@ static int __devinit adc_ts_probe(struct platform_device *pdev)
goto err;
}
+ /* Create workqueue for ADC sampling and calculation */
INIT_WORK(&adc_ts->ts_work, adc_ts_work);
adc_ts->ts_workqueue = create_singlethread_workqueue("mvf-adc-touch");
@@ -887,14 +977,11 @@ static int __devinit adc_ts_probe(struct platform_device *pdev)
goto err;
}
- for (i = 0;i < COL_TOUCH_GPIOS;i++) {
- ret = gpio_request_one(col_ts_drivers[i].control_gpio.gpio,
- col_ts_drivers[i].control_gpio.flags,
- col_ts_drivers[i].control_gpio.label);
- if (ret) {
- dev_err(dev, "failed to request GPIOs\n");
- goto err;
- }
+ /* Request GPIOs for FETs and touch detection */
+ ret = gpio_request_array(col_ts_gpios, COL_TS_GPIO_PULLUP + 1);
+ if (ret) {
+ dev_err(dev, "failed to request GPIOs\n");
+ goto err;
}
return 0;