diff options
Diffstat (limited to 'drivers/input')
-rwxr-xr-x | drivers/input/touchscreen/Kconfig | 20 | ||||
-rwxr-xr-x | drivers/input/touchscreen/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/colibri-vf50-ts.c | 413 | ||||
-rw-r--r-- | drivers/input/touchscreen/fusion_F0710A.c | 502 | ||||
-rw-r--r-- | drivers/input/touchscreen/fusion_F0710A.h | 87 |
5 files changed, 1024 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index dcd283c11eaa..482013b2225f 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -777,4 +777,24 @@ 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. + +config TOUCHSCREEN_FUSION_F0710A + tristate "TouchRevolution Fusion F0710A Touchscreens" + depends on I2C + help + Say Y here if you want to support the multi-touch input driver for + the TouchRevolution Fusion 7 and 10 panels. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index efed4c9c3adc..66b42836bc7e 100755 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -66,3 +66,5 @@ 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 +obj-$(CONFIG_TOUCHSCREEN_FUSION_F0710A) += fusion_F0710A.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..bc42253f778b --- /dev/null +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -0,0 +1,413 @@ +/* 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 + +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 plate_p, int plate_m, int adc, int adc_channel) +{ + int i, value = 0; + gpio_set_value(plate_p, 0); /* Low active */ + gpio_set_value(plate_m, 1); /* High active */ + + /* 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; + + gpio_set_value(plate_p, 1); + gpio_set_value(plate_m, 0); + + 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 GND, high active) */ + gpio_set_value(pdata->gpio_ym, 1); + + /* 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; + struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data; + int val_x, val_y, val_z1, val_z2, val_p = 0; + + struct adc_feature feature = { + .channel = ADC0, + .clk_sel = ADCIOC_BUSCLK_SET, + .clk_div_num = 1, + .res_mode = 12, + .sam_time = 6, + .lp_con = ADCIOC_LPOFF_SET, + .hs_oper = ADCIOC_HSOFF_SET, + .vol_ref = ADCIOC_VR_VREF_SET, + .tri_sel = ADCIOC_SOFTTS_SET, + .ha_sel = ADCIOC_HA_SET, + .ha_sam = 8, + .do_ena = ADCIOC_DOEOFF_SET, + .ac_ena = ADCIOC_ADACKENOFF_SET, + .dma_ena = ADCIDC_DMAOFF_SET, + .cc_ena = ADCIOC_CCEOFF_SET, + .compare_func_ena = ADCIOC_ACFEOFF_SET, + .range_ena = ADCIOC_ACRENOFF_SET, + .greater_ena = ADCIOC_ACFGTOFF_SET, + .result0 = 0, + .result1 = 0, + }; + + 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(pdata->gpio_xp, pdata->gpio_xm, 1, 0); + if (val_x < 0) + continue; + + /* Y-Direction */ + val_y = adc_ts_measure(pdata->gpio_yp, pdata->gpio_ym, 0, 0); + if (val_y < 0) + continue; + + /* Touch pressure + * Measure on XP/YM + */ + val_z1 = adc_ts_measure(pdata->gpio_yp, pdata->gpio_xm, 0, 1); + if (val_z1 < 0) + continue; + val_z2 = adc_ts_measure(pdata->gpio_yp, pdata->gpio_xm, 1, 2); + if (val_z2 < 0) + continue; + + /* According to datasheet of our touchscreen, + * resistance on X axis is 400~1200.. + */ + /* 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 > 1800) + 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 */ + gpio_set_value(pdata->gpio_ym, 0); + + /* 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 */ + gpio_direction_output(pdata->gpio_xp, 1); /* Low active */ + gpio_direction_output(pdata->gpio_xm, 0); /* High active */ + gpio_direction_output(pdata->gpio_yp, 1); /* Low active */ + gpio_direction_output(pdata->gpio_ym, 0); /* High active */ + + /* 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_detect); + if (adc_ts->pen_irq < 0) { + dev_err(dev, "Unable to get IRQ for GPIO %d\n", + pdata->gpio_pen_detect); + 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(adc_ts->pen_irq, 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; + struct colibri_ts_platform_data *pdata = pdev->dev.platform_data; + + 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(pdata->gpio_xp, "Touchscreen XP"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_xm, "Touchscreen XM"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_yp, "Touchscreen YP"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_ym, "Touchscreen YM"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_pen_detect, "Pen/Touch detect"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_pen_detect_pullup, + "Pen/Touch detect pull-up"); + if (ret) + 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/input/touchscreen/fusion_F0710A.c b/drivers/input/touchscreen/fusion_F0710A.c new file mode 100644 index 000000000000..05bbdf5e8c5b --- /dev/null +++ b/drivers/input/touchscreen/fusion_F0710A.c @@ -0,0 +1,502 @@ +/* + * "fusion_F0710A" touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <asm/irq.h> +#include <linux/gpio.h> +#include <linux/input/fusion_F0710A.h> + +#include "fusion_F0710A.h" + +#define DRV_NAME "fusion_F0710A" + + +static struct fusion_F0710A_data fusion_F0710A; + +static unsigned short normal_i2c[] = { fusion_F0710A_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +//I2C_CLIENT_INSMOD; + +static int fusion_F0710A_write_u8(u8 addr, u8 data) +{ + return i2c_smbus_write_byte_data(fusion_F0710A.client, addr, data); +} + +static int fusion_F0710A_read_u8(u8 addr) +{ + return i2c_smbus_read_byte_data(fusion_F0710A.client, addr); +} + +static int fusion_F0710A_read_block(u8 addr, u8 len, u8 *data) +{ +#if 0 + /* When i2c_smbus_read_i2c_block_data() takes a block length parameter, we can do + * this. lm-sensors lists hints this has been fixed, but I can't tell whether it + * was or will be merged upstream. */ + + return i2c_smbus_read_i2c_block_data(&fusion_F0710A.client, addr, data); +#else + u8 msgbuf0[1] = { addr }; + u16 slave = fusion_F0710A.client->addr; + u16 flags = fusion_F0710A.client->flags; + struct i2c_msg msg[2] = { { slave, flags, 1, msgbuf0 }, + { slave, flags | I2C_M_RD, len, data } + }; + + return i2c_transfer(fusion_F0710A.client->adapter, msg, ARRAY_SIZE(msg)); +#endif +} + + +static int fusion_F0710A_register_input(void) +{ + int ret; + struct input_dev *dev; + + dev = fusion_F0710A.input = input_allocate_device(); + if (dev == NULL) + return -ENOMEM; + + dev->name = "fusion_F0710A"; + + set_bit(EV_KEY, dev->evbit); + set_bit(EV_ABS, dev->evbit); + + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, fusion_F0710A.info.xres-1, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, fusion_F0710A.info.yres-1, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0); + + input_set_abs_params(dev, ABS_X, 0, fusion_F0710A.info.xres-1, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, fusion_F0710A.info.yres-1, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + ret = input_register_device(dev); + if (ret < 0) + goto bail1; + + return 0; + +bail1: + input_free_device(dev); + return ret; +} + +#define WC_RETRY_COUNT 3 +static int fusion_F0710A_write_complete(void) +{ + int ret, i; + + for(i=0; i<WC_RETRY_COUNT; i++) + { + ret = fusion_F0710A_write_u8(fusion_F0710A_SCAN_COMPLETE, 0); + if(ret == 0) + break; + else + dev_err(&fusion_F0710A.client->dev, "Write complete failed(%d): %d\n", i, ret); + } + + return ret; +} + +#define DATA_START fusion_F0710A_DATA_INFO +#define DATA_END fusion_F0710A_SEC_TIDTS +#define DATA_LEN (DATA_END - DATA_START + 1) +#define DATA_OFF(x) ((x) - DATA_START) + +static int fusion_F0710A_read_sensor(void) +{ + int ret; + u8 data[DATA_LEN]; + +#define DATA(x) (data[DATA_OFF(x)]) + /* To ensure data coherency, read the sensor with a single transaction. */ + ret = fusion_F0710A_read_block(DATA_START, DATA_LEN, data); + if (ret < 0) { + dev_err(&fusion_F0710A.client->dev, + "Read block failed: %d\n", ret); + + return ret; + } + + fusion_F0710A.f_num = DATA(fusion_F0710A_DATA_INFO)&0x03; + + fusion_F0710A.y1 = DATA(fusion_F0710A_POS_X1_HI) << 8; + fusion_F0710A.y1 |= DATA(fusion_F0710A_POS_X1_LO); + fusion_F0710A.x1 = DATA(fusion_F0710A_POS_Y1_HI) << 8; + fusion_F0710A.x1 |= DATA(fusion_F0710A_POS_Y1_LO); + fusion_F0710A.z1 = DATA(fusion_F0710A_FIR_PRESS); + fusion_F0710A.tip1 = DATA(fusion_F0710A_FIR_TIDTS)&0x0f; + fusion_F0710A.tid1 = (DATA(fusion_F0710A_FIR_TIDTS)&0xf0)>>4; + + + fusion_F0710A.y2 = DATA(fusion_F0710A_POS_X2_HI) << 8; + fusion_F0710A.y2 |= DATA(fusion_F0710A_POS_X2_LO); + fusion_F0710A.x2 = DATA(fusion_F0710A_POS_Y2_HI) << 8; + fusion_F0710A.x2 |= DATA(fusion_F0710A_POS_Y2_LO); + fusion_F0710A.z2 = DATA(fusion_F0710A_SEC_PRESS); + fusion_F0710A.tip2 = DATA(fusion_F0710A_SEC_TIDTS)&0x0f; + fusion_F0710A.tid2 =(DATA(fusion_F0710A_SEC_TIDTS)&0xf0)>>4; +#undef DATA + + return 0; +} + +#define val_cut_max(x, max, reverse) \ +do \ +{ \ + if(x > max) \ + x = max; \ + if(reverse) \ + x = (max) - (x); \ +} \ +while(0) + +static void fusion_F0710A_wq(struct work_struct *work) +{ + struct input_dev *dev = fusion_F0710A.input; + int save_points = 0; + int x1 = 0, y1 = 0, z1 = 0, x2 = 0, y2 = 0, z2 = 0; + + if (fusion_F0710A_read_sensor() < 0) + goto restore_irq; + +#ifdef DEBUG + printk(KERN_DEBUG "tip1, tid1, x1, y1, z1 (%x,%x,%d,%d,%d); tip2, tid2, x2, y2, z2 (%x,%x,%d,%d,%d)\n", + fusion_F0710A.tip1, fusion_F0710A.tid1, fusion_F0710A.x1, fusion_F0710A.y1, fusion_F0710A.z1, + fusion_F0710A.tip2, fusion_F0710A.tid2, fusion_F0710A.x2, fusion_F0710A.y2, fusion_F0710A.z2); +#endif /* DEBUG */ + + val_cut_max(fusion_F0710A.x1, fusion_F0710A.info.xres-1, fusion_F0710A.info.xy_reverse); + val_cut_max(fusion_F0710A.y1, fusion_F0710A.info.yres-1, fusion_F0710A.info.xy_reverse); + val_cut_max(fusion_F0710A.x2, fusion_F0710A.info.xres-1, fusion_F0710A.info.xy_reverse); + val_cut_max(fusion_F0710A.y2, fusion_F0710A.info.yres-1, fusion_F0710A.info.xy_reverse); + + if(fusion_F0710A.tip1 == 1) + { + if(fusion_F0710A.tid1 == 1) + { + /* first point */ + x1 = fusion_F0710A.x1; + y1 = fusion_F0710A.y1; + z1 = fusion_F0710A.z1; + save_points |= fusion_F0710A_SAVE_PT1; + } + else if(fusion_F0710A.tid1 == 2) + { + /* second point ABS_DISTANCE second point pressure, BTN_2 second point touch */ + x2 = fusion_F0710A.x1; + y2 = fusion_F0710A.y1; + z2 = fusion_F0710A.z1; + save_points |= fusion_F0710A_SAVE_PT2; + } + } + + if(fusion_F0710A.tip2 == 1) + { + if(fusion_F0710A.tid2 == 2) + { + /* second point ABS_DISTANCE second point pressure, BTN_2 second point touch */ + x2 = fusion_F0710A.x2; + y2 = fusion_F0710A.y2; + z2 = fusion_F0710A.z2; + save_points |= fusion_F0710A_SAVE_PT2; + } + else if(fusion_F0710A.tid2 == 1)/* maybe this will never happen */ + { + /* first point */ + x1 = fusion_F0710A.x2; + y1 = fusion_F0710A.y2; + z1 = fusion_F0710A.z2; + save_points |= fusion_F0710A_SAVE_PT1; + } + } + + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, z1); + input_report_abs(dev, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(dev, ABS_MT_POSITION_X, x1); + input_report_abs(dev, ABS_MT_POSITION_Y, y1); + input_mt_sync(dev); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, z2); + input_report_abs(dev, ABS_MT_WIDTH_MAJOR, 2); + input_report_abs(dev, ABS_MT_POSITION_X, x2); + input_report_abs(dev, ABS_MT_POSITION_Y, y2); + input_mt_sync(dev); + + input_report_abs(dev, ABS_X, x1); + input_report_abs(dev, ABS_Y, y1); + input_report_abs(dev, ABS_PRESSURE, z1); + input_report_key(dev, BTN_TOUCH, fusion_F0710A.tip1); + + input_sync(dev); + +restore_irq: + enable_irq(fusion_F0710A.client->irq); + + /* Clear fusion_F0710A interrupt */ + fusion_F0710A_write_complete(); +} +static DECLARE_WORK(fusion_F0710A_work, fusion_F0710A_wq); + +static irqreturn_t fusion_F0710A_interrupt(int irq, void *dev_id) +{ + disable_irq_nosync(fusion_F0710A.client->irq); + + queue_work(fusion_F0710A.workq, &fusion_F0710A_work); + + return IRQ_HANDLED; +} + +const static u8* g_ver_product[4] = { + "10Z8", "70Z7", "43Z6", "" +}; + +static int fusion_F0710A_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data; + int ret; + u8 ver_product, ver_id; + u32 version; + + if (pdata == NULL) + { + dev_err(&i2c->dev, "No platform data for Fusion driver\n"); + return -ENODEV; + } + + /* Request pinmuxing, if necessary */ + if (pdata->pinmux_fusion_pins != NULL) + { + ret = pdata->pinmux_fusion_pins(); + if (ret < 0) { + dev_err(&i2c->dev, "muxing GPIOs failed\n"); + return -ENODEV; + } + } + + if ((gpio_request(pdata->gpio_int, "SO-DIMM 28 (Iris X16-38 Pen)") == 0) && + (gpio_direction_input(pdata->gpio_int) == 0)) { + gpio_export(pdata->gpio_int, 0); + } else { + dev_warn(&i2c->dev, "Could not obtain GPIO for Fusion pen down\n"); + return -ENODEV; + } + + if ((gpio_request(pdata->gpio_reset, "SO-DIMM 30 (Iris X16-39 RST)") == 0) && + (gpio_direction_output(pdata->gpio_reset, 1) == 0)) { + + /* Generate a 0 => 1 edge explicitly, and wait for startup... */ + gpio_set_value(pdata->gpio_reset, 0); + msleep(10); + gpio_set_value(pdata->gpio_reset, 1); + /* Wait for startup (up to 125ms according to datasheet) */ + msleep(125); + + gpio_export(pdata->gpio_reset, 0); + } else { + dev_warn(&i2c->dev, "Could not obtain GPIO for Fusion reset\n"); + ret = -ENODEV; + goto bail0; + } + + /* Use Pen Down GPIO as sampling interrupt */ + i2c->irq = gpio_to_irq(pdata->gpio_int); + + if(!i2c->irq) + { + dev_err(&i2c->dev, "fusion_F0710A irq < 0 \n"); + ret = -ENOMEM; + goto bail1; + } + + /* Attach the I2C client */ + fusion_F0710A.client = i2c; + i2c_set_clientdata(i2c, &fusion_F0710A); + + dev_info(&i2c->dev, "Touchscreen registered with bus id (%d) with slave address 0x%x\n", + i2c_adapter_id(fusion_F0710A.client->adapter), fusion_F0710A.client->addr); + + /* Read out a lot of registers */ + ret = fusion_F0710A_read_u8(fusion_F0710A_VIESION_INFO_LO); + if (ret < 0) { + dev_err(&i2c->dev, "query failed: %d\n", ret); + goto bail1; + } + ver_product = (((u8)ret) & 0xc0) >> 6; + version = (10 + ((((u32)ret)&0x30) >> 4)) * 100000; + version += (((u32)ret)&0xf) * 1000; + /* Read out a lot of registers */ + ret = fusion_F0710A_read_u8(fusion_F0710A_VIESION_INFO); + if (ret < 0) { + dev_err(&i2c->dev, "query failed: %d\n", ret); + goto bail1; + } + ver_id = ((u8)(ret) & 0x6) >> 1; + version += ((((u32)ret) & 0xf8) >> 3) * 10; + version += (((u32)ret) & 0x1) + 1; /* 0 is build 1, 1 is build 2 */ + dev_info(&i2c->dev, "version product %s(%d)\n", g_ver_product[ver_product] ,ver_product); + dev_info(&i2c->dev, "version id %s(%d)\n", ver_id ? "1.4" : "1.0", ver_id); + dev_info(&i2c->dev, "version series (%d)\n", version); + + switch(ver_product) + { + case fusion_F0710A_VIESION_07: /* 7 inch */ + fusion_F0710A.info.xres = fusion_F0710A07_XMAX; + fusion_F0710A.info.yres = fusion_F0710A07_YMAX; + fusion_F0710A.info.xy_reverse = fusion_F0710A07_REV; + break; + case fusion_F0710A_VIESION_43: /* 4.3 inch */ + fusion_F0710A.info.xres = fusion_F0710A43_XMAX; + fusion_F0710A.info.yres = fusion_F0710A43_YMAX; + fusion_F0710A.info.xy_reverse = fusion_F0710A43_REV; + break; + default: /* fusion_F0710A_VIESION_10 10 inch */ + fusion_F0710A.info.xres = fusion_F0710A10_XMAX; + fusion_F0710A.info.yres = fusion_F0710A10_YMAX; + fusion_F0710A.info.xy_reverse = fusion_F0710A10_REV; + break; + } + + /* Register the input device. */ + ret = fusion_F0710A_register_input(); + if (ret < 0) { + dev_err(&i2c->dev, "can't register input: %d\n", ret); + goto bail1; + } + + /* Create a worker thread */ + fusion_F0710A.workq = create_singlethread_workqueue(DRV_NAME); + if (fusion_F0710A.workq == NULL) { + dev_err(&i2c->dev, "can't create work queue\n"); + ret = -ENOMEM; + goto bail2; + } + + + /* Register for the interrupt and enable it. Our handler will + * start getting invoked after this call. */ + ret = request_irq(i2c->irq, fusion_F0710A_interrupt, IRQF_TRIGGER_RISING, + i2c->name, &fusion_F0710A); + if (ret < 0) { + dev_err(&i2c->dev, "can't get irq %d: %d\n", i2c->irq, ret); + goto bail3; + } + /* clear the irq first */ + ret = fusion_F0710A_write_u8(fusion_F0710A_SCAN_COMPLETE, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Clear irq failed: %d\n", ret); + goto bail4; + } + + return 0; + +bail4: + free_irq(i2c->irq, &fusion_F0710A); + +bail3: + destroy_workqueue(fusion_F0710A.workq); + fusion_F0710A.workq = NULL; + +bail2: + input_unregister_device(fusion_F0710A.input); +bail1: + gpio_free(pdata->gpio_reset); +bail0: + gpio_free(pdata->gpio_int); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int fusion_F0710A_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + disable_irq(i2c->irq); + flush_workqueue(fusion_F0710A.workq); + + return 0; +} + +static int fusion_F0710A_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + enable_irq(i2c->irq); + + return 0; +} +#endif + +static int fusion_F0710A_remove(struct i2c_client *i2c) +{ + struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data; + + gpio_free(pdata->gpio_int); + gpio_free(pdata->gpio_reset); + destroy_workqueue(fusion_F0710A.workq); + free_irq(i2c->irq, &fusion_F0710A); + input_unregister_device(fusion_F0710A.input); + i2c_set_clientdata(i2c, NULL); + + dev_info(&i2c->dev, "driver removed\n"); + + return 0; +} + +static struct i2c_device_id fusion_F0710A_id[] = { + {"fusion_F0710A", 0}, + {}, +}; + +static const struct dev_pm_ops fusion_F0710A_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fusion_F0710A_suspend, fusion_F0710A_resume) +}; + +static struct i2c_driver fusion_F0710A_i2c_drv = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .pm = &fusion_F0710A_pm_ops, + }, + .probe = fusion_F0710A_probe, + .remove = fusion_F0710A_remove, + .id_table = fusion_F0710A_id, + .address_list = normal_i2c, +}; + +static int __init fusion_F0710A_init( void ) +{ + int ret; + + memset(&fusion_F0710A, 0, sizeof(fusion_F0710A)); + + /* Probe for fusion_F0710A on I2C. */ + ret = i2c_add_driver(&fusion_F0710A_i2c_drv); + if (ret < 0) { + printk(KERN_WARNING DRV_NAME " can't add i2c driver: %d\n", ret); + } + + return ret; +} + +static void __exit fusion_F0710A_exit( void ) +{ + i2c_del_driver(&fusion_F0710A_i2c_drv); +} +module_init(fusion_F0710A_init); +module_exit(fusion_F0710A_exit); + +MODULE_DESCRIPTION("fusion_F0710A Touchscreen Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/fusion_F0710A.h b/drivers/input/touchscreen/fusion_F0710A.h new file mode 100644 index 000000000000..85f8210345a9 --- /dev/null +++ b/drivers/input/touchscreen/fusion_F0710A.h @@ -0,0 +1,87 @@ +/* + * "fusion_F0710A" touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* I2C slave address */ +#define fusion_F0710A_I2C_SLAVE_ADDR 0x10 + +/* I2C registers */ +#define fusion_F0710A_DATA_INFO 0x00 + +/* First Point*/ +#define fusion_F0710A_POS_X1_HI 0x01 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_X1_LO 0x02 /* 16-bit register, LSB */ +#define fusion_F0710A_POS_Y1_HI 0x03 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_Y1_LO 0x04 /* 16-bit register, LSB */ +#define fusion_F0710A_FIR_PRESS 0X05 +#define fusion_F0710A_FIR_TIDTS 0X06 + +/* Second Point */ +#define fusion_F0710A_POS_X2_HI 0x07 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_X2_LO 0x08 /* 16-bit register, LSB */ +#define fusion_F0710A_POS_Y2_HI 0x09 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_Y2_LO 0x0A /* 16-bit register, LSB */ +#define fusion_F0710A_SEC_PRESS 0x0B +#define fusion_F0710A_SEC_TIDTS 0x0C + +#define fusion_F0710A_VIESION_INFO_LO 0X0E +#define fusion_F0710A_VIESION_INFO 0X0F + +#define fusion_F0710A_RESET 0x10 +#define fusion_F0710A_SCAN_COMPLETE 0x11 + + +#define fusion_F0710A_VIESION_10 0 +#define fusion_F0710A_VIESION_07 1 +#define fusion_F0710A_VIESION_43 2 + +/* fusion_F0710A 10 inch panel */ +#define fusion_F0710A10_XMAX 2275 +#define fusion_F0710A10_YMAX 1275 +#define fusion_F0710A10_REV 1 + +/* fusion_F0710A 7 inch panel */ +#define fusion_F0710A07_XMAX 1500 +#define fusion_F0710A07_YMAX 900 +#define fusion_F0710A07_REV 0 + +/* fusion_F0710A 4.3 inch panel */ +#define fusion_F0710A43_XMAX 900 +#define fusion_F0710A43_YMAX 500 +#define fusion_F0710A43_REV 0 + +#define fusion_F0710A_SAVE_PT1 0x1 +#define fusion_F0710A_SAVE_PT2 0x2 + + + +/* fusion_F0710A touch screen information */ +struct fusion_F0710A_info { + int xres; /* x resolution */ + int yres; /* y resolution */ + int xy_reverse; /* if need reverse in the x,y value x=xres-1-x, y=yres-1-y*/ +}; + +struct fusion_F0710A_data { + struct fusion_F0710A_info info; + struct i2c_client *client; + struct workqueue_struct *workq; + struct input_dev *input; + u16 x1; + u16 y1; + u8 z1; + u8 tip1; + u8 tid1; + u16 x2; + u16 y2; + u8 z2; + u8 tip2; + u8 tid2; + u8 f_num; + u8 save_points; +}; + |