diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2013-10-02 18:38:38 +0200 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2013-10-02 18:38:38 +0200 |
commit | 21b01a4d6e5ef433116fe457a18fd561b7de0a3e (patch) | |
tree | 7652753b02d11afdfef49db3edc8a3c1ddc99751 | |
parent | 821308d4f18d849f2666c66acd9e7c09dc7d4b19 (diff) |
colibri-vf50-ts/mvf-adc: Moved touchscreen support in a module
The touchscreen support backed by hardware on the Colibri VF50
module and the Vybrid ADC is now moved to a own module for better
maintainability. The mvf-adc driver exports some of its method
now. A good good locking mechanism is still missing.
-rw-r--r-- | arch/arm/mach-mvf/board-colibri_vf50.c | 2 | ||||
-rwxr-xr-x | drivers/input/touchscreen/Kconfig | 13 | ||||
-rwxr-xr-x | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/colibri-vf50-ts.c | 443 | ||||
-rw-r--r-- | drivers/misc/mvf_adc.c | 474 | ||||
-rw-r--r-- | include/linux/mvf_adc.h | 8 |
6 files changed, 532 insertions, 409 deletions
diff --git a/arch/arm/mach-mvf/board-colibri_vf50.c b/arch/arm/mach-mvf/board-colibri_vf50.c index 0928cd9ce9ab..2eecc72ba2fc 100644 --- a/arch/arm/mach-mvf/board-colibri_vf50.c +++ b/arch/arm/mach-mvf/board-colibri_vf50.c @@ -285,7 +285,7 @@ static struct colibri_ts_platform_data colibri_ts_pdata = { 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, + return imx_add_platform_device("colibri-vf50-ts", 0, NULL, 0, pdata, sizeof(*pdata)); } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index dcd283c11eaa..e2ed17521035 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -777,4 +777,17 @@ config TOUCHSCREEN_CRTOUCH To compile this driver as a module, choose M here: the module will be called crtouch_ts. + +config TOUCHSCREEN_COLIBRI_VF50 + tristate "Toradex Colibri on board touchscreen driver" + depends on ARCH_MVF && MVF_ADC + help + Say Y here if you have a Colibri VF50 and plan to use + the on-board provided 4-wire touchscreen driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called colibri_vf50_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index efed4c9c3adc..8ee22ce06c71 100755 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -66,3 +66,4 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_CRTOUCH) += crtouch_ts.o +obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c new file mode 100644 index 000000000000..be34e4865225 --- /dev/null +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -0,0 +1,443 @@ +/* Copyright 2013 Toradex AG + * + * Toradex Colibri VF50 Touchscreen driver + * + * 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. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#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> +#include <mach/colibri-ts.h> + +#define DRIVER_NAME "colibri-vf50-ts" +#define DRV_VERSION "1.0" + +#define MVF_ADC_MAX ((1 << 12) - 1) + +#define COLI_TOUCH_MIN_DELAY_US 1000 +#define COLI_TOUCH_MAX_DELAY_US 2000 + +#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_TS_GPIO_TOUCH 4 +#define COL_TS_GPIO_PULLUP 5 + +#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; +}; + +/* GPIO */ +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 = &col_ts_gpios[COL_TS_GPIO_XP], + }, + [COL_TS_WIRE_XM] = { + .enable_state = 1, + .control_gpio = &col_ts_gpios[COL_TS_GPIO_XM], + }, + [COL_TS_WIRE_YP] = { + .enable_state = 0, + .control_gpio = &col_ts_gpios[COL_TS_GPIO_YP], + }, + [COL_TS_WIRE_YM] = { + .enable_state = 1, + .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, \ + !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) + +struct adc_touch_device { + struct platform_device *pdev; + + bool stop_touchscreen; + + int pen_irq; + struct input_dev *ts_input; + struct workqueue_struct *ts_workqueue; + struct work_struct ts_work; +}; + +struct adc_touch_device *touch; + +/* + * Enables given plates and measures touch parameters using ADC + */ +static int adc_ts_measure(int plate1, int plate2, int adc, int adc_channel) +{ + int i, value = 0; + col_ts_enable_wire(plate1); + col_ts_enable_wire(plate2); + + /* Use hrtimer sleep since msleep sleeps 10ms+ */ + usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); + + for (i = 0; i < 5; i++) { + int ret = mvf_adc_register_and_convert(adc, adc_channel); + if (ret < 0) + return -EINVAL; + + value += ret; + } + + value /= 5; + + col_ts_disable_wire(plate1); + col_ts_disable_wire(plate2); + + return value; +} + +/* + * Enable touch detection using falling edge detection on XM + */ +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); +} + +/* + * ADC touch screen sampling worker function + */ +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); + struct device *dev = &adc_ts->pdev->dev; + int val_x, val_y, val_z1, val_z2, val_p = 0; + + struct adc_feature feature = { + .channel = 0, + .clk_sel = ADCIOC_BUSCLK_SET, + .clk_div_num = 1, +// .res_mode = 12, + .ha_sam = 32, + .sam_time = 24, + .hs_oper = ADCIOC_HSOFF_SET + }; + + mvf_adc_initiate(0); + mvf_adc_set(0, &feature); + + mvf_adc_initiate(1); + mvf_adc_set(1, &feature); + + while (!adc_ts->stop_touchscreen) + { + /* X-Direction */ + 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_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_WIRE_YP, COL_TS_WIRE_XM, 0, 1); + if (val_z1 < 0) + continue; + val_z2 = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_XM, 1, 2); + if (val_z2 < 0) + continue; + + /* According to datasheet of our touchscreen, + * resistance on X axis is 400~1200.. + */ + // if ((val_z2 - val_z1) < (MVF_ADC_MAX - (1<<9)) { + /* Validate signal (avoid calculation using noise) */ + if (val_z1 > 64 && val_x > 64) { + /* Calculate resistance between the plates + * lower resistance means higher pressure */ + int r_x = (1000 * val_x) / MVF_ADC_MAX; + val_p = (r_x * val_z2) / val_z1 - r_x; + } else { + val_p = 2000; + } + /* + 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 touch pressure is too low, stop measuring and reenable + * touch detection + */ + if (val_p > 1050) + break; + + /* 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 irqreturn_t adc_tc_touched(int irq, void *dev_id) +{ + 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; + + 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); + + 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, wait for pull-up to settle */ + adc_ts_enable_touch_detection(adc_ts); + msleep(10); + + 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_input) +{ + 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_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; + 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; + } + + adc_ts->pdev = pdev; + + 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; + } + + /* 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"); + + if (!adc_ts->ts_workqueue) { + dev_err(dev, "failed to create workqueue"); + 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; + } + + dev_info(dev, "attached driver successfully\n"); + + 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; +} + +static struct platform_driver adc_ts_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = adc_ts_probe, + .remove = __devexit_p(adc_ts_remove), +}; + +static int __init adc_ts_init(void) +{ + int ret; + + ret = platform_driver_register(&adc_ts_driver); + if (ret) + printk(KERN_ERR "%s: failed to add adc touchscreen driver\n", + __func__); + + return ret; +} + +static void __exit adc_ts_exit(void) +{ + platform_driver_unregister(&adc_ts_driver); +} +module_init(adc_ts_init); +module_exit(adc_ts_exit); + +MODULE_AUTHOR("Stefan Agner"); +MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/misc/mvf_adc.c b/drivers/misc/mvf_adc.c index 30c417c98622..53f99b3c93cb 100644 --- a/drivers/misc/mvf_adc.c +++ b/drivers/misc/mvf_adc.c @@ -25,80 +25,13 @@ #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> -#include <mach/colibri-ts.h> #define DRIVER_NAME "mvf-adc" -#define DRIVER_TS_NAME "mvf-adc-ts" #define DRV_VERSION "1.2" #define MVF_ADC_MAX_DEVICES 4 #define MVF_ADC_MAX ((1 << 12) - 1) -#define COLI_TOUCH_MIN_DELAY_US 1000 -#define COLI_TOUCH_MAX_DELAY_US 2000 - -#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_TS_GPIO_TOUCH 4 -#define COL_TS_GPIO_PULLUP 5 - -#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; -}; - -/* GPIO */ -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 = &col_ts_gpios[COL_TS_GPIO_XP], - }, - [COL_TS_WIRE_XM] = { - .enable_state = 1, - .control_gpio = &col_ts_gpios[COL_TS_GPIO_XM], - }, - [COL_TS_WIRE_YP] = { - .enable_state = 0, - .control_gpio = &col_ts_gpios[COL_TS_GPIO_YP], - }, - [COL_TS_WIRE_YM] = { - .enable_state = 1, - .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, \ - !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) */ @@ -132,24 +65,11 @@ struct adc_device { static struct adc_device *adc_devices[MVF_ADC_MAX_DEVICES]; -struct adc_touch_device { - struct platform_device *pdev; - - bool stop_touchscreen; - - int pen_irq; - 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) @@ -183,7 +103,7 @@ static void adc_try(struct adc_device *adc) } } -int adc_initiate(struct adc_device *adc_dev) +static int adc_initiate(struct adc_device *adc_dev) { unsigned long reg, tmp, pin; struct adc_device *adc = adc_dev; @@ -567,6 +487,69 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) return 0; } +static int adc_convert_wait(struct adc_device *adc_dev, unsigned char channel) +{ + INIT_COMPLETION(adc_tsi); + adc_try(adc_dev); + wait_for_completion(&adc_tsi); + + if (!data_array[channel].flag) + return -EINVAL; + + data_array[channel].flag = 0; + return data_array[channel].res_value; +} + +/** + * mvf_adc_initiate - Initiate a given ADC converter + * + * @adc: ADC block to initiate + */ +int mvf_adc_initiate(unsigned int adc) +{ + return adc_initiate(adc_devices[adc]); +} +EXPORT_SYMBOL(mvf_adc_initiate); + +/** + * mvf_adc_set - Configure a given ADC converter + * + * @adc: ADC block to configure + * @adc_fea: Features to enable + * + * Returns zero on success, error number otherwise + */ +int mvf_adc_set(unsigned int adc, struct adc_feature *adc_fea) +{ + return adc_set(adc_devices[adc], adc_fea); +} +EXPORT_SYMBOL(mvf_adc_set); + +/** + * mvf_adc_register_and_convert - Register a client and start a convertion + * + * @adc: ADC block + * @channel: Channel to convert + * + * Returns converted value or error code + */ +int mvf_adc_register_and_convert(unsigned int adc, unsigned char channel) +{ + struct adc_client *client; + + /* Register client... */ + client = adc_register(adc_devices[adc]->pdev, channel); + if (!client) + return -ENOMEM; + + /* Start convertion */ + return adc_convert_wait(adc_devices[adc], channel); + + /* Free client */ + kfree(client); +} +EXPORT_SYMBOL(mvf_adc_register_and_convert); + static int adc_open(struct inode *inode, struct file *file) { struct adc_device *dev = container_of(inode->i_cdev, @@ -609,13 +592,8 @@ static long adc_ioctl(struct file *file, unsigned int cmd, break; case ADC_CONVERT: - INIT_COMPLETION(adc_tsi); - adc_try(adc_dev); - wait_for_completion_interruptible(&adc_tsi); - if (data_array[feature.channel].flag) { - feature.result0 = data_array[feature.channel].res_value; - data_array[feature.channel].flag = 0; - } + feature.result0 = adc_convert_wait(adc_dev, feature.channel); + if (copy_to_user((struct adc_feature *)argp, &feature, sizeof(feature))) return -EFAULT; @@ -703,311 +681,6 @@ static const struct file_operations adc_fops = { .read = NULL, }; -/* - * Enables given plates and measures touch parameters using ADC - */ -static int adc_ts_measure(int plate1, int plate2, int adc, int adc_channel) -{ - int i, value = 0; - col_ts_enable_wire(plate1); - col_ts_enable_wire(plate2); - - /* Use hrtimer sleep since msleep sleeps 10ms+ */ - usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); - - for (i = 0; i < 5; i++) { - adc_register(adc_devices[adc]->pdev, adc_channel); - - INIT_COMPLETION(adc_tsi); - adc_try(adc_devices[adc]); - wait_for_completion(&adc_tsi); - - if (!data_array[adc_channel].flag) - return -EINVAL; - - value += data_array[adc_channel].res_value; - data_array[adc_channel].flag = 0; - } - - value /= 5; - - col_ts_disable_wire(plate1); - col_ts_disable_wire(plate2); - - 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, - struct adc_touch_device, ts_work); - struct device *dev = &adc_ts->pdev->dev; - int val_x, val_y, val_z1, val_z2, val_p = 0; - - struct adc_feature feature = { - .channel = 0, - .clk_sel = ADCIOC_BUSCLK_SET, - .clk_div_num = 1, -// .res_mode = 12, - .ha_sam = 32, - .sam_time = 24, - .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); - - - while (!adc_ts->stop_touchscreen) - { - /* X-Direction */ - 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_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_WIRE_YP, COL_TS_WIRE_XM, 0, 1); - if (val_z1 < 0) - continue; - val_z2 = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_XM, 1, 2); - if (val_z2 < 0) - continue; - - /* According to datasheet of our touchscreen, - * resistance on X axis is 400~1200.. - */ - // if ((val_z2 - val_z1) < (MVF_ADC_MAX - (1<<9)) { - /* Validate signal (avoid calculation using noise) */ - if (val_z1 > 64 && val_x > 64) { - /* Calculate resistance between the plates - * lower resistance means higher pressure */ - int r_x = (1000 * val_x) / MVF_ADC_MAX; - val_p = (r_x * val_z2) / val_z1 - r_x; - } else { - val_p = 2000; - } - /* - 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 touch pressure is too low, stop measuring and reenable - * touch detection - */ - if (val_p > 1050) - break; - - /* 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 irqreturn_t adc_tc_touched(int irq, void *dev_id) -{ - 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; - - 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); - - 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_input) -{ - 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_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; - 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; - } - - adc_ts->pdev = pdev; - - 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; - } - - /* 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"); - - if (!adc_ts->ts_workqueue) { - dev_err(dev, "failed to create workqueue"); - 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; -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) { @@ -1119,15 +792,6 @@ 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; @@ -1154,11 +818,6 @@ 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: @@ -1167,7 +826,6 @@ err: static void __exit adc_exit(void) { - platform_driver_unregister(&adc_ts_driver); platform_driver_unregister(&adc_driver); class_destroy(adc_class); } diff --git a/include/linux/mvf_adc.h b/include/linux/mvf_adc.h index 26562531afe0..f4288e623514 100644 --- a/include/linux/mvf_adc.h +++ b/include/linux/mvf_adc.h @@ -201,4 +201,12 @@ struct adc_feature { unsigned int result0, result1; }; +#ifdef __KERNEL__ +extern int mvf_adc_initiate(unsigned int adc); + +extern int mvf_adc_set(unsigned int adc, struct adc_feature *adc_fea); + +extern int mvf_adc_register_and_convert(unsigned int adc, unsigned char channel); +#endif + #endif |