diff options
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 17 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/ar1020-i2c.c | 500 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 44 | ||||
-rw-r--r-- | drivers/input/touchscreen/fusion_F0710A.c | 510 | ||||
-rw-r--r-- | drivers/input/touchscreen/fusion_F0710A.h | 88 | ||||
-rw-r--r-- | drivers/input/touchscreen/stmpe-ts.c | 11 |
7 files changed, 1145 insertions, 27 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 14931da02921..3c65fe480fcb 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -103,6 +103,16 @@ config TOUCHSCREEN_AD7879_SPI To compile this driver as a module, choose M here: the module will be called ad7879-spi. +config TOUCHSCREEN_AR1020_I2C + tristate "Microchip AR1020 I2C touchscreen" + depends on I2C + help + Say Y here if you have a Microchip AR1020 I2C Controller and + want to enable support for the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called ar1020-i2c. + config TOUCHSCREEN_AR1021_I2C tristate "Microchip AR1021 i2c touchscreen" depends on I2C && OF @@ -674,6 +684,13 @@ config TOUCHSCREEN_MIGOR To compile this driver as a module, choose M here: the module will be called migor_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. + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d9a53317f05a..ae4876458cfa 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o +obj-$(CONFIG_TOUCHSCREEN_AR1020_I2C) += ar1020-i2c.o obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o @@ -103,3 +104,4 @@ obj-$(CONFIG_TOUCHSCREEN_CT36X_WLD) += vtl/ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o +obj-$(CONFIG_TOUCHSCREEN_FUSION_F0710A) += fusion_F0710A.o diff --git a/drivers/input/touchscreen/ar1020-i2c.c b/drivers/input/touchscreen/ar1020-i2c.c new file mode 100644 index 000000000000..1ecfbecf460d --- /dev/null +++ b/drivers/input/touchscreen/ar1020-i2c.c @@ -0,0 +1,500 @@ +/* + * Microchip I2C Touchscreen Driver + * + * Copyright (c) 2011 Microchip Technology, Inc. + * + * http://www.microchip.com/mtouch + */ + +/* + * 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/input.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kobject.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> + +/* The private data structure that is referenced within the I2C bus driver */ +struct ar1020_i2c_priv { + struct i2c_client *client; + struct input_dev *input; + int irq; + int testCount; + int command_pending; + int use_count; + int button; + struct delayed_work reenable_work; +}; + +/* + * These are all the sysfs variables used to store and retrieve information + * from a user-level application + */ +static char sendBuffer[100]; +static char receiveBuffer[100]; +static int commandDataPending; + +/* + * These variables allows the IRQ to be specified via a module parameter + * or kernel parameter. To configuration of these value, please see + * driver documentation. + */ +static int touchIRQ; + +module_param(touchIRQ, int, S_IRUGO); + + +/* + * Since the reference to private data is stored within the I2C + * bus driver, we will store another reference within this driver + * so the sysfs related function may also access this data + */ +struct ar1020_i2c_priv *g_priv; + +/* + * Display value of "commandDataPending" variable to application that is + * requesting it's value. + */ +static ssize_t commandDataPending_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", commandDataPending); +} + +/* + * Save value to "commandDataPending" variable from application that is + * requesting this. + */ +static ssize_t commandDataPending_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + sscanf(buf, "%d", &commandDataPending); + return count; +} + +static struct kobj_attribute commandDataPending_attribute = + __ATTR(commandDataPending, 0666, commandDataPending_show, + commandDataPending_store); + + +/* + * Display value of "receiveBuffer" variable to application that is + * requesting it's value. + */ +static ssize_t receiveBuffer_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + /* receive data is no longer pending */ + commandDataPending = 0; + return sprintf(buf, "%s", receiveBuffer); +} + +/* + * Save value to "receiveBuffer" variable from application that is + * requesting this. + */ +static ssize_t receiveBuffer_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + return snprintf(receiveBuffer, sizeof(receiveBuffer), "%s", buf); +} + +static struct kobj_attribute receiveBuffer_attribute = + __ATTR(receiveBuffer, 0666, receiveBuffer_show, receiveBuffer_store); + +/* + * Display value of "sendBuffer" variable to application that is + * requesting it's value. + */ +static ssize_t sendBuffer_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", sendBuffer); +} + +/* + * Save value to "sendBuffer" variable from application that is + * requesting this. + */ +static ssize_t sendBuffer_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int data[8]; + char buff[8]; + int cnt; + int i; + struct ar1020_i2c_priv *priv = g_priv; + + commandDataPending = 0; + + cnt = sscanf(buf, "%x %x %x %x %x %x %x %x", &data[0], &data[1], + &data[2], &data[3], &data[4], &data[5], &data[6], &data[7]); + + pr_debug("AR1020 I2C: Processed %d bytes.\n", cnt); + + /* Verify command string to send to controller is valid */ + if (cnt < 3) + pr_info("AR1020 I2C: Insufficient command bytes to process.\n"); + else if (data[0] != 0x0) + pr_info("AR1020 I2C: Leading zero required when sending I2C commands.\n"); + else if (data[1] != 0x55) + pr_info("AR1020 I2C: Invalid header byte (0x55 expected).\n"); + else if (data[2] != (cnt - 3)) + pr_info("AR1020 I2C: Number of command bytes specified not " + "valid for current string.\n"); + + strcpy(sendBuffer, ""); + pr_debug("AR1020 I2C: sending command bytes: "); + for (i = 0; i < cnt; i++) { + buff[i] = (char)data[i]; + pr_debug("0x%02x ", data[i]); + } + pr_debug("\n"); + + if (priv) { + priv->command_pending = 1; + i2c_master_send(priv->client, buff, cnt); + } + + return snprintf(sendBuffer, sizeof(sendBuffer), "%s", buf); +} + +static struct kobj_attribute sendBuffer_attribute = + __ATTR(sendBuffer, 0666, sendBuffer_show, sendBuffer_store); + +/* + * Create a group of calibration attributes so we may work with them + * as a set. + */ +static struct attribute *attrs[] = { + &commandDataPending_attribute.attr, + &receiveBuffer_attribute.attr, + &sendBuffer_attribute.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static struct kobject *ar1020_kobj; + +static void irq_reenable_work(struct work_struct *work) +{ + struct ar1020_i2c_priv *priv = container_of(work, + struct ar1020_i2c_priv, reenable_work.work); + + enable_irq(priv->irq); +} + +/* + * When the controller interrupt is asserted, this function is scheduled + * to be called to read the controller data. + */ +static irqreturn_t touch_irq_handler_func(int irq, void *dev_id) +{ + struct ar1020_i2c_priv *priv = (struct ar1020_i2c_priv *)dev_id; + int i; + int cnt; + unsigned char packet[9]; + int x; + int y; + int button; + + for (i = 0; i < 5; i++) + packet[i] = 0; + + /* + * We want to ensure we only read packets when we are not in the middle + * of command communication. Disable command mode after receiving + * command response to resume receiving packets. + */ + cnt = i2c_master_recv(priv->client, packet, + (priv->command_pending) ? 9 : 5); + + if (cnt <= 0) + goto error; + + if (packet[0] == 0x55) { + unsigned char *p = receiveBuffer; + unsigned char *pend = p + sizeof(receiveBuffer) - 2; + /* process up to 9 bytes */ + strcpy(receiveBuffer, ""); + + priv->command_pending = 0; + + if (packet[1] > 6) + pr_info("AR1020 I2C: invalid byte count\n"); + else if (cnt > packet[1] + 2) + cnt = packet[1] + 2; + + for (i = 0; i < cnt; i++) { + int ret = snprintf(p, pend - p, " 0x%02x", packet[i]); + if (ret <= 0) + break; + p += ret; + if (p >= pend) + break; + } + *p++ = '\n'; + *p++ = 0; + pr_info("AR1020 I2C: command response: %s", receiveBuffer); + commandDataPending = 1; + return IRQ_HANDLED; + } + + /* + * Decode packets of data from a device path using AR1XXX protocol. + * Data format, 5 bytes: SYNC, DATA1, DATA2, DATA3, DATA4 + * SYNC [7:0]: 1,0,0,0,0,TOUCHSTATUS[0:0] + * DATA1[7:0]: 0,X-LOW[6:0] + * DATA2[7:0]: 0,X-HIGH[4:0] + * DATA3[7:0]: 0,Y-LOW[6:0] + * DATA4[7:0]: 0,Y-HIGH[4:0] + * + * TOUCHSTATUS: 0 = Touch up, 1 = Touch down + */ + if ((packet[0] & 0xfe) != 0x80) { + pr_err("AR1020 I2C: 1st Touch byte not valid. Value: 0x%02x\n", + packet[0]); + goto error; /* irq line may be shorted high, let's delay */ + } + for (i = 1; i < 5; i++) { + /* verify byte is valid for current index */ + if (0x80 & packet[i]) { + pr_err("AR1020 I2C: Touch byte not valid. Value: " + "0x%02x Index: 0x%02x\n", packet[i], i); + goto error; + } + } + x = ((packet[2] & 0x1f) << 7) | (packet[1] & 0x7f); + y = ((packet[4] & 0x1f) << 7) | (packet[3] & 0x7f); + button = (packet[0] & 1); + + if (!button && !priv->button) + return IRQ_HANDLED; + + priv->button = button; + input_report_abs(priv->input, ABS_X, x); + input_report_abs(priv->input, ABS_Y, y); + input_report_key(priv->input, BTN_TOUCH, button); + input_sync(priv->input); + return IRQ_HANDLED; +error: + disable_irq_nosync(priv->irq); + schedule_delayed_work(&priv->reenable_work, 100); + return IRQ_HANDLED; +} + +static int ar1020_i2c_open(struct input_dev *idev) +{ + struct ar1020_i2c_priv *priv = input_get_drvdata(idev); + + if (priv->use_count++ == 0) + enable_irq(priv->irq); + return 0; +} + +static void ar1020_i2c_close(struct input_dev *idev) +{ + struct ar1020_i2c_priv *priv = input_get_drvdata(idev); + + if (--priv->use_count == 0) + disable_irq(priv->irq); +} + +/* + * After the kernel's platform specific source files have been modified to + * reference the "ar1020_i2c" driver, this function will then be called. + * This function needs to be called to finish registering the driver. + */ +static int ar1020_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ar1020_i2c_priv *priv = NULL; + struct input_dev *input_dev = NULL; + int err = 0; + int irq; + + pr_info("%s: begin\n", __func__); + + if (!client) { + pr_err("AR1020 I2C: client pointer is NULL\n"); + err = -EINVAL; + goto error; + } + + irq = client->irq; + if (touchIRQ) + irq = touchIRQ; + + if (!irq) { + pr_err("AR1020 I2C: no IRQ set for touch controller\n"); + err = -EINVAL; + goto error; + } + + priv = kzalloc(sizeof(struct ar1020_i2c_priv), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!priv) { + pr_err("AR1020 I2C: kzalloc error\n"); + err = -ENOMEM; + goto error; + } + + if (!input_dev) { + pr_err("AR1020 I2C: input allocate error\n"); + err = -ENOMEM; + goto error; + } + + priv->client = client; + priv->irq = irq; + priv->input = input_dev; + INIT_DELAYED_WORK(&priv->reenable_work, irq_reenable_work); + + input_dev->name = "AR1020 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + + input_dev->open = ar1020_i2c_open; + input_dev->close = ar1020_i2c_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, 4095, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 4095, 0, 0); + input_set_drvdata(input_dev, priv); + err = input_register_device(input_dev); + if (err) { + pr_err("AR1020 I2C: error registering input device\n"); + goto error; + } + + /* set type and register gpio pin as our interrupt */ + err = request_threaded_irq(priv->irq, NULL, touch_irq_handler_func, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "AR1020 I2C IRQ", + priv); + if (err < 0) + goto error1; + disable_irq(priv->irq); /* wait for open */ + i2c_set_clientdata(client, priv); + /* + * save pointer so sysfs helper functions may also have access + * to private data + */ + g_priv = priv; + return 0; + +error1: + input_unregister_device(input_dev); +error: + if (input_dev) + input_free_device(input_dev); + + kfree(priv); + return err; + +} + +/* + * Unregister/remove the kernel driver from memory. + */ +static int ar1020_i2c_remove(struct i2c_client *client) +{ + struct ar1020_i2c_priv *priv = + (struct ar1020_i2c_priv *)i2c_get_clientdata(client); + + free_irq(priv->irq, priv); + input_unregister_device(priv->input); + kfree(priv); + g_priv = NULL; + return 0; +} + +/* This structure describe a list of supported slave chips */ +static const struct i2c_device_id ar1020_i2c_id[] = { + { "ar1020_i2c", 0 }, + { } +}; + +/* + * This is the initial set of information the kernel has + * before probing drivers on the system + */ +static struct i2c_driver ar1020_i2c_driver = { + .driver = { + .name = "ar1020_i2c", + }, + .probe = ar1020_i2c_probe, + .remove = ar1020_i2c_remove, + /* + * suspend/resume functions not needed since controller automatically + * put's itself to sleep mode after configurable short period of time + */ + .suspend = NULL, + .resume = NULL, + .id_table = ar1020_i2c_id, +}; + +/* + * This function is called during startup even if the platform specific + * files have not been setup yet. + */ +static int __init ar1020_i2c_init(void) +{ + int retval; + + pr_debug("AR1020 I2C: ar1020_i2c_init: begin\n"); + strcpy(receiveBuffer, ""); + strcpy(sendBuffer, ""); + + /* + * Creates a kobject "ar1020" that appears as a sub-directory + * under "/sys/kernel". + */ + ar1020_kobj = kobject_create_and_add("ar1020", kernel_kobj); + if (!ar1020_kobj) { + pr_err("AR1020 I2C: cannot create kobject\n"); + return -ENOMEM; + } + + /* Create the files associated with this kobject */ + retval = sysfs_create_group(ar1020_kobj, &attr_group); + if (retval) { + pr_err("AR1020 I2C: error registering ar1020-i2c driver's sysfs interface\n"); + kobject_put(ar1020_kobj); + } + + return i2c_add_driver(&ar1020_i2c_driver); +} + +/* + * This function is called after ar1020_i2c_remove() immediately before + * being removed from the kernel. + */ +static void __exit ar1020_i2c_exit(void) +{ + pr_debug("AR1020 I2C: ar1020_i2c_exit begin\n"); + kobject_put(ar1020_kobj); + i2c_del_driver(&ar1020_i2c_driver); +} + +MODULE_AUTHOR("Steve Grahovac <steve.grahovac@microchip.com>"); +MODULE_DESCRIPTION("AR1020 touchscreen I2C bus driver"); +MODULE_LICENSE("GPL"); + +module_init(ar1020_i2c_init); +module_exit(ar1020_i2c_exit); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c2fb0236a47c..eff12ea59470 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -28,6 +28,7 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/slab.h> +#include <linux/gpio/consumer.h> #include <asm/unaligned.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> @@ -192,6 +193,8 @@ enum t100_type { /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_GPIO_TIME 20 /* msec */ +#define MXT_RESET_INVALID_CHG 100 /* msec */ #define MXT_RESET_TIME 200 /* msec */ #define MXT_RESET_TIMEOUT 3000 /* msec */ #define MXT_CRC_TIMEOUT 1000 /* msec */ @@ -300,6 +303,7 @@ struct mxt_data { u8 multitouch; struct t7_config t7_cfg; struct mxt_dbg dbg; + struct gpio_desc *reset_gpio; /* Cached parameters from object table */ u16 T5_address; @@ -1194,7 +1198,7 @@ static int mxt_soft_reset(struct mxt_data *data) return ret; /* Ignore CHG line for 100ms after reset */ - msleep(100); + msleep(MXT_RESET_INVALID_CHG); mxt_acquire_irq(data); @@ -3126,11 +3130,9 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) if (IS_ERR(pdata)) return PTR_ERR(pdata); - data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "Failed to allocate memory\n"); + data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL); + if (!data) return -ENOMEM; - } snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); @@ -3144,19 +3146,34 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) init_completion(&data->reset_completion); init_completion(&data->crc_completion); - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - pdata->irqflags | IRQF_ONESHOT, - client->name, data); + data->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(data->reset_gpio)) { + error = PTR_ERR(data->reset_gpio); + dev_err(&client->dev, "Failed to get reset gpio: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mxt_interrupt, + pdata->irqflags | IRQF_ONESHOT, + client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_mem; + return error; } disable_irq(client->irq); + if (data->reset_gpio) { + msleep(MXT_RESET_GPIO_TIME); + gpiod_set_value(data->reset_gpio, 1); + msleep(MXT_RESET_INVALID_CHG); + } + error = mxt_initialize(data); if (error) - goto err_free_irq; + return error; error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { @@ -3170,10 +3187,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) err_free_object: mxt_free_input_device(data); mxt_free_object_table(data); -err_free_irq: - free_irq(client->irq, data); -err_free_mem: - kfree(data); return error; } @@ -3181,11 +3194,10 @@ static int mxt_remove(struct i2c_client *client) { struct mxt_data *data = i2c_get_clientdata(client); + disable_irq(data->irq); sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); - free_irq(data->irq, data); mxt_free_input_device(data); mxt_free_object_table(data); - kfree(data); return 0; } diff --git a/drivers/input/touchscreen/fusion_F0710A.c b/drivers/input/touchscreen/fusion_F0710A.c new file mode 100644 index 000000000000..779c3e83a402 --- /dev/null +++ b/drivers/input/touchscreen/fusion_F0710A.c @@ -0,0 +1,510 @@ +/* + * "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 <linux/input/mt.h> +#include <linux/slab.h> +#include <linux/of_gpio.h> + + +#include "fusion_F0710A.h" + +#define DRV_NAME "fusion_F0710A" +#define MAX_TOUCHES 2 + +static struct fusion_F0710A_data fusion_F0710A; + +static unsigned short normal_i2c[] = { fusion_F0710A_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +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) +{ + 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)); +} + +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); + set_bit(EV_SYN, dev->evbit); + set_bit(BTN_TOUCH, dev->keybit); + + input_mt_init_slots(dev, MAX_TOUCHES, 0); + 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_PRESSURE, 0, 255, 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; +} + +static void fusion_F0710A_reset(void) +{ + /* Generate a 0 => 1 edge explicitly, and wait for startup... */ + gpio_set_value(fusion_F0710A.gpio_reset, 0); + msleep(10); + gpio_set_value(fusion_F0710A.gpio_reset, 1); + /* Wait for startup (up to 125ms according to datasheet) */ + msleep(125); +} + +#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_warn(&fusion_F0710A.client->dev, + "Write complete failed(%d): %d. Resetting controller...\n", i, ret); + fusion_F0710A_reset(); + } + } + + 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; + + 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.tid1) { + input_mt_slot(dev, fusion_F0710A.tid1 - 1); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, fusion_F0710A.tip1); + if (fusion_F0710A.tip1) { + input_report_abs(dev, ABS_MT_POSITION_X, fusion_F0710A.x1); + input_report_abs(dev, ABS_MT_POSITION_Y, fusion_F0710A.y1); + input_report_abs(dev, ABS_MT_PRESSURE, fusion_F0710A.z1); + } + } + + if (fusion_F0710A.tid2) { + input_mt_slot(dev, fusion_F0710A.tid2 - 1); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, fusion_F0710A.tip2); + if (fusion_F0710A.tip2) { + input_report_abs(dev, ABS_MT_POSITION_X, fusion_F0710A.x2); + input_report_abs(dev, ABS_MT_POSITION_Y, fusion_F0710A.y2); + input_report_abs(dev, ABS_MT_PRESSURE, fusion_F0710A.z2); + } + } + + input_mt_report_pointer_emulation(dev, false); + 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 of_fusion_F0710A_get_pins(struct device_node *np, + unsigned int *int_pin, unsigned int *reset_pin) +{ + if (of_gpio_count(np) < 2) + return -ENODEV; + + *int_pin = of_get_gpio(np, 0); + *reset_pin = of_get_gpio(np, 1); + + if (!gpio_is_valid(*int_pin) || !gpio_is_valid(*reset_pin)) { + pr_err("%s: invalid GPIO pins, int=%d/reset=%d\n", + np->full_name, *int_pin, *reset_pin); + return -ENODEV; + } + + return 0; +} + +static int fusion_F0710A_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct device_node *np = i2c->dev.of_node; + struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data; + int ret; + u8 ver_product, ver_id; + u32 version; + + if (np != NULL) { + pdata = i2c->dev.platform_data = + devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) { + dev_err(&i2c->dev, "No platform data for Fusion driver\n"); + return -ENODEV; + } + /* the dtb did the pinmuxing for us */ + pdata->pinmux_fusion_pins = NULL; + ret = of_fusion_F0710A_get_pins(i2c->dev.of_node, + &pdata->gpio_int, &pdata->gpio_reset); + if (ret) + return ret; + } + else 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, "Fusion pen down interrupt") == 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, "Fusion reset") == 0) && + (gpio_direction_output(pdata->gpio_reset, 1) == 0)) { + fusion_F0710A.gpio_reset = pdata->gpio_reset; + fusion_F0710A_reset(); + 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); + irq_set_irq_type(i2c->irq, IRQ_TYPE_LEVEL_HIGH); + + 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 of_device_id fusion_F0710A_dt_ids[] = { + { + .compatible = "touchrevolution,fusion-f0710a", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, fusion_F0710A_dt_ids); + +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, + .of_match_table = fusion_F0710A_dt_ids, + }, + .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..3e5af9e72b78 --- /dev/null +++ b/drivers/input/touchscreen/fusion_F0710A.h @@ -0,0 +1,88 @@ +/* + * "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; + int gpio_reset; + 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; +}; + diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 2a78e27b4495..05674e0e233f 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -49,17 +49,6 @@ #define STMPE_IRQ_TOUCH_DET 0 -#define SAMPLE_TIME(x) ((x & 0xf) << 4) -#define MOD_12B(x) ((x & 0x1) << 3) -#define REF_SEL(x) ((x & 0x1) << 1) -#define ADC_FREQ(x) (x & 0x3) -#define AVE_CTRL(x) ((x & 0x3) << 6) -#define DET_DELAY(x) ((x & 0x7) << 3) -#define SETTLING(x) (x & 0x7) -#define FRACTION_Z(x) (x & 0x7) -#define I_DRIVE(x) (x & 0x1) -#define OP_MODE(x) ((x & 0x7) << 1) - #define STMPE_TS_NAME "stmpe-ts" #define XY_MASK 0xfff |