diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2013-09-30 09:33:07 +0200 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2013-09-30 09:33:07 +0200 |
commit | 2b3164d35a9eda538e6dc1fcbf3eca5bcc4f526b (patch) | |
tree | 1010eef19ccbf11c9b9bf6a526b531d64c7bc139 | |
parent | 426b5e3157b00f34986116c2dcf93f50ea061565 (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.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-mvf/devices-mvf.h | 4 | ||||
-rw-r--r-- | arch/arm/plat-mxc/devices/platform-mvf-adc.c | 13 | ||||
-rwxr-xr-x | arch/arm/plat-mxc/include/mach/devices-common.h | 5 | ||||
-rw-r--r-- | drivers/misc/mvf_adc.c | 288 |
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); } |