summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2013-09-30 09:33:07 +0200
committerStefan Agner <stefan.agner@toradex.com>2013-09-30 09:33:07 +0200
commit2b3164d35a9eda538e6dc1fcbf3eca5bcc4f526b (patch)
tree1010eef19ccbf11c9b9bf6a526b531d64c7bc139
parent426b5e3157b00f34986116c2dcf93f50ea061565 (diff)
mvf_adc: Initial touchscreen support
The Vybrid VF50 support 4-wire touchscreens using FETs and ADC inputs. This drivers extends the ADC driver to deliver initial support for this interface.
-rw-r--r--arch/arm/mach-mvf/board-colibri_vf50.c1
-rw-r--r--arch/arm/mach-mvf/devices-mvf.h4
-rw-r--r--arch/arm/plat-mxc/devices/platform-mvf-adc.c13
-rwxr-xr-xarch/arm/plat-mxc/include/mach/devices-common.h5
-rw-r--r--drivers/misc/mvf_adc.c288
5 files changed, 307 insertions, 4 deletions
diff --git a/arch/arm/mach-mvf/board-colibri_vf50.c b/arch/arm/mach-mvf/board-colibri_vf50.c
index 622aad8bf7aa..de76b3612ce1 100644
--- a/arch/arm/mach-mvf/board-colibri_vf50.c
+++ b/arch/arm/mach-mvf/board-colibri_vf50.c
@@ -392,6 +392,7 @@ static void __init mvf_init_adc(void)
{
mvf_add_adc(0);
mvf_add_adc(1);
+ mvf_add_adc_touch(0);
}
/*!
diff --git a/arch/arm/mach-mvf/devices-mvf.h b/arch/arm/mach-mvf/devices-mvf.h
index c434834ba0b2..29949074cf44 100644
--- a/arch/arm/mach-mvf/devices-mvf.h
+++ b/arch/arm/mach-mvf/devices-mvf.h
@@ -148,6 +148,10 @@ 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 029e8e3a39ae..259251c91fb8 100644
--- a/arch/arm/plat-mxc/devices/platform-mvf-adc.c
+++ b/arch/arm/plat-mxc/devices/platform-mvf-adc.c
@@ -28,6 +28,12 @@ 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(
@@ -49,3 +55,10 @@ struct platform_device *__init mvf_add_adcdev(
return imx_add_platform_device("mvf-adc", data->id, res,
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/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index 0e2a2a1d13e9..0de55332cff6 100755
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -97,6 +97,11 @@ 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/drivers/misc/mvf_adc.c b/drivers/misc/mvf_adc.c
index 7ff4e9d89a9c..7cc2e6a6ac5e 100644
--- a/drivers/misc/mvf_adc.c
+++ b/drivers/misc/mvf_adc.c
@@ -25,11 +25,62 @@
#include <linux/mvf_adc.h>
#include <linux/device.h>
#include <linux/cdev.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
#define DRIVER_NAME "mvf-adc"
-#define DRV_VERSION "1.1"
+#define DRIVER_TS_NAME "mvf-adc-ts"
+#define DRV_VERSION "1.2"
-#define MVF_ADC_MAX_MINORS 4
+#define MVF_ADC_MAX_DEVICES 4
+#define MVF_ADC_MAX ((1 << 12) - 1)
+
+#define MVF_ADC_TOUCH_DELAY_MS 5
+
+#define COL_TS_GPIO_XP 0
+#define COL_TS_GPIO_XM 1
+#define COL_TS_GPIO_YP 2
+#define COL_TS_GPIO_YM 3
+
+#define COL_TOUCH_GPIOS 4
+
+struct col_ts_driver {
+ int enable_state;
+ struct gpio control_gpio;
+};
+
+/* GPIO */
+static struct col_ts_driver col_ts_drivers[COL_TOUCH_GPIOS] = {
+ [COL_TS_GPIO_XP] = {
+ .enable_state = 0,
+ .control_gpio = { 13, GPIOF_IN, "Touchscreen PX" },
+ },
+ [COL_TS_GPIO_XM] = {
+ .enable_state = 1,
+ .control_gpio = { 5, GPIOF_IN, "Touchscreen MX" },
+ },
+ [COL_TS_GPIO_YP] = {
+ .enable_state = 0,
+ .control_gpio = { 12, GPIOF_IN, "Touchscreen PY" },
+ },
+ [COL_TS_GPIO_YM] = {
+ .enable_state = 1,
+ .control_gpio = { 4, GPIOF_IN, "Touchscreen MY" },
+ },
+};
+
+#define col_ts_init_wire(ts_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, \
+ 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, \
+ !col_ts_drivers[ts_gpio].enable_state)
/*
* wait_event_interruptible(wait_queue_head_t suspendq, int suspend_flag)
@@ -62,11 +113,25 @@ struct adc_device {
int irq;
};
+static struct adc_device *adc_devices[MVF_ADC_MAX_DEVICES];
+
+struct adc_touch_device {
+ struct platform_device *pdev;
+
+ bool stop_touchscreen;
+
+ struct input_dev *ts_input;
+ struct workqueue_struct *ts_workqueue;
+ struct work_struct ts_work;
+};
+
struct data {
unsigned int res_value;
bool flag;
};
+struct adc_touch_device *touch;
+
struct data data_array[7];
#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
@@ -620,6 +685,202 @@ static const struct file_operations adc_fops = {
.read = NULL,
};
+static void adc_ts_measurement(int plate1, int plate2, int adc_to_sample)
+{
+}
+
+static void adc_ts_work(struct work_struct *ts_work)
+{
+ struct adc_touch_device *adc_ts = container_of(ts_work,
+ struct adc_touch_device, ts_work);
+ int val_x = 0, val_y = 0, val_p = 0;
+
+ struct adc_feature feature = {
+ .channel = 0,
+ .clk_sel = ADCIOC_BUSCLK_SET,
+ .clk_div_num = 1,
+// .res_mode = 12,
+ .ha_sam = 32,
+ .hs_oper = ADCIOC_HSOFF_SET
+ };
+
+ adc_initiate(adc_devices[0]);
+ adc_set(adc_devices[0], &feature);
+
+ 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 */
+ col_ts_enable_wire(COL_TS_GPIO_XP);
+ col_ts_enable_wire(COL_TS_GPIO_XM);
+
+ msleep(MVF_ADC_TOUCH_DELAY_MS);
+
+ adc_register(adc_devices[1]->pdev, feature.channel);
+
+ INIT_COMPLETION(adc_tsi);
+ adc_try(adc_devices[1]);
+ wait_for_completion_interruptible(&adc_tsi);
+
+ if (data_array[feature.channel].flag) {
+ val_x = data_array[feature.channel].res_value;
+ data_array[feature.channel].flag = 0;
+ }
+
+ col_ts_disable_wire(COL_TS_GPIO_XP);
+ col_ts_disable_wire(COL_TS_GPIO_XM);
+
+ msleep(MVF_ADC_TOUCH_DELAY_MS);
+
+ col_ts_enable_wire(COL_TS_GPIO_YP);
+ col_ts_enable_wire(COL_TS_GPIO_YM);
+
+ msleep(MVF_ADC_TOUCH_DELAY_MS);
+
+ adc_register(adc_devices[0]->pdev, feature.channel);
+
+ INIT_COMPLETION(adc_tsi);
+ adc_try(adc_devices[0]);
+ wait_for_completion_interruptible(&adc_tsi);
+
+ if (data_array[feature.channel].flag) {
+ val_y = data_array[feature.channel].res_value;
+ data_array[feature.channel].flag = 0;
+ }
+
+ col_ts_disable_wire(COL_TS_GPIO_YP);
+ col_ts_disable_wire(COL_TS_GPIO_YM);
+
+
+ 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, val_p);
+ input_report_key(adc_ts->ts_input, BTN_TOUCH, 1);
+ input_sync(adc_ts->ts_input);
+ }
+}
+
+static int adc_ts_open(struct input_dev *dev)
+{
+ struct adc_touch_device *adc_ts = input_get_drvdata(dev);
+
+ adc_ts->stop_touchscreen = false;
+
+ /* Start worker thread */
+ queue_work(adc_ts->ts_workqueue, &adc_ts->ts_work);
+
+ dev_info(&dev->dev, "Touchscreen open\n");
+
+ return 0;
+}
+
+static void adc_ts_close(struct input_dev *dev)
+{
+ struct adc_touch_device *adc_ts = input_get_drvdata(dev);
+
+ 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");
+}
+
+
+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;
+
+ adc_ts = kzalloc(sizeof(struct adc_touch_device), GFP_KERNEL);
+ if (!adc_ts) {
+ dev_err(dev, "Failed to allocate TS device!\n");
+ return -ENOMEM;
+ }
+
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(dev, "Failed to allocate TS input device!\n");
+ ret = -ENOMEM;
+ goto err_input_allocate;
+ }
+
+ input->name = DRIVER_NAME;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = dev;
+ input->open = adc_ts_open;
+ input->close = adc_ts_close;
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, MVF_ADC_MAX, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, MVF_ADC_MAX, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, MVF_ADC_MAX, 0, 0);
+
+ adc_ts->ts_input = input;
+ input_set_drvdata(input, adc_ts);
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(dev, "failed to register input device\n");
+ goto err;
+ }
+
+ INIT_WORK(&adc_ts->ts_work, adc_ts_work);
+ adc_ts->ts_workqueue = create_singlethread_workqueue("mvf-adc-touch");
+
+ if (!adc_ts->ts_workqueue) {
+ dev_err(dev, "failed to create workqueue");
+ 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;
+ }
+ }
+
+ return 0;
+err:
+ input_free_device(touch->ts_input);
+
+err_input_allocate:
+ kfree(adc_ts);
+
+ return ret;
+}
+
+static int __devexit adc_ts_remove(struct platform_device *pdev)
+{
+ struct adc_touch_device *adc_ts = platform_get_drvdata(pdev);
+
+ input_unregister_device(adc_ts->ts_input);
+
+ destroy_workqueue(adc_ts->ts_workqueue);
+ kfree(adc_ts->ts_input);
+ kfree(adc_ts);
+
+ return 0;
+}
+
+
/* probe */
static int __devinit adc_probe(struct platform_device *pdev)
{
@@ -690,6 +951,9 @@ static int __devinit adc_probe(struct platform_device *pdev)
/* Associated structures */
platform_set_drvdata(pdev, adc);
+ /* Save device structure by Platform device ID for touch */
+ adc_devices[pdev->id] = adc;
+
dev_info(dev, "attached adc driver\n");
return 0;
@@ -728,6 +992,15 @@ static struct platform_driver adc_driver = {
.remove = __devexit_p(adc_remove),
};
+static struct platform_driver adc_ts_driver = {
+ .driver = {
+ .name = DRIVER_TS_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = adc_ts_probe,
+ .remove = __devexit_p(adc_ts_remove),
+};
+
static int __init adc_init(void)
{
int ret;
@@ -741,10 +1014,11 @@ static int __init adc_init(void)
}
/* Obtain device numbers and register char device */
- ret = alloc_chrdev_region(&dev, 0, MVF_ADC_MAX_MINORS, "mvf-adc");
+ ret = alloc_chrdev_region(&dev, 0, MVF_ADC_MAX_DEVICES, "mvf-adc");
if (ret)
{
- printk(KERN_ERR "%s: can't register character device\n", __func__);
+ printk(KERN_ERR "%s: can't register character device\n",
+ __func__);
goto err_class;
}
mvf_adc_major = MAJOR(dev);
@@ -753,6 +1027,11 @@ static int __init adc_init(void)
if (ret)
printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
+ ret = platform_driver_register(&adc_ts_driver);
+ if (ret)
+ printk(KERN_ERR "%s: failed to add adc touchscreen driver\n",
+ __func__);
+
err_class:
class_destroy(adc_class);
err:
@@ -761,6 +1040,7 @@ err:
static void __exit adc_exit(void)
{
+ platform_driver_unregister(&adc_ts_driver);
platform_driver_unregister(&adc_driver);
class_destroy(adc_class);
}