summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig9
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/evdev.c53
-rw-r--r--drivers/input/keyreset.c239
-rw-r--r--drivers/input/misc/Kconfig16
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/gpio_axis.c192
-rw-r--r--drivers/input/misc/gpio_event.c228
-rw-r--r--drivers/input/misc/gpio_input.c390
-rw-r--r--drivers/input/misc/gpio_matrix.c441
-rw-r--r--drivers/input/misc/gpio_output.c97
-rw-r--r--drivers/input/misc/keychord.c391
-rw-r--r--drivers/input/touchscreen/Kconfig8
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/atmxt.c4154
-rw-r--r--drivers/input/touchscreen/atmxt.h197
16 files changed, 6419 insertions, 0 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 38b523a1ece..3ad49c1f0a3 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -174,6 +174,15 @@ config INPUT_APMPOWER
To compile this driver as a module, choose M here: the
module will be called apm-power.
+config INPUT_KEYRESET
+ tristate "Reset key"
+ depends on INPUT
+ ---help---
+ Say Y here if you want to reboot when some keys are pressed;
+
+ To compile this driver as a module, choose M here: the
+ module will be called keyreset.
+
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f631497..191ea43d080 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index f0f8928b3c8..23425d7922c 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -24,6 +24,7 @@
#include <linux/major.h>
#include <linux/device.h>
#include <linux/cdev.h>
+#include <linux/wakelock.h>
#include "input-compat.h"
struct evdev {
@@ -44,6 +45,9 @@ struct evdev_client {
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+ struct wake_lock wake_lock;
+ bool use_wake_lock;
+ char name[28];
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
@@ -71,10 +75,14 @@ static void __pass_event(struct evdev_client *client,
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
+ if (client->use_wake_lock)
+ wake_unlock(&client->wake_lock);
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
+ if (client->use_wake_lock)
+ wake_lock(&client->wake_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
}
@@ -289,6 +297,8 @@ static int evdev_release(struct inode *inode, struct file *file)
mutex_unlock(&evdev->mutex);
evdev_detach_client(evdev, client);
+ if (client->use_wake_lock)
+ wake_lock_destroy(&client->wake_lock);
kfree(client);
evdev_close_device(evdev);
@@ -320,6 +330,8 @@ static int evdev_open(struct inode *inode, struct file *file)
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
+ snprintf(client->name, sizeof(client->name), "%s-%d",
+ dev_name(&evdev->dev), task_tgid_vnr(current));
client->evdev = evdev;
evdev_attach_client(evdev, client);
@@ -386,6 +398,9 @@ static int evdev_fetch_next_event(struct evdev_client *client,
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
+ if (client->use_wake_lock &&
+ client->packet_head == client->tail)
+ wake_unlock(&client->wake_lock);
}
spin_unlock_irq(&client->buffer_lock);
@@ -674,6 +689,35 @@ static int evdev_handle_mt_request(struct input_dev *dev,
return 0;
}
+static int evdev_enable_suspend_block(struct evdev *evdev,
+ struct evdev_client *client)
+{
+ if (client->use_wake_lock)
+ return 0;
+
+ spin_lock_irq(&client->buffer_lock);
+ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
+ client->use_wake_lock = true;
+ if (client->packet_head != client->tail)
+ wake_lock(&client->wake_lock);
+ spin_unlock_irq(&client->buffer_lock);
+ return 0;
+}
+
+static int evdev_disable_suspend_block(struct evdev *evdev,
+ struct evdev_client *client)
+{
+ if (!client->use_wake_lock)
+ return 0;
+
+ spin_lock_irq(&client->buffer_lock);
+ client->use_wake_lock = false;
+ wake_lock_destroy(&client->wake_lock);
+ spin_unlock_irq(&client->buffer_lock);
+
+ return 0;
+}
+
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
@@ -755,6 +799,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
case EVIOCSKEYCODE_V2:
return evdev_handle_set_keycode_v2(dev, p);
+
+ case EVIOCGSUSPENDBLOCK:
+ return put_user(client->use_wake_lock, ip);
+
+ case EVIOCSSUSPENDBLOCK:
+ if (p)
+ return evdev_enable_suspend_block(evdev, client);
+ else
+ return evdev_disable_suspend_block(evdev, client);
}
size = _IOC_SIZE(cmd);
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
new file mode 100644
index 00000000000..36208fe0baa
--- /dev/null
+++ b/drivers/input/keyreset.c
@@ -0,0 +1,239 @@
+/* drivers/input/keyreset.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/keyreset.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+
+struct keyreset_state {
+ struct input_handler input_handler;
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long key[BITS_TO_LONGS(KEY_CNT)];
+ spinlock_t lock;
+ int key_down_target;
+ int key_down;
+ int key_up;
+ int restart_disabled;
+ int (*reset_fn)(void);
+};
+
+int restart_requested;
+static void deferred_restart(struct work_struct *dummy)
+{
+ restart_requested = 2;
+ sys_sync();
+ restart_requested = 3;
+ kernel_restart(NULL);
+}
+static DECLARE_WORK(restart_work, deferred_restart);
+
+static void keyreset_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ unsigned long flags;
+ struct keyreset_state *state = handle->private;
+
+ if (type != EV_KEY)
+ return;
+
+ if (code >= KEY_MAX)
+ return;
+
+ if (!test_bit(code, state->keybit))
+ return;
+
+ spin_lock_irqsave(&state->lock, flags);
+ if (!test_bit(code, state->key) == !value)
+ goto done;
+ __change_bit(code, state->key);
+ if (test_bit(code, state->upbit)) {
+ if (value) {
+ state->restart_disabled = 1;
+ state->key_up++;
+ } else
+ state->key_up--;
+ } else {
+ if (value)
+ state->key_down++;
+ else
+ state->key_down--;
+ }
+ if (state->key_down == 0 && state->key_up == 0)
+ state->restart_disabled = 0;
+
+ pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
+ state->key_down, state->key_up, state->restart_disabled);
+
+ if (value && !state->restart_disabled &&
+ state->key_down == state->key_down_target) {
+ state->restart_disabled = 1;
+ if (restart_requested)
+ panic("keyboard reset failed, %d", restart_requested);
+ if (state->reset_fn) {
+ restart_requested = state->reset_fn();
+ } else {
+ pr_info("keyboard reset\n");
+ schedule_work(&restart_work);
+ restart_requested = 1;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static int keyreset_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i;
+ int ret;
+ struct input_handle *handle;
+ struct keyreset_state *state =
+ container_of(handler, struct keyreset_state, input_handler);
+
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "keyreset";
+ handle->private = state;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("using input dev %s for key reset\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keyreset_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id keyreset_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(input, keyreset_ids);
+
+static int keyreset_probe(struct platform_device *pdev)
+{
+ int ret;
+ int key, *keyp;
+ struct keyreset_state *state;
+ struct keyreset_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ spin_lock_init(&state->lock);
+ keyp = pdata->keys_down;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ state->key_down_target++;
+ __set_bit(key, state->keybit);
+ }
+ if (pdata->keys_up) {
+ keyp = pdata->keys_up;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ __set_bit(key, state->keybit);
+ __set_bit(key, state->upbit);
+ }
+ }
+
+ if (pdata->reset_fn)
+ state->reset_fn = pdata->reset_fn;
+
+ state->input_handler.event = keyreset_event;
+ state->input_handler.connect = keyreset_connect;
+ state->input_handler.disconnect = keyreset_disconnect;
+ state->input_handler.name = KEYRESET_NAME;
+ state->input_handler.id_table = keyreset_ids;
+ ret = input_register_handler(&state->input_handler);
+ if (ret) {
+ kfree(state);
+ return ret;
+ }
+ platform_set_drvdata(pdev, state);
+ return 0;
+}
+
+int keyreset_remove(struct platform_device *pdev)
+{
+ struct keyreset_state *state = platform_get_drvdata(pdev);
+ input_unregister_handler(&state->input_handler);
+ kfree(state);
+ return 0;
+}
+
+
+struct platform_driver keyreset_driver = {
+ .driver.name = KEYRESET_NAME,
+ .probe = keyreset_probe,
+ .remove = keyreset_remove,
+};
+
+static int __init keyreset_init(void)
+{
+ return platform_driver_register(&keyreset_driver);
+}
+
+static void __exit keyreset_exit(void)
+{
+ return platform_driver_unregister(&keyreset_driver);
+}
+
+module_init(keyreset_init);
+module_exit(keyreset_exit);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index bb698e1f9e4..4abf046e30b 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -299,6 +299,17 @@ config INPUT_ATI_REMOTE2
To compile this driver as a module, choose M here: the module will be
called ati_remote2.
+config INPUT_KEYCHORD
+ tristate "Key chord input driver support"
+ help
+ Say Y here if you want to enable the key chord driver
+ accessible at /dev/keychord. This driver can be used
+ for receiving notifications when client specified key
+ combinations are pressed.
+
+ To compile this driver as a module, choose M here: the
+ module will be called keychord.
+
config INPUT_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control"
depends on USB_ARCH_HAS_HCD
@@ -434,6 +445,11 @@ config INPUT_SGI_BTNS
To compile this driver as a module, choose M here: the
module will be called sgi_btns.
+config INPUT_GPIO
+ tristate "GPIO driver support"
+ help
+ Say Y here if you want to support gpio based keys, wheels etc...
+
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index d7fc17f11d7..6b0e8a67772 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -28,9 +28,11 @@ obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
+obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c
new file mode 100644
index 00000000000..0acf4a576f5
--- /dev/null
+++ b/drivers/input/misc/gpio_axis.c
@@ -0,0 +1,192 @@
+/* drivers/input/misc/gpio_axis.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+struct gpio_axis_state {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_axis_info *info;
+ uint32_t pos;
+};
+
+uint16_t gpio_axis_4bit_gray_map_table[] = {
+ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */
+ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */
+ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */
+ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */
+ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */
+ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */
+ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */
+ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */
+};
+uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_4bit_gray_map_table[in];
+}
+
+uint16_t gpio_axis_5bit_singletrack_map_table[] = {
+ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */
+ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */
+ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */
+ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */
+ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */
+ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */
+ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */
+ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */
+ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */
+ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */
+};
+uint16_t gpio_axis_5bit_singletrack_map(
+ struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_5bit_singletrack_map_table[in];
+}
+
+static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
+{
+ struct gpio_event_axis_info *ai = as->info;
+ int i;
+ int change;
+ uint16_t state = 0;
+ uint16_t pos;
+ uint16_t old_pos = as->pos;
+ for (i = ai->count - 1; i >= 0; i--)
+ state = (state << 1) | gpio_get_value(ai->gpio[i]);
+ pos = ai->map(ai, state);
+ if (ai->flags & GPIOEAF_PRINT_RAW)
+ pr_info("axis %d-%d raw %x, pos %d -> %d\n",
+ ai->type, ai->code, state, old_pos, pos);
+ if (report && pos != old_pos) {
+ if (ai->type == EV_REL) {
+ change = (ai->decoded_size + pos - old_pos) %
+ ai->decoded_size;
+ if (change > ai->decoded_size / 2)
+ change -= ai->decoded_size;
+ if (change == ai->decoded_size / 2) {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d unknown direction, "
+ "pos %d -> %d\n", ai->type,
+ ai->code, old_pos, pos);
+ change = 0; /* no closest direction */
+ }
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d change %d\n",
+ ai->type, ai->code, change);
+ input_report_rel(as->input_devs->dev[ai->dev],
+ ai->code, change);
+ } else {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d now %d\n",
+ ai->type, ai->code, pos);
+ input_event(as->input_devs->dev[ai->dev],
+ ai->type, ai->code, pos);
+ }
+ input_sync(as->input_devs->dev[ai->dev]);
+ }
+ as->pos = pos;
+}
+
+static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_axis_state *as = dev_id;
+ gpio_event_update_axis(as, 1);
+ return IRQ_HANDLED;
+}
+
+int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ int irq;
+ struct gpio_event_axis_info *ai;
+ struct gpio_axis_state *as;
+
+ ai = container_of(info, struct gpio_event_axis_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ for (i = 0; i < ai->count; i++)
+ disable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ for (i = 0; i < ai->count; i++)
+ enable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ *data = as = kmalloc(sizeof(*as), GFP_KERNEL);
+ if (as == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_axis_state_failed;
+ }
+ as->input_devs = input_devs;
+ as->info = ai;
+ if (ai->dev >= input_devs->count) {
+ pr_err("gpio_event_axis: bad device index %d >= %d "
+ "for %d:%d\n", ai->dev, input_devs->count,
+ ai->type, ai->code);
+ ret = -EINVAL;
+ goto err_bad_device_index;
+ }
+
+ input_set_capability(input_devs->dev[ai->dev],
+ ai->type, ai->code);
+ if (ai->type == EV_ABS) {
+ input_set_abs_params(input_devs->dev[ai->dev], ai->code,
+ 0, ai->decoded_size - 1, 0, 0);
+ }
+ for (i = 0; i < ai->count; i++) {
+ ret = gpio_request(ai->gpio[i], "gpio_event_axis");
+ if (ret < 0)
+ goto err_request_gpio_failed;
+ ret = gpio_direction_input(ai->gpio[i]);
+ if (ret < 0)
+ goto err_gpio_direction_input_failed;
+ ret = irq = gpio_to_irq(ai->gpio[i]);
+ if (ret < 0)
+ goto err_get_irq_num_failed;
+ ret = request_irq(irq, gpio_axis_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "gpio_event_axis", as);
+ if (ret < 0)
+ goto err_request_irq_failed;
+ }
+ gpio_event_update_axis(as, 0);
+ return 0;
+ }
+
+ ret = 0;
+ as = *data;
+ for (i = ai->count - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ai->gpio[i]), as);
+err_request_irq_failed:
+err_get_irq_num_failed:
+err_gpio_direction_input_failed:
+ gpio_free(ai->gpio[i]);
+err_request_gpio_failed:
+ ;
+ }
+err_bad_device_index:
+ kfree(as);
+ *data = NULL;
+err_alloc_axis_state_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
new file mode 100644
index 00000000000..90f07eba3ce
--- /dev/null
+++ b/drivers/input/misc/gpio_event.c
@@ -0,0 +1,228 @@
+/* drivers/input/misc/gpio_event.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct gpio_event {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_platform_data *info;
+ void *state[0];
+};
+
+static int gpio_input_event(
+ struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ int i;
+ int devnr;
+ int ret = 0;
+ int tmp_ret;
+ struct gpio_event_info **ii;
+ struct gpio_event *ip = input_get_drvdata(dev);
+
+ for (devnr = 0; devnr < ip->input_devs->count; devnr++)
+ if (ip->input_devs->dev[devnr] == dev)
+ break;
+ if (devnr == ip->input_devs->count) {
+ pr_err("gpio_input_event: unknown device %p\n", dev);
+ return -EIO;
+ }
+
+ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->event) {
+ tmp_ret = (*ii)->event(ip->input_devs, *ii,
+ &ip->state[i],
+ devnr, type, code, value);
+ if (tmp_ret)
+ ret = tmp_ret;
+ }
+ }
+ return ret;
+}
+
+static int gpio_event_call_all_func(struct gpio_event *ip, int func)
+{
+ int i;
+ int ret;
+ struct gpio_event_info **ii;
+
+ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
+ ii = ip->info->info;
+ for (i = 0; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->func == NULL) {
+ ret = -ENODEV;
+ pr_err("gpio_event_probe: Incomplete pdata, "
+ "no function\n");
+ goto err_no_func;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend)
+ continue;
+ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i],
+ func);
+ if (ret) {
+ pr_err("gpio_event_probe: function failed\n");
+ goto err_func_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ i = ip->info->info_count;
+ ii = ip->info->info + i;
+ while (i > 0) {
+ i--;
+ ii--;
+ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend)
+ continue;
+ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1);
+err_func_failed:
+err_no_func:
+ ;
+ }
+ return ret;
+}
+
+static void __maybe_unused gpio_event_suspend(struct gpio_event *ip)
+{
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
+ if (ip->info->power)
+ ip->info->power(ip->info, 0);
+}
+
+static void __maybe_unused gpio_event_resume(struct gpio_event *ip)
+{
+ if (ip->info->power)
+ ip->info->power(ip->info, 1);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
+}
+
+static int gpio_event_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_event *ip;
+ struct gpio_event_platform_data *event_info;
+ int dev_count = 1;
+ int i;
+ int registered = 0;
+
+ event_info = pdev->dev.platform_data;
+ if (event_info == NULL) {
+ pr_err("gpio_event_probe: No pdata\n");
+ return -ENODEV;
+ }
+ if ((!event_info->name && !event_info->names[0]) ||
+ !event_info->info || !event_info->info_count) {
+ pr_err("gpio_event_probe: Incomplete pdata\n");
+ return -ENODEV;
+ }
+ if (!event_info->name)
+ while (event_info->names[dev_count])
+ dev_count++;
+ ip = kzalloc(sizeof(*ip) +
+ sizeof(ip->state[0]) * event_info->info_count +
+ sizeof(*ip->input_devs) +
+ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL);
+ if (ip == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ ip->input_devs = (void*)&ip->state[event_info->info_count];
+ platform_set_drvdata(pdev, ip);
+
+ for (i = 0; i < dev_count; i++) {
+ struct input_dev *input_dev = input_allocate_device();
+ if (input_dev == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: "
+ "Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ input_set_drvdata(input_dev, ip);
+ input_dev->name = event_info->name ?
+ event_info->name : event_info->names[i];
+ input_dev->event = gpio_input_event;
+ ip->input_devs->dev[i] = input_dev;
+ }
+ ip->input_devs->count = dev_count;
+ ip->info = event_info;
+ if (event_info->power)
+ ip->info->power(ip->info, 1);
+
+ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
+ if (err)
+ goto err_call_all_func_failed;
+
+ for (i = 0; i < dev_count; i++) {
+ err = input_register_device(ip->input_devs->dev[i]);
+ if (err) {
+ pr_err("gpio_event_probe: Unable to register %s "
+ "input device\n", ip->input_devs->dev[i]->name);
+ goto err_input_register_device_failed;
+ }
+ registered++;
+ }
+
+ return 0;
+
+err_input_register_device_failed:
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+err_call_all_func_failed:
+ if (event_info->power)
+ ip->info->power(ip->info, 0);
+ for (i = 0; i < registered; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ for (i = dev_count - 1; i >= registered; i--) {
+ input_free_device(ip->input_devs->dev[i]);
+err_input_dev_alloc_failed:
+ ;
+ }
+ kfree(ip);
+err_kp_alloc_failed:
+ return err;
+}
+
+static int gpio_event_remove(struct platform_device *pdev)
+{
+ struct gpio_event *ip = platform_get_drvdata(pdev);
+ int i;
+
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+ if (ip->info->power)
+ ip->info->power(ip->info, 0);
+ for (i = 0; i < ip->input_devs->count; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ kfree(ip);
+ return 0;
+}
+
+static struct platform_driver gpio_event_driver = {
+ .probe = gpio_event_probe,
+ .remove = gpio_event_remove,
+ .driver = {
+ .name = GPIO_EVENT_DEV_NAME,
+ },
+};
+
+module_platform_driver(gpio_event_driver);
+
+MODULE_DESCRIPTION("GPIO Event Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c
new file mode 100644
index 00000000000..eefd02725af
--- /dev/null
+++ b/drivers/input/misc/gpio_input.c
@@ -0,0 +1,390 @@
+/* drivers/input/misc/gpio_input.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pm_wakeup.h>
+
+enum {
+ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */
+ DEBOUNCE_PRESSED = BIT(1),
+ DEBOUNCE_NOTPRESSED = BIT(2),
+ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */
+ DEBOUNCE_POLL = BIT(4), /* Stable polling state */
+
+ DEBOUNCE_UNKNOWN =
+ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED,
+};
+
+struct gpio_key_state {
+ struct gpio_input_state *ds;
+ uint8_t debounce;
+};
+
+struct gpio_input_state {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_input_info *info;
+ struct hrtimer timer;
+ int use_irq;
+ int debounce_count;
+ spinlock_t irq_lock;
+ struct wakeup_source *ws;
+ struct gpio_key_state key_state[0];
+};
+
+static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
+{
+ int i;
+ int pressed;
+ struct gpio_input_state *ds =
+ container_of(timer, struct gpio_input_state, timer);
+ unsigned gpio_flags = ds->info->flags;
+ unsigned npolarity;
+ int nkeys = ds->info->keymap_size;
+ const struct gpio_event_direct_entry *key_entry;
+ struct gpio_key_state *key_state;
+ unsigned long irqflags;
+ uint8_t debounce;
+ bool sync_needed;
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++)
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+#endif
+ key_entry = ds->info->keymap;
+ key_state = ds->key_state;
+ sync_needed = false;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ debounce = key_state->debounce;
+ if (debounce & DEBOUNCE_WAIT_IRQ)
+ continue;
+ if (key_state->debounce & DEBOUNCE_UNSTABLE) {
+ debounce = key_state->debounce = DEBOUNCE_UNKNOWN;
+ enable_irq(gpio_to_irq(key_entry->gpio));
+ if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) continue debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH);
+ pressed = gpio_get_value(key_entry->gpio) ^ npolarity;
+ if (debounce & DEBOUNCE_POLL) {
+ if (pressed == !(debounce & DEBOUNCE_PRESSED)) {
+ ds->debounce_count++;
+ key_state->debounce = DEBOUNCE_UNKNOWN;
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-"
+ "%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ continue;
+ }
+ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 1\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_PRESSED;
+ continue;
+ }
+ if (!pressed && (debounce & DEBOUNCE_PRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 0\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_NOTPRESSED;
+ continue;
+ }
+ /* key is stable */
+ ds->debounce_count--;
+ if (ds->use_irq)
+ key_state->debounce |= DEBOUNCE_WAIT_IRQ;
+ else
+ key_state->debounce |= DEBOUNCE_POLL;
+ if (gpio_flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
+ "changed to %d\n", ds->info->type,
+ key_entry->code, i, key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ sync_needed = true;
+ }
+ if (sync_needed) {
+ for (i = 0; i < ds->input_devs->count; i++)
+ input_sync(ds->input_devs->dev[i]);
+ }
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+ }
+#endif
+
+ if (ds->debounce_count)
+ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL);
+ else if (!ds->use_irq)
+ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL);
+ else
+ __pm_relax(ds->ws);
+
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_key_state *ks = dev_id;
+ struct gpio_input_state *ds = ks->ds;
+ int keymap_index = ks - ds->key_state;
+ const struct gpio_event_direct_entry *key_entry;
+ unsigned long irqflags;
+ int pressed;
+
+ if (!ds->use_irq)
+ return IRQ_HANDLED;
+
+ key_entry = &ds->info->keymap[keymap_index];
+
+ if (ds->info->debounce_time.tv64) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ks->debounce & DEBOUNCE_WAIT_IRQ) {
+ ks->debounce = DEBOUNCE_UNKNOWN;
+ if (ds->debounce_count++ == 0) {
+ __pm_stay_awake(ds->ws);
+ hrtimer_start(
+ &ds->timer, ds->info->debounce_time,
+ HRTIMER_MODE_REL);
+ }
+ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_event_input_irq_handler: "
+ "key %x-%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ keymap_index, key_entry->gpio);
+ } else {
+ disable_irq_nosync(irq);
+ ks->debounce = DEBOUNCE_UNSTABLE;
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ } else {
+ pressed = gpio_get_value(key_entry->gpio) ^
+ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH);
+ if (ds->info->flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_event_input_irq_handler: key %x-%x, %d "
+ "(%d) changed to %d\n",
+ ds->info->type, key_entry->code, keymap_index,
+ key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ input_sync(ds->input_devs->dev[key_entry->dev]);
+ }
+ return IRQ_HANDLED;
+}
+
+static int gpio_event_input_request_irqs(struct gpio_input_state *ds)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ for (i = 0; i < ds->info->keymap_size; i++) {
+ err = irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_event_input_irq_handler,
+ req_flags, "gpio_keys", &ds->key_state[i]);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: request_irq "
+ "failed for input %d, irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_request_irq_failed;
+ }
+ if (ds->info->info.no_suspend) {
+ err = enable_irq_wake(irq);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: "
+ "enable_irq_wake failed for input %d, "
+ "irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_enable_irq_wake_failed;
+ }
+ }
+ }
+ return 0;
+
+ for (i = ds->info->keymap_size - 1; i >= 0; i--) {
+ irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (ds->info->info.no_suspend)
+ disable_irq_wake(irq);
+err_enable_irq_wake_failed:
+ free_irq(irq, &ds->key_state[i]);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ unsigned long irqflags;
+ struct gpio_event_input_info *di;
+ struct gpio_input_state *ds = *data;
+ char *wlname;
+
+ di = container_of(info, struct gpio_event_input_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ disable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_cancel(&ds->timer);
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ enable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (ktime_to_ns(di->poll_time) <= 0)
+ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC);
+
+ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) *
+ di->keymap_size, GFP_KERNEL);
+ if (ds == NULL) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate private data\n");
+ goto err_ds_alloc_failed;
+ }
+ ds->debounce_count = di->keymap_size;
+ ds->input_devs = input_devs;
+ ds->info = di;
+ wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s",
+ input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "");
+
+ ds->ws = wakeup_source_register(wlname);
+ kfree(wlname);
+ if (!ds->ws) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate wakeup source\n");
+ goto err_ws_failed;
+ }
+
+ spin_lock_init(&ds->irq_lock);
+
+ for (i = 0; i < di->keymap_size; i++) {
+ int dev = di->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_input_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ di->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], di->type,
+ di->keymap[i].code);
+ ds->key_state[i].ds = ds;
+ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
+ }
+
+ for (i = 0; i < di->keymap_size; i++) {
+ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in");
+ if (ret) {
+ pr_err("gpio_event_input_func: gpio_request "
+ "failed for %d\n", di->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_input(di->keymap[i].gpio);
+ if (ret) {
+ pr_err("gpio_event_input_func: "
+ "gpio_direction_input failed for %d\n",
+ di->keymap[i].gpio);
+ goto err_gpio_configure_failed;
+ }
+ }
+
+ ret = gpio_event_input_request_irqs(ds);
+
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ ds->use_irq = ret == 0;
+
+ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s "
+ "mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ ret == 0 ? "interrupt" : "polling");
+
+ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ds->timer.function = gpio_event_input_timer_func;
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ ret = 0;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ hrtimer_cancel(&ds->timer);
+ if (ds->use_irq) {
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+ int irq = gpio_to_irq(di->keymap[i].gpio);
+ if (ds->info->info.no_suspend)
+ disable_irq_wake(irq);
+ free_irq(irq, &ds->key_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+err_gpio_configure_failed:
+ gpio_free(di->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ wakeup_source_unregister(ds->ws);
+err_ws_failed:
+ kfree(ds);
+err_ds_alloc_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
new file mode 100644
index 00000000000..eaa9e89d473
--- /dev/null
+++ b/drivers/input/misc/gpio_matrix.c
@@ -0,0 +1,441 @@
+/* drivers/input/misc/gpio_matrix.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+struct gpio_kp {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_matrix_info *keypad_info;
+ struct hrtimer timer;
+ struct wake_lock wake_lock;
+ int current_output;
+ unsigned int use_irq:1;
+ unsigned int key_state_changed:1;
+ unsigned int last_key_state_changed:1;
+ unsigned int some_keys_pressed:2;
+ unsigned int disabled_irq:1;
+ unsigned long keys_pressed[0];
+};
+
+static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int key_index = out * mi->ninputs + in;
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ __clear_bit(key_index, kp->keys_pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "not cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ }
+}
+
+static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
+{
+ int rv = 0;
+ int key_index;
+
+ key_index = out * kp->keypad_info->ninputs + in;
+ while (out < kp->keypad_info->noutputs) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ rv = 1;
+ clear_phantom_key(kp, out, in);
+ }
+ key_index += kp->keypad_info->ninputs;
+ out++;
+ }
+ return rv;
+}
+
+static void remove_phantom_keys(struct gpio_kp *kp)
+{
+ int out, in, inp;
+ int key_index;
+
+ if (kp->some_keys_pressed < 3)
+ return;
+
+ for (out = 0; out < kp->keypad_info->noutputs; out++) {
+ inp = -1;
+ key_index = out * kp->keypad_info->ninputs;
+ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ if (inp == -1) {
+ inp = in;
+ continue;
+ }
+ if (inp >= 0) {
+ if (!restore_keys_for_input(kp, out + 1,
+ inp))
+ break;
+ clear_phantom_key(kp, out, inp);
+ inp = -2;
+ }
+ restore_keys_for_input(kp, out, in);
+ }
+ }
+ }
+}
+
+static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int pressed = test_bit(key_index, kp->keys_pressed);
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (keycode == KEY_RESERVED) {
+ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
+ pr_info("gpiomatrix: unmapped key, %d-%d "
+ "(%d-%d) changed to %d\n",
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
+ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
+ "changed to %d\n", keycode,
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ input_report_key(kp->input_devs->dev[dev], keycode, pressed);
+ }
+ }
+}
+
+static void report_sync(struct gpio_kp *kp)
+{
+ int i;
+
+ for (i = 0; i < kp->input_devs->count; i++)
+ input_sync(kp->input_devs->dev[i]);
+}
+
+static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
+{
+ int out, in;
+ int key_index;
+ int gpio;
+ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
+
+ out = kp->current_output;
+ if (out == mi->noutputs) {
+ out = 0;
+ kp->last_key_state_changed = kp->key_state_changed;
+ kp->key_state_changed = 0;
+ kp->some_keys_pressed = 0;
+ } else {
+ key_index = out * mi->ninputs;
+ for (in = 0; in < mi->ninputs; in++, key_index++) {
+ gpio = mi->input_gpios[in];
+ if (gpio_get_value(gpio) ^ !polarity) {
+ if (kp->some_keys_pressed < 3)
+ kp->some_keys_pressed++;
+ kp->key_state_changed |= !__test_and_set_bit(
+ key_index, kp->keys_pressed);
+ } else
+ kp->key_state_changed |= __test_and_clear_bit(
+ key_index, kp->keys_pressed);
+ }
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, !polarity);
+ else
+ gpio_direction_input(gpio);
+ out++;
+ }
+ kp->current_output = out;
+ if (out < mi->noutputs) {
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, polarity);
+ else
+ gpio_direction_output(gpio, polarity);
+ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
+ if (kp->key_state_changed) {
+ hrtimer_start(&kp->timer, mi->debounce_delay,
+ HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ kp->key_state_changed = kp->last_key_state_changed;
+ }
+ if (kp->key_state_changed) {
+ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
+ remove_phantom_keys(kp);
+ key_index = 0;
+ for (out = 0; out < mi->noutputs; out++)
+ for (in = 0; in < mi->ninputs; in++, key_index++)
+ report_key(kp, key_index, out, in);
+ report_sync(kp);
+ }
+ if (!kp->use_irq || kp->some_keys_pressed) {
+ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+
+ /* No keys are pressed, reenable interrupt */
+ for (out = 0; out < mi->noutputs; out++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[out], polarity);
+ else
+ gpio_direction_output(mi->output_gpios[out], polarity);
+ }
+ for (in = 0; in < mi->ninputs; in++)
+ enable_irq(gpio_to_irq(mi->input_gpios[in]));
+ wake_unlock(&kp->wake_lock);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id)
+{
+ int i;
+ struct gpio_kp *kp = dev_id;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+
+ if (!kp->use_irq) {
+ /* ignore interrupt while registering the handler */
+ kp->disabled_irq = 1;
+ disable_irq_nosync(irq_in);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < mi->ninputs; i++)
+ disable_irq_nosync(gpio_to_irq(mi->input_gpios[i]));
+ for (i = 0; i < mi->noutputs; i++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[i],
+ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ gpio_direction_input(mi->output_gpios[i]);
+ }
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ return IRQ_HANDLED;
+}
+
+static int gpio_keypad_request_irqs(struct gpio_kp *kp)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long request_flags;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+
+ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) {
+ default:
+ request_flags = IRQF_TRIGGER_FALLING;
+ break;
+ case GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_RISING;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ:
+ request_flags = IRQF_TRIGGER_LOW;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_HIGH;
+ break;
+ }
+
+ for (i = 0; i < mi->ninputs; i++) {
+ err = irq = gpio_to_irq(mi->input_gpios[i]);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_keypad_irq_handler, request_flags,
+ "gpio_kp", kp);
+ if (err) {
+ pr_err("gpiomatrix: request_irq failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ goto err_request_irq_failed;
+ }
+ err = enable_irq_wake(irq);
+ if (err) {
+ pr_err("gpiomatrix: set_irq_wake failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ }
+ disable_irq(irq);
+ if (kp->disabled_irq) {
+ kp->disabled_irq = 0;
+ enable_irq(irq);
+ }
+ }
+ return 0;
+
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int i;
+ int err;
+ int key_count;
+ struct gpio_kp *kp;
+ struct gpio_event_matrix_info *mi;
+
+ mi = container_of(info, struct gpio_event_matrix_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) {
+ /* TODO: disable scanning */
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (mi->keymap == NULL ||
+ mi->input_gpios == NULL ||
+ mi->output_gpios == NULL) {
+ err = -ENODEV;
+ pr_err("gpiomatrix: Incomplete pdata\n");
+ goto err_invalid_platform_data;
+ }
+ key_count = mi->ninputs * mi->noutputs;
+
+ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) *
+ BITS_TO_LONGS(key_count), GFP_KERNEL);
+ if (kp == NULL) {
+ err = -ENOMEM;
+ pr_err("gpiomatrix: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ kp->input_devs = input_devs;
+ kp->keypad_info = mi;
+ for (i = 0; i < key_count; i++) {
+ unsigned short keyentry = mi->keymap[i];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+ if (dev >= input_devs->count) {
+ pr_err("gpiomatrix: bad device index %d >= "
+ "%d for key code %d\n",
+ dev, input_devs->count, keycode);
+ err = -EINVAL;
+ goto err_bad_keymap;
+ }
+ if (keycode && keycode <= KEY_MAX)
+ input_set_capability(input_devs->dev[dev],
+ EV_KEY, keycode);
+ }
+
+ for (i = 0; i < mi->noutputs; i++) {
+ err = gpio_request(mi->output_gpios[i], "gpio_kp_out");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_request_output_gpio_failed;
+ }
+ if (gpio_cansleep(mi->output_gpios[i])) {
+ pr_err("gpiomatrix: unsupported output gpio %d,"
+ " can sleep\n", mi->output_gpios[i]);
+ err = -EINVAL;
+ goto err_output_gpio_configure_failed;
+ }
+ if (mi->flags & GPIOKPF_DRIVE_INACTIVE)
+ err = gpio_direction_output(mi->output_gpios[i],
+ !(mi->flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ err = gpio_direction_input(mi->output_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_configure failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_output_gpio_configure_failed;
+ }
+ }
+ for (i = 0; i < mi->ninputs; i++) {
+ err = gpio_request(mi->input_gpios[i], "gpio_kp_in");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "input %d\n", mi->input_gpios[i]);
+ goto err_request_input_gpio_failed;
+ }
+ err = gpio_direction_input(mi->input_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_direction_input failed"
+ " for input %d\n", mi->input_gpios[i]);
+ goto err_gpio_direction_input_failed;
+ }
+ }
+ kp->current_output = mi->noutputs;
+ kp->key_state_changed = 1;
+
+ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ kp->timer.function = gpio_keypad_timer_func;
+ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
+ err = gpio_keypad_request_irqs(kp);
+ kp->use_irq = err == 0;
+
+ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for "
+ "%s%s in %s mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ kp->use_irq ? "interrupt" : "polling");
+
+ if (kp->use_irq)
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+ }
+
+ err = 0;
+ kp = *data;
+
+ if (kp->use_irq)
+ for (i = mi->noutputs - 1; i >= 0; i--)
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+
+ hrtimer_cancel(&kp->timer);
+ wake_lock_destroy(&kp->wake_lock);
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_gpio_direction_input_failed:
+ gpio_free(mi->input_gpios[i]);
+err_request_input_gpio_failed:
+ ;
+ }
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_output_gpio_configure_failed:
+ gpio_free(mi->output_gpios[i]);
+err_request_output_gpio_failed:
+ ;
+ }
+err_bad_keymap:
+ kfree(kp);
+err_kp_alloc_failed:
+err_invalid_platform_data:
+ return err;
+}
diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c
new file mode 100644
index 00000000000..2aac2fad0a1
--- /dev/null
+++ b/drivers/input/misc/gpio_output.c
@@ -0,0 +1,97 @@
+/* drivers/input/misc/gpio_output.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+
+int gpio_event_output_event(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, unsigned int dev, unsigned int type,
+ unsigned int code, int value)
+{
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+ if (type != oi->type)
+ return 0;
+ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
+ value = !value;
+ for (i = 0; i < oi->keymap_size; i++)
+ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code)
+ gpio_set_value(oi->keymap[i].gpio, value);
+ return 0;
+}
+
+int gpio_event_output_func(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, int func)
+{
+ int ret;
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME)
+ return 0;
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ int dev = oi->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_output_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ oi->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], oi->type,
+ oi->keymap[i].code);
+ }
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ ret = gpio_request(oi->keymap[i].gpio,
+ "gpio_event_output");
+ if (ret) {
+ pr_err("gpio_event_output_func: gpio_request "
+ "failed for %d\n", oi->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_output(oi->keymap[i].gpio,
+ output_level);
+ if (ret) {
+ pr_err("gpio_event_output_func: "
+ "gpio_direction_output failed for %d\n",
+ oi->keymap[i].gpio);
+ goto err_gpio_direction_output_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ for (i = oi->keymap_size - 1; i >= 0; i--) {
+err_gpio_direction_output_failed:
+ gpio_free(oi->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ return ret;
+}
+
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
new file mode 100644
index 00000000000..a5ea27ad0e1
--- /dev/null
+++ b/drivers/input/misc/keychord.c
@@ -0,0 +1,391 @@
+/*
+ * drivers/input/misc/keychord.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/keychord.h>
+#include <linux/sched.h>
+
+#define KEYCHORD_NAME "keychord"
+#define BUFFER_SIZE 16
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Key chord input driver");
+MODULE_SUPPORTED_DEVICE("keychord");
+MODULE_LICENSE("GPL");
+
+#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \
+ ((char *)kc + sizeof(struct input_keychord) + \
+ kc->count * sizeof(kc->keycodes[0])))
+
+struct keychord_device {
+ struct input_handler input_handler;
+ int registered;
+
+ /* list of keychords to monitor */
+ struct input_keychord *keychords;
+ int keychord_count;
+
+ /* bitmask of keys contained in our keychords */
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ /* current state of the keys */
+ unsigned long keystate[BITS_TO_LONGS(KEY_CNT)];
+ /* number of keys that are currently pressed */
+ int key_down;
+
+ /* second input_device_id is needed for null termination */
+ struct input_device_id device_ids[2];
+
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ unsigned char head;
+ unsigned char tail;
+ __u16 buff[BUFFER_SIZE];
+};
+
+static int check_keychord(struct keychord_device *kdev,
+ struct input_keychord *keychord)
+{
+ int i;
+
+ if (keychord->count != kdev->key_down)
+ return 0;
+
+ for (i = 0; i < keychord->count; i++) {
+ if (!test_bit(keychord->keycodes[i], kdev->keystate))
+ return 0;
+ }
+
+ /* we have a match */
+ return 1;
+}
+
+static void keychord_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ struct keychord_device *kdev = handle->private;
+ struct input_keychord *keychord;
+ unsigned long flags;
+ int i, got_chord = 0;
+
+ if (type != EV_KEY || code >= KEY_MAX)
+ return;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* do nothing if key state did not change */
+ if (!test_bit(code, kdev->keystate) == !value)
+ goto done;
+ __change_bit(code, kdev->keystate);
+ if (value)
+ kdev->key_down++;
+ else
+ kdev->key_down--;
+
+ /* don't notify on key up */
+ if (!value)
+ goto done;
+ /* ignore this event if it is not one of the keys we are monitoring */
+ if (!test_bit(code, kdev->keybit))
+ goto done;
+
+ keychord = kdev->keychords;
+ if (!keychord)
+ goto done;
+
+ /* check to see if the keyboard state matches any keychords */
+ for (i = 0; i < kdev->keychord_count; i++) {
+ if (check_keychord(kdev, keychord)) {
+ kdev->buff[kdev->head] = keychord->id;
+ kdev->head = (kdev->head + 1) % BUFFER_SIZE;
+ got_chord = 1;
+ break;
+ }
+ /* skip to next keychord */
+ keychord = NEXT_KEYCHORD(keychord);
+ }
+
+done:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (got_chord) {
+ pr_info("keychord: got keychord id %d. Any tasks: %d\n",
+ keychord->id,
+ !list_empty_careful(&kdev->waitq.task_list));
+ wake_up_interruptible(&kdev->waitq);
+ }
+}
+
+static int keychord_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i, ret;
+ struct input_handle *handle;
+ struct keychord_device *kdev =
+ container_of(handler, struct keychord_device, input_handler);
+
+ /*
+ * ignore this input device if it does not contain any keycodes
+ * that we are monitoring
+ */
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = KEYCHORD_NAME;
+ handle->private = kdev;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("keychord: using input dev %s for fevent\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keychord_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+/*
+ * keychord_read is used to read keychord events from the driver
+ */
+static ssize_t keychord_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ __u16 id;
+ int retval;
+ unsigned long flags;
+
+ if (count < sizeof(id))
+ return -EINVAL;
+ count = sizeof(id);
+
+ if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(kdev->waitq,
+ kdev->head != kdev->tail);
+ if (retval)
+ return retval;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* pop a keychord ID off the queue */
+ id = kdev->buff[kdev->tail];
+ kdev->tail = (kdev->tail + 1) % BUFFER_SIZE;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (copy_to_user(buffer, &id, count))
+ return -EFAULT;
+
+ return count;
+}
+
+/*
+ * keychord_write is used to configure the driver
+ */
+static ssize_t keychord_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ struct input_keychord *keychords = 0;
+ struct input_keychord *keychord, *next, *end;
+ int ret, i, key;
+ unsigned long flags;
+
+ if (count < sizeof(struct input_keychord))
+ return -EINVAL;
+ keychords = kzalloc(count, GFP_KERNEL);
+ if (!keychords)
+ return -ENOMEM;
+
+ /* read list of keychords from userspace */
+ if (copy_from_user(keychords, buffer, count)) {
+ kfree(keychords);
+ return -EFAULT;
+ }
+
+ /* unregister handler before changing configuration */
+ if (kdev->registered) {
+ input_unregister_handler(&kdev->input_handler);
+ kdev->registered = 0;
+ }
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* clear any existing configuration */
+ kfree(kdev->keychords);
+ kdev->keychords = 0;
+ kdev->keychord_count = 0;
+ kdev->key_down = 0;
+ memset(kdev->keybit, 0, sizeof(kdev->keybit));
+ memset(kdev->keystate, 0, sizeof(kdev->keystate));
+ kdev->head = kdev->tail = 0;
+
+ keychord = keychords;
+ end = (struct input_keychord *)((char *)keychord + count);
+
+ while (keychord < end) {
+ next = NEXT_KEYCHORD(keychord);
+ if (keychord->count <= 0 || next > end) {
+ pr_err("keychord: invalid keycode count %d\n",
+ keychord->count);
+ goto err_unlock_return;
+ }
+ if (keychord->version != KEYCHORD_VERSION) {
+ pr_err("keychord: unsupported version %d\n",
+ keychord->version);
+ goto err_unlock_return;
+ }
+
+ /* keep track of the keys we are monitoring in keybit */
+ for (i = 0; i < keychord->count; i++) {
+ key = keychord->keycodes[i];
+ if (key < 0 || key >= KEY_CNT) {
+ pr_err("keychord: keycode %d out of range\n",
+ key);
+ goto err_unlock_return;
+ }
+ __set_bit(key, kdev->keybit);
+ }
+
+ kdev->keychord_count++;
+ keychord = next;
+ }
+
+ kdev->keychords = keychords;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ ret = input_register_handler(&kdev->input_handler);
+ if (ret) {
+ kfree(keychords);
+ kdev->keychords = 0;
+ return ret;
+ }
+ kdev->registered = 1;
+
+ return count;
+
+err_unlock_return:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ kfree(keychords);
+ return -EINVAL;
+}
+
+static unsigned int keychord_poll(struct file *file, poll_table *wait)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ poll_wait(file, &kdev->waitq, wait);
+
+ if (kdev->head != kdev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int keychord_open(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev;
+
+ kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL);
+ if (!kdev)
+ return -ENOMEM;
+
+ spin_lock_init(&kdev->lock);
+ init_waitqueue_head(&kdev->waitq);
+
+ kdev->input_handler.event = keychord_event;
+ kdev->input_handler.connect = keychord_connect;
+ kdev->input_handler.disconnect = keychord_disconnect;
+ kdev->input_handler.name = KEYCHORD_NAME;
+ kdev->input_handler.id_table = kdev->device_ids;
+
+ kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT;
+ __set_bit(EV_KEY, kdev->device_ids[0].evbit);
+
+ file->private_data = kdev;
+
+ return 0;
+}
+
+static int keychord_release(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ if (kdev->registered)
+ input_unregister_handler(&kdev->input_handler);
+ kfree(kdev);
+
+ return 0;
+}
+
+static const struct file_operations keychord_fops = {
+ .owner = THIS_MODULE,
+ .open = keychord_open,
+ .release = keychord_release,
+ .read = keychord_read,
+ .write = keychord_write,
+ .poll = keychord_poll,
+};
+
+static struct miscdevice keychord_misc = {
+ .fops = &keychord_fops,
+ .name = KEYCHORD_NAME,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+static int __init keychord_init(void)
+{
+ return misc_register(&keychord_misc);
+}
+
+static void __exit keychord_exit(void)
+{
+ misc_deregister(&keychord_misc);
+}
+
+module_init(keychord_init);
+module_exit(keychord_exit);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index f9a5fd89bc0..bb3895a81c9 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -11,6 +11,14 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
+config TOUCHSCREEN_ATMXT
+ tristate "Atmel mXT Touchscreen Driver"
+ depends on I2C
+ help
+ Say Y here if you have an Atmel mXT touchscreen.
+
+ If unsure, say N.
+
config TOUCHSCREEN_88PM860X
tristate "Marvell 88PM860x touchscreen"
depends on MFD_88PM860X
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6bfbeab67c9..5f41086e3b1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -72,3 +72,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ATMXT) += atmxt.o
diff --git a/drivers/input/touchscreen/atmxt.c b/drivers/input/touchscreen/atmxt.c
new file mode 100644
index 00000000000..2c4b57fd15c
--- /dev/null
+++ b/drivers/input/touchscreen/atmxt.c
@@ -0,0 +1,4154 @@
+/*
+ * Copyright (C) 2010-2014 Motorola Mobility, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/* Driver for Atmel maXTouch touchscreens that uses tdat files */
+#include "atmxt.h"
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/wakeup_source_notify.h>
+
+static int atmxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int atmxt_remove(struct i2c_client *client);
+static int atmxt_suspend(struct i2c_client *client, pm_message_t message);
+static int atmxt_resume(struct i2c_client *client);
+static int atmxt_enter_aot(struct atmxt_driver_data *dd);
+static int atmxt_exit_aot(struct atmxt_driver_data *dd);
+static int atmxt_init(void);
+static void atmxt_exit(void);
+static void atmxt_free(struct atmxt_driver_data *dd);
+static void atmxt_free_ic_data(struct atmxt_driver_data *dd);
+static void atmxt_set_drv_state(struct atmxt_driver_data *dd,
+ enum atmxt_driver_state state);
+static int atmxt_get_drv_state(struct atmxt_driver_data *dd);
+static void atmxt_set_ic_state(struct atmxt_driver_data *dd,
+ enum atmxt_ic_state state);
+static int atmxt_get_ic_state(struct atmxt_driver_data *dd);
+static int atmxt_verify_pdata(struct atmxt_driver_data *dd);
+static int atmxt_request_tdat(struct atmxt_driver_data *dd);
+static void atmxt_tdat_callback(const struct firmware *tdat, void *context);
+static int atmxt_validate_tdat(const struct firmware *tdat);
+static int atmxt_validate_settings(uint8_t *data, uint32_t size);
+static int atmxt_gpio_init(struct atmxt_driver_data *dd);
+static int atmxt_register_inputs(struct atmxt_driver_data *dd,
+ uint8_t *rdat, int rsize);
+static int atmxt_request_irq(struct atmxt_driver_data *dd);
+static int atmxt_restart_ic(struct atmxt_driver_data *dd);
+static irqreturn_t atmxt_isr(int irq, void *handle);
+static int atmxt_get_info_header(struct atmxt_driver_data *dd);
+static int atmxt_get_object_table(struct atmxt_driver_data *dd);
+static int atmxt_process_object_table(struct atmxt_driver_data *dd);
+static uint8_t *atmxt_get_settings_entry(struct atmxt_driver_data *dd,
+ uint16_t num);
+static int atmxt_copy_platform_data(uint8_t *reg, uint8_t *entry,
+ uint8_t *tsett);
+static int atmxt_check_settings(struct atmxt_driver_data *dd, bool *reset);
+static int atmxt_send_settings(struct atmxt_driver_data *dd, bool save_nvm);
+static int atmxt_start_ic_calibration_fix(struct atmxt_driver_data *dd);
+static int atmxt_verify_ic_calibration_fix(struct atmxt_driver_data *dd);
+static int atmxt_stop_ic_calibration_fix(struct atmxt_driver_data *dd);
+static int atmxt_i2c_write(struct atmxt_driver_data *dd,
+ uint8_t addr_lo, uint8_t addr_hi, uint8_t *buf, int size);
+static int atmxt_i2c_read(struct atmxt_driver_data *dd, uint8_t *buf, int size);
+static int atmxt_save_internal_data(struct atmxt_driver_data *dd);
+static int atmxt_save_data5(struct atmxt_driver_data *dd, uint8_t *entry);
+static int atmxt_save_data6(struct atmxt_driver_data *dd, uint8_t *entry);
+static int atmxt_save_data7(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data8(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data9(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data42(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data46(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data62(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static void atmxt_compute_checksum(struct atmxt_driver_data *dd);
+static void atmxt_compute_partial_checksum(uint8_t *byte1, uint8_t *byte2,
+ uint8_t *low, uint8_t *mid, uint8_t *high);
+static void atmxt_active_handler(struct atmxt_driver_data *dd);
+static int atmxt_process_message(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static void atmxt_report_touches(struct atmxt_driver_data *dd);
+static void atmxt_release_touches(struct atmxt_driver_data *dd);
+static int atmxt_message_handler6(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static int atmxt_message_handler9(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static int atmxt_message_handler42(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static int atmxt_resume_restart(struct atmxt_driver_data *dd);
+static int atmxt_force_bootloader(struct atmxt_driver_data *dd);
+static bool atmxt_check_firmware_update(struct atmxt_driver_data *dd);
+static int atmxt_validate_firmware(uint8_t *data, uint32_t size);
+static int atmxt_flash_firmware(struct atmxt_driver_data *dd);
+static char *atmxt_msg2str(const uint8_t *msg, uint8_t size);
+static bool atmxt_wait4irq(struct atmxt_driver_data *dd);
+static int atmxt_create_sysfs_files(struct atmxt_driver_data *dd);
+static void atmxt_remove_sysfs_files(struct atmxt_driver_data *dd);
+
+static const struct i2c_device_id atmxt_id[] = {
+ /* This name must match the i2c_board_info name */
+ { ATMXT_I2C_NAME, 0 }, { }
+};
+
+MODULE_DEVICE_TABLE(i2c, atmxt_id);
+
+#ifdef CONFIG_OF
+static struct of_device_id atmxt_match_tbl[] = {
+ { .compatible = "atmel,atmxt-ts" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, atmxt_match_tbl);
+#endif
+
+static struct i2c_driver atmxt_driver = {
+ .driver = {
+ .name = ATMXT_I2C_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(atmxt_match_tbl),
+#endif
+ },
+ .probe = atmxt_probe,
+ .remove = atmxt_remove,
+ .id_table = atmxt_id,
+ .suspend = atmxt_suspend,
+ .resume = atmxt_resume,
+};
+
+#ifdef CONFIG_OF
+static struct touch_platform_data *
+atmxt_of_init(struct i2c_client *client)
+{
+ struct touch_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+ const char *fp = NULL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "pdata allocation failure\n");
+ return NULL;
+ }
+
+ of_property_read_string(np, "atmel,atmxt-tdat-filename", &fp);
+
+ pdata->filename = (char *)fp;
+ pdata->gpio_interrupt = of_get_gpio(np, 0);
+ pdata->gpio_reset = of_get_gpio(np, 1);
+
+ return pdata;
+}
+
+static int atmxt_pctrl_sel_state(struct device *dev, struct pinctrl *pctrl,
+ const char *state)
+{
+ int r;
+ struct pinctrl_state *pctrl_state = pinctrl_lookup_state(pctrl, state);
+ if (IS_ERR(pctrl_state)) {
+ dev_err(dev, "no %s pinctrl state\n", state);
+ return PTR_ERR(pctrl_state);
+ }
+ r = pinctrl_select_state(pctrl, pctrl_state);
+ if (r)
+ dev_err(dev, "failed to activate pinctrl %s\n", state);
+ return r;
+}
+
+inline int atmxt_tdat_detect_snowflake(struct i2c_client *client,
+ struct touch_platform_data *pdata)
+{
+ int r, need = 0;
+ struct pinctrl *pctrl = devm_pinctrl_get(&client->dev);
+ if (IS_ERR(pctrl)) {
+ dev_err(&client->dev, "no pinctrl handle\n");
+ return PTR_ERR(pctrl);
+ }
+
+ r = of_property_read_u32(client->dev.of_node,
+ "support-snowflake", &need);
+ if (r || !need) {
+ dev_dbg(&client->dev, "do not support snowflake\n");
+ goto exit_pullup;
+ }
+
+ /* To detect new snowflake touch sensor, it needs set touch_irq
+ * to pull-down, then read it back, the low value means snowflake
+ * touch is connected
+ */
+ r = atmxt_pctrl_sel_state(&client->dev, pctrl, "pulldown");
+ if (r)
+ goto exit;
+
+ msleep(1);
+ if (gpio_get_value(pdata->gpio_interrupt) == 0) {
+ /* detected snowflake, replace tdat file name */
+ const char *fp = NULL;
+ r = of_property_read_string_index(client->dev.of_node,
+ "atmel,atmxt-tdat-filename",
+ 1, &fp);
+ if (r) {
+ dev_err(&client->dev, "no snowflake tdat file defined\n");
+ goto exit;
+ }
+ pdata->filename = (char *)fp;
+ }
+
+exit_pullup:
+ /* switch to pullup by default that allow touch interrupt */
+ r = atmxt_pctrl_sel_state(&client->dev, pctrl, "pullup");
+ dev_dbg(&client->dev, "tdat file is %s\n", pdata->filename);
+
+exit:
+ devm_pinctrl_put(pctrl);
+ return r;
+}
+#else
+static inline struct touch_platform_data *
+atmxt_of_init(struct i2c_client *client)
+{
+ return NULL;
+}
+
+inline int atmxt_tdat_detect_snowflake(struct i2c_client *client,
+ struct touch_platform_data *pdata)
+{
+ return 0
+}
+#endif
+
+static int atmxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct atmxt_driver_data *dd = NULL;
+ int err = 0;
+ bool debugfail = false;
+
+ printk(KERN_INFO "%s: Driver: %s, Version: %s, Date: %s\n", __func__,
+ ATMXT_I2C_NAME, ATMXT_DRIVER_VERSION, ATMXT_DRIVER_DATE);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "%s: Missing I2C adapter support.\n", __func__);
+ err = -ENODEV;
+ goto atmxt_probe_fail;
+ }
+
+ dd = kzalloc(sizeof(struct atmxt_driver_data), GFP_KERNEL);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Unable to create driver data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+
+ dd->drv_stat = ATMXT_DRV_INIT;
+ dd->ic_stat = ATMXT_IC_UNKNOWN;
+ dd->status = 0x0000;
+ dd->client = client;
+
+ if (client->dev.of_node)
+ dd->pdata = atmxt_of_init(client);
+ else
+ dd->pdata = client->dev.platform_data;
+
+ if (!dd->pdata) {
+ printk(KERN_ERR "%s: No platform data found.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_probe_fail;
+ }
+
+ i2c_set_clientdata(client, dd);
+ dd->in_dev = NULL;
+
+ dd->mutex = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (dd->mutex == NULL) {
+ printk(KERN_ERR "%s: Unable to create mutex lock.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+ mutex_init(dd->mutex);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ dd->dbg = kzalloc(sizeof(struct atmxt_debug), GFP_KERNEL);
+ if (dd->dbg == NULL) {
+ printk(KERN_ERR "%s: Unable to create driver debug data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+ dd->dbg->dbg_lvl = ATMXT_DBG0;
+#endif
+
+ dd->util = kzalloc(sizeof(struct atmxt_util_data), GFP_KERNEL);
+ if (dd->util == NULL) {
+ printk(KERN_ERR "%s: Unable to create touch utility data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+
+ err = atmxt_verify_pdata(dd);
+ if (err < 0)
+ goto atmxt_probe_fail;
+
+ err = atmxt_gpio_init(dd);
+ if (err < 0)
+ goto atmxt_probe_fail;
+
+ err = atmxt_tdat_detect_snowflake(client, dd->pdata);
+ if (err < 0)
+ goto atmxt_probe_fail;
+
+ err = atmxt_request_irq(dd);
+ if (err < 0)
+ goto atmxt_unreg_suspend;
+
+ err = atmxt_request_tdat(dd);
+ if (err < 0)
+ goto atmxt_free_irq;
+
+ wake_lock_init(&dd->timed_lock, WAKE_LOCK_SUSPEND, "atmxt-timed-lock");
+
+ err = atmxt_create_sysfs_files(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Probe had error %d when creating sysfs files.\n",
+ __func__, err);
+ debugfail = true;
+ }
+
+ goto atmxt_probe_pass;
+
+atmxt_free_irq:
+ free_irq(dd->client->irq, dd);
+atmxt_unreg_suspend:
+ gpio_free(dd->pdata->gpio_reset);
+ gpio_free(dd->pdata->gpio_interrupt);
+atmxt_probe_fail:
+ atmxt_free(dd);
+ printk(KERN_ERR "%s: Probe failed with error code %d.\n",
+ __func__, err);
+ return err;
+
+atmxt_probe_pass:
+ if (debugfail) {
+ printk(KERN_INFO "%s: Probe completed with errors.\n",
+ __func__);
+ } else {
+ printk(KERN_INFO "%s: Probe successful.\n", __func__);
+ }
+ return 0;
+}
+
+static int atmxt_remove(struct i2c_client *client)
+{
+ struct atmxt_driver_data *dd = NULL;
+
+ dd = i2c_get_clientdata(client);
+ if (dd != NULL) {
+ if (wake_lock_active(&dd->timed_lock))
+ wake_unlock(&dd->timed_lock);
+
+ wake_lock_destroy(&dd->timed_lock);
+ free_irq(dd->client->irq, dd);
+ atmxt_remove_sysfs_files(dd);
+ gpio_free(dd->pdata->gpio_reset);
+ gpio_free(dd->pdata->gpio_interrupt);
+ atmxt_free(dd);
+ }
+
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+static int atmxt_suspend(struct i2c_client *client, pm_message_t message)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd;
+ int drv_state;
+
+ dd = i2c_get_clientdata(client);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Driver data is missing.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_suspend_no_dd_fail;
+ }
+
+ mutex_lock(dd->mutex);
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Suspending...\n", __func__);
+
+ drv_state = atmxt_get_drv_state(dd);
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ case ATMXT_DRV_IDLE:
+ atmxt_set_drv_state(dd, ATMXT_DRV_SUSPENDED);
+ break;
+
+ case ATMXT_DRV_SUSPENDED:
+ pr_err("%s: Driver has already suspended.\n", __func__);
+ break;
+
+ default:
+ pr_err("%s: Cannot suspend in driver state %s.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ err = -EPERM;
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Suspend complete.\n", __func__);
+
+ mutex_unlock(dd->mutex);
+
+atmxt_suspend_no_dd_fail:
+ return (err < 0) ? err : 0;
+}
+
+static int atmxt_resume(struct i2c_client *client)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd;
+ int drv_state;
+
+ dd = i2c_get_clientdata(client);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Driver data is missing.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_resume_no_dd_fail;
+ }
+
+ mutex_lock(dd->mutex);
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Resuming...\n", __func__);
+
+ drv_state = atmxt_get_drv_state(dd);
+ switch (drv_state) {
+ case ATMXT_DRV_SUSPENDED:
+ if (dd->status & (1 << ATMXT_RESUME_HANDLE_ISR)) {
+ pr_info("%s: Enabling pending ISR...\n", __func__);
+ if (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))) {
+ dd->status = dd->status |
+ (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ }
+ dd->status = dd->status &
+ ~(1 << ATMXT_RESUME_HANDLE_ISR);
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ } else if (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))) {
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ } else {
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ }
+ break;
+
+ default:
+ pr_err("%s: Resuming in driver state %s.\n", __func__,
+ atmxt_ic_state_string[drv_state]);
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Resume complete.\n", __func__);
+
+ mutex_unlock(dd->mutex);
+
+atmxt_resume_no_dd_fail:
+ return (err < 0) ? err : 0;
+}
+
+static int atmxt_enter_aot(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int drv_state = 0;
+ int ic_state = 0;
+ uint8_t sleep_cmd[4] = {0x64, 0x64, 0x19, 0x00};
+ uint8_t adx_cmd[2] = {0x08, 0x08};
+ uint8_t mxd_cmd = 0x08;
+ uint8_t sup_cmd[5] = {0x41, 0x28, 0x14, 0x14, 0x40};
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Entering AOT...\n", __func__);
+
+ drv_state = atmxt_get_drv_state(dd);
+ ic_state = atmxt_get_ic_state(dd);
+
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ case ATMXT_DRV_IDLE:
+ switch (ic_state) {
+ case ATMXT_IC_ACTIVE:
+ err = atmxt_i2c_write(dd,
+ dd->addr->pwr[0], dd->addr->pwr[1],
+ &(sleep_cmd[0]), 4);
+ if (err < 0) {
+ pr_err("%s: Failed to reduce power.\n",
+ __func__);
+ goto atmxt_enter_aot_fail;
+ }
+
+ err = atmxt_i2c_write(dd,
+ dd->addr->adx[0], dd->addr->adx[1],
+ &(adx_cmd[0]), 2);
+ if (err < 0) {
+ pr_err("%s: Failed to reduce adx.\n",
+ __func__);
+ goto atmxt_enter_aot_fail;
+ }
+
+ err = atmxt_i2c_write(dd,
+ dd->addr->mxd[0], dd->addr->mxd[1],
+ &mxd_cmd, 1);
+ if (err < 0) {
+ pr_err("%s: Failed to reduce mxd.\n",
+ __func__);
+ goto atmxt_enter_aot_fail;
+ }
+
+ err = atmxt_i2c_write(dd,
+ dd->addr->sup[0], dd->addr->sup[1],
+ &(sup_cmd[0]), 5);
+ if (err < 0) {
+ pr_err("%s: Failed to change sup.\n",
+ __func__);
+ goto atmxt_enter_aot_fail;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_AOT);
+ break;
+
+ default:
+ pr_err("%s: Driver %s, IC %s enter AOT.\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+ break;
+ }
+ break;
+
+ default:
+ pr_err("%s: Trying to enter AOT in driver state %s.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Enter AOT complete.\n", __func__);
+
+atmxt_enter_aot_fail:
+ return err;
+}
+
+static int atmxt_exit_aot(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int drv_state = 0;
+ int ic_state = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Exiting AOT...\n", __func__);
+
+ drv_state = atmxt_get_drv_state(dd);
+ ic_state = atmxt_get_ic_state(dd);
+
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ case ATMXT_DRV_IDLE:
+ switch (ic_state) {
+ case ATMXT_IC_ACTIVE:
+ pr_err("%s: Driver %s, IC %s exit AOT.\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+ break;
+
+ case ATMXT_IC_AOT:
+ err = atmxt_i2c_write(dd,
+ dd->addr->pwr[0], dd->addr->pwr[1],
+ &(dd->data->pwr[0]), 4);
+ if (err < 0) {
+ pr_err("%s: Failed to wake IC.\n", __func__);
+ goto atmxt_exit_aot_fail;
+ }
+
+ err = atmxt_i2c_write(dd,
+ dd->addr->adx[0], dd->addr->adx[1],
+ &(dd->data->adx[0]), 2);
+ if (err < 0) {
+ pr_err("%s: Failed to restore adx.\n",
+ __func__);
+ goto atmxt_exit_aot_fail;
+ }
+
+ err = atmxt_i2c_write(dd,
+ dd->addr->mxd[0], dd->addr->mxd[1],
+ &(dd->data->mxd), 1);
+ if (err < 0) {
+ pr_err("%s: Failed to restore mxd.\n",
+ __func__);
+ goto atmxt_exit_aot_fail;
+ }
+
+ err = atmxt_i2c_write(dd,
+ dd->addr->sup[0], dd->addr->sup[1],
+ &(dd->data->sup[0]), 5);
+ if (err < 0) {
+ pr_err("%s: Failed to restore sup.\n",
+ __func__);
+ goto atmxt_exit_aot_fail;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_ACTIVE);
+ break;
+
+ default:
+ pr_err("%s: Driver %s, IC %s exit AOT--%s...\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state], "recovering");
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Recovery failed %s %d.\n",
+ __func__, "with error code", err);
+ goto atmxt_exit_aot_fail;
+ }
+ }
+ break;
+
+ case ATMXT_DRV_INIT:
+ pr_err("%s: Trying to leave AOT in driver state %s.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ break;
+
+ default:
+ pr_err("%s: Driver %s, IC %s exit AOT--%s...\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state], "recovering");
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ pr_err("%s: Recovery failed %s %d.\n",
+ __func__, "with error code", err);
+ goto atmxt_exit_aot_fail;
+ }
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Exit AOT complete.\n", __func__);
+
+atmxt_exit_aot_fail:
+ return err;
+}
+
+static int __init atmxt_init(void)
+{
+ return i2c_add_driver(&atmxt_driver);
+}
+
+static void __exit atmxt_exit(void)
+{
+ i2c_del_driver(&atmxt_driver);
+ return;
+}
+
+module_init(atmxt_init);
+module_exit(atmxt_exit);
+
+static void atmxt_free(struct atmxt_driver_data *dd)
+{
+ if (dd != NULL) {
+ dd->pdata = NULL;
+ dd->client = NULL;
+
+ if (dd->mutex != NULL) {
+ kfree(dd->mutex);
+ dd->mutex = NULL;
+ }
+
+ if (dd->util != NULL) {
+ kfree(dd->util->data);
+ dd->util->data = NULL;
+ kfree(dd->util);
+ dd->util = NULL;
+ }
+
+ if (dd->in_dev != NULL) {
+ input_unregister_device(dd->in_dev);
+ dd->in_dev = NULL;
+ }
+
+ atmxt_free_ic_data(dd);
+
+ if (dd->rdat != NULL) {
+ kfree(dd->rdat);
+ dd->rdat = NULL;
+ }
+
+ if (dd->dbg != NULL) {
+ kfree(dd->dbg);
+ dd->dbg = NULL;
+ }
+
+ kfree(dd);
+ dd = NULL;
+ }
+
+ return;
+}
+
+static void atmxt_free_ic_data(struct atmxt_driver_data *dd)
+{
+ if (dd->info_blk != NULL) {
+ kfree(dd->info_blk->data);
+ dd->info_blk->data = NULL;
+ kfree(dd->info_blk->msg_id);
+ dd->info_blk->msg_id = NULL;
+ kfree(dd->info_blk);
+ dd->info_blk = NULL;
+ }
+
+ if (dd->nvm != NULL) {
+ kfree(dd->nvm->data);
+ dd->nvm->data = NULL;
+ kfree(dd->nvm);
+ dd->nvm = NULL;
+ }
+
+ if (dd->addr != NULL) {
+ kfree(dd->addr);
+ dd->addr = NULL;
+ }
+
+ if (dd->data != NULL) {
+ kfree(dd->data);
+ dd->data = NULL;
+ }
+
+ return;
+}
+
+static void atmxt_set_drv_state(struct atmxt_driver_data *dd,
+ enum atmxt_driver_state state)
+{
+ printk(KERN_INFO "%s: Driver state %s -> %s\n", __func__,
+ atmxt_driver_state_string[dd->drv_stat],
+ atmxt_driver_state_string[state]);
+ dd->drv_stat = state;
+ return;
+}
+
+static int atmxt_get_drv_state(struct atmxt_driver_data *dd)
+{
+ return dd->drv_stat;
+}
+
+static void atmxt_set_ic_state(struct atmxt_driver_data *dd,
+ enum atmxt_ic_state state)
+{
+ printk(KERN_INFO "%s: IC state %s -> %s\n", __func__,
+ atmxt_ic_state_string[dd->ic_stat],
+ atmxt_ic_state_string[state]);
+ dd->ic_stat = state;
+ return;
+}
+
+static int atmxt_get_ic_state(struct atmxt_driver_data *dd)
+{
+ return dd->ic_stat;
+}
+
+static int atmxt_verify_pdata(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Verifying platform data...\n", __func__);
+
+ if (dd->pdata == NULL) {
+ printk(KERN_ERR "%s: Platform data is missing.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_verify_pdata_fail;
+ }
+
+ if (dd->pdata->gpio_reset == 0) {
+ printk(KERN_ERR "%s: Reset GPIO is invalid.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_verify_pdata_fail;
+ }
+
+ if (dd->pdata->gpio_interrupt == 0) {
+ printk(KERN_ERR "%s: Interrupt GPIO is invalid.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_verify_pdata_fail;
+ }
+
+ if (dd->pdata->filename == NULL) {
+ printk(KERN_ERR "%s: Touch data filename is missing.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_verify_pdata_fail;
+ }
+
+atmxt_verify_pdata_fail:
+ return err;
+}
+
+static int atmxt_request_tdat(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Requesting tdat data...%s\n", __func__,
+ dd->pdata->filename);
+
+ err = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG, dd->pdata->filename, &(dd->client->dev),
+ GFP_KERNEL, dd, atmxt_tdat_callback);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to schedule tdat request.\n",
+ __func__);
+ goto atmxt_request_tdat_fail;
+ }
+
+atmxt_request_tdat_fail:
+ return err;
+}
+
+static void atmxt_tdat_callback(const struct firmware *tdat, void *context)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = context;
+ bool icfail = false;
+ uint8_t cur_id = 0x00;
+ uint32_t cur_size = 0;
+ uint8_t *cur_data = NULL;
+ size_t loc = 0;
+
+ mutex_lock(dd->mutex);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if (dd->status & (1 << ATMXT_WAITING_FOR_TDAT)) {
+ printk(KERN_INFO "%s: Processing new tdat file...\n", __func__);
+ dd->status = dd->status & ~(1 << ATMXT_WAITING_FOR_TDAT);
+ } else {
+ printk(KERN_INFO "%s: Processing %s...\n", __func__,
+ dd->pdata->filename);
+ }
+#else
+ printk(KERN_INFO "%s: Processing %s...\n", __func__,
+ dd->pdata->filename);
+#endif
+
+ if (tdat == NULL) {
+ printk(KERN_ERR "%s: No data received.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_tdat_callback_fail;
+ }
+
+ err = atmxt_validate_tdat(tdat);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if (atmxt_get_drv_state(dd) != ATMXT_DRV_INIT) {
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+ atmxt_set_drv_state(dd, ATMXT_DRV_INIT);
+ }
+
+ if (dd->util->data != NULL) {
+ kfree(dd->util->data);
+ dd->util->data = NULL;
+ dd->util->size = 0;
+ dd->util->tsett = NULL;
+ dd->util->tsett_size = 0;
+ dd->util->fw = NULL;
+ dd->util->fw_size = 0;
+ dd->util->addr[0] = 0x00;
+ dd->util->addr[1] = 0x00;
+ }
+#endif
+
+ dd->util->data = kzalloc(tdat->size * sizeof(uint8_t), GFP_KERNEL);
+ if (dd->util->data == NULL) {
+ printk(KERN_ERR "%s: Unable to copy tdat.\n", __func__);
+ err = -ENOMEM;
+ goto atmxt_tdat_callback_fail;
+ }
+ memcpy(dd->util->data, tdat->data, tdat->size);
+ dd->util->size = tdat->size;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: MCV is 0x%02X.\n", __func__,
+ dd->util->data[loc]);
+ loc++;
+
+ while (loc < dd->util->size) {
+ cur_id = dd->util->data[loc];
+ cur_size = (dd->util->data[loc+3] << 16) |
+ (dd->util->data[loc+2] << 8) |
+ dd->util->data[loc+1];
+ cur_data = &(dd->util->data[loc+4]);
+
+ switch (cur_id) {
+ case 0x00:
+ break;
+
+ case 0x01:
+ dd->util->tsett = cur_data;
+ dd->util->tsett_size = cur_size;
+ break;
+
+ case 0x02:
+ dd->util->fw = cur_data;
+ dd->util->fw_size = cur_size;
+ break;
+
+ case 0x03:
+ if (cur_size == 0 || cur_size % 10 != 0) {
+ printk(KERN_ERR
+ "%s: Abs data format is invalid.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_tdat_callback_fail;
+ }
+
+ err = atmxt_register_inputs(dd, cur_data, cur_size);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+ break;
+
+ case 0x04:
+ if (cur_size < 4) {
+ printk(KERN_ERR
+ "%s: Driver data is too small.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_tdat_callback_fail;
+ }
+ dd->settings = (cur_data[1] << 8) | cur_data[0];
+ dd->util->addr[0] = cur_data[2];
+ dd->util->addr[1] = cur_data[3];
+ dd->client->addr = dd->util->addr[0];
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Record %hu found but not used.\n",
+ __func__, cur_id);
+ break;
+ }
+
+ loc = loc + cur_size + 4;
+ }
+
+ err = atmxt_validate_settings(dd->util->tsett, dd->util->tsett_size);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+
+ err = atmxt_validate_firmware(dd->util->fw, dd->util->fw_size);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+
+ err = atmxt_restart_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Restarting IC failed with error code %d.\n",
+ __func__, err);
+ icfail = true;
+ }
+
+ if (!icfail) {
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ dd->status = dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ printk(KERN_INFO "%s: Touch initialization successful.\n",
+ __func__);
+ } else {
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ printk(KERN_INFO
+ "%s: Touch initialization completed with errors.\n",
+ __func__);
+ }
+
+ goto atmxt_tdat_callback_exit;
+
+atmxt_tdat_callback_fail:
+ printk(KERN_ERR "%s: Touch initialization failed with error code %d.\n",
+ __func__, err);
+
+atmxt_tdat_callback_exit:
+ release_firmware(tdat);
+ mutex_unlock(dd->mutex);
+
+ return;
+}
+
+static int atmxt_validate_tdat(const struct firmware *tdat)
+{
+ int err = 0;
+ int length = 0;
+ size_t loc = 0;
+
+ if (tdat->data == NULL || tdat->size == 0) {
+ printk(KERN_ERR "%s: No data found.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_validate_tdat_fail;
+ }
+
+ if (tdat->data[loc] != 0x31) {
+ printk(KERN_ERR "%s: MCV 0x%02X is not supported.\n",
+ __func__, tdat->data[loc]);
+ err = -EINVAL;
+ goto atmxt_validate_tdat_fail;
+ }
+ loc++;
+
+ while (loc < (tdat->size - 3)) {
+ length = (tdat->data[loc+3] << 16) |
+ (tdat->data[loc+2] << 8) |
+ tdat->data[loc+1];
+ if ((loc + length + 4) > tdat->size) {
+ printk(KERN_ERR
+ "%s: Overflow in data at byte %u %s %hu.\n",
+ __func__, loc, "and record", tdat->data[loc]);
+ err = -EOVERFLOW;
+ goto atmxt_validate_tdat_fail;
+ }
+
+ loc = loc + length + 4;
+ }
+
+ if (loc != (tdat->size)) {
+ printk(KERN_ERR "%s: Data is misaligned.\n", __func__);
+ err = -ENOEXEC;
+ goto atmxt_validate_tdat_fail;
+ }
+
+atmxt_validate_tdat_fail:
+ return err;
+}
+
+static int atmxt_validate_settings(uint8_t *data, uint32_t size)
+{
+ int err = 0;
+ uint32_t iter = 0;
+ uint16_t length = 0x0000;
+
+ if (data == NULL || size == 0) {
+ printk(KERN_ERR "%s: No settings data found.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_validate_settings_fail;
+ } else if (size <= 5) {
+ printk(KERN_ERR "%s: Settings data is malformed.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_validate_settings_fail;
+ }
+
+ while (iter < (size - 1)) {
+ length = (data[iter+4] << 8) | data[iter+3];
+ if ((iter + length + 5) > size) {
+ printk(KERN_ERR
+ "%s: Group record overflow on iter %u.\n",
+ __func__, iter);
+ err = -EOVERFLOW;
+ goto atmxt_validate_settings_fail;
+ }
+
+ iter = iter + length + 5;
+ }
+
+ if (iter != size) {
+ printk(KERN_ERR "%s: Group records misaligned.\n", __func__);
+ err = -ENOEXEC;
+ goto atmxt_validate_settings_fail;
+ }
+
+atmxt_validate_settings_fail:
+ return err;
+}
+
+static int atmxt_gpio_init(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ struct gpio touch_gpio[] = {
+ {dd->pdata->gpio_reset, GPIOF_OUT_INIT_LOW, "touch_reset"},
+ {dd->pdata->gpio_interrupt, GPIOF_IN, "touch_irq"},
+ };
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Requesting touch GPIOs...\n", __func__);
+
+ err = gpio_request_array(touch_gpio, ARRAY_SIZE(touch_gpio));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to request touch GPIOs.\n",
+ __func__);
+ goto atmxt_gpio_init_fail;
+ }
+
+
+atmxt_gpio_init_fail:
+ return err;
+}
+
+static int atmxt_register_inputs(struct atmxt_driver_data *dd,
+ uint8_t *rdat, int rsize)
+{
+ int err = 0;
+ int i = 0;
+ int iter = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Registering inputs...\n", __func__);
+
+ if (dd->rdat != NULL)
+ kfree(dd->rdat);
+
+ dd->rdat = kzalloc(sizeof(struct atmxt_report_data), GFP_KERNEL);
+ if (dd->rdat == NULL) {
+ printk(KERN_ERR "%s: Unable to create report data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_register_inputs_fail;
+ }
+
+ if (dd->in_dev != NULL)
+ input_unregister_device(dd->in_dev);
+
+ dd->in_dev = input_allocate_device();
+ if (dd->in_dev == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate input device.\n",
+ __func__);
+ err = -ENODEV;
+ goto atmxt_register_inputs_fail;
+ }
+
+ dd->in_dev->name = ATMXT_I2C_NAME;
+ input_set_drvdata(dd->in_dev, dd);
+ set_bit(INPUT_PROP_DIRECT, dd->in_dev->propbit);
+
+ input_mt_init_slots(dd->in_dev, ATMXT_MAX_TOUCHES, 0);
+
+ /* Need for palm detection */
+ set_bit(KEY_SLEEP, dd->in_dev->keybit);
+ set_bit(EV_KEY, dd->in_dev->evbit);
+
+ set_bit(EV_ABS, dd->in_dev->evbit);
+ for (i = 0; i < rsize; i += 10) {
+ if (((rdat[i+1] << 8) | rdat[i+0]) != ATMXT_ABS_RESERVED) {
+ input_set_abs_params(dd->in_dev,
+ (rdat[i+1] << 8) | rdat[i+0],
+ (rdat[i+3] << 8) | rdat[i+2],
+ (rdat[i+5] << 8) | rdat[i+4],
+ (rdat[i+7] << 8) | rdat[i+6],
+ (rdat[i+9] << 8) | rdat[i+8]);
+ }
+
+ if (iter < ARRAY_SIZE(dd->rdat->axis)) {
+ dd->rdat->axis[iter] = (rdat[i+1] << 8) | rdat[i+0];
+ iter++;
+ }
+ }
+
+ for (i = iter; i < ARRAY_SIZE(dd->rdat->axis); i++)
+ dd->rdat->axis[i] = ATMXT_ABS_RESERVED;
+
+ input_set_events_per_packet(dd->in_dev,
+ ATMXT_MAX_TOUCHES * (ARRAY_SIZE(dd->rdat->axis) + 1));
+
+ err = input_register_device(dd->in_dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to register input device.\n",
+ __func__);
+ err = -ENODEV;
+ input_free_device(dd->in_dev);
+ dd->in_dev = NULL;
+ goto atmxt_register_inputs_fail;
+ }
+
+atmxt_register_inputs_fail:
+ return err;
+}
+
+static int atmxt_request_irq(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Requesting IRQ...\n", __func__);
+
+ err = gpio_get_value(dd->pdata->gpio_interrupt);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Cannot test IRQ line level.\n", __func__);
+ goto atmxt_request_irq_fail;
+ } else if (err == 0) {
+ printk(KERN_ERR
+ "%s: Line already active; cannot request IRQ.\n",
+ __func__);
+ err = -EIO;
+ goto atmxt_request_irq_fail;
+ }
+
+ err = request_threaded_irq(dd->client->irq, NULL, atmxt_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ ATMXT_I2C_NAME, dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: IRQ request failed.\n", __func__);
+ goto atmxt_request_irq_fail;
+ }
+
+ disable_irq_nosync(dd->client->irq);
+
+atmxt_request_irq_fail:
+ return err;
+}
+
+static int atmxt_restart_ic(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ bool irq_low = false;
+ uint32_t size = 0;
+ bool update_fw = false;
+ bool need_reset = false;
+ int cur_drv_state = 0;
+ bool update_complete = false;
+
+atmxt_restart_ic_start:
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Restarting IC...\n", __func__);
+
+ atmxt_free_ic_data(dd);
+ atmxt_release_touches(dd);
+ irq_low = false;
+ if (atmxt_get_ic_state(dd) != ATMXT_IC_UNKNOWN)
+ atmxt_set_ic_state(dd, ATMXT_IC_UNKNOWN);
+
+ if (!update_fw && !update_complete) {
+ atmxt_dbg(dd, ATMXT_DBG2,
+ "%s: Resetting touch IC...\n", __func__);
+ gpio_set_value(dd->pdata->gpio_reset, 0);
+ udelay(ATMXT_IC_RESET_HOLD_TIME);
+ gpio_set_value(dd->pdata->gpio_reset, 1);
+ }
+
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low && !update_fw) {
+ printk(KERN_ERR "%s: Timeout waiting for interrupt.\n",
+ __func__);
+ err = -ETIME;
+ goto atmxt_restart_ic_fail;
+ }
+
+ dd->info_blk = kzalloc(sizeof(struct atmxt_info_block), GFP_KERNEL);
+ if (dd->info_blk == NULL) {
+ printk(KERN_ERR "%s: Unable to create info block data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ if (update_fw) {
+ if (!irq_low) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Ignored interrupt timeout.\n", __func__);
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Trying bootloader...\n", __func__);
+ update_fw = false;
+ goto atmxt_restart_ic_updatefw_start;
+ }
+
+ err = atmxt_get_info_header(dd);
+ if (err < 0) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Error reaching IC in normal mode. %s\n",
+ __func__, "Trying bootloader...");
+atmxt_restart_ic_updatefw_start:
+ dd->client->addr = dd->util->addr[1];
+ err = atmxt_i2c_read(dd, &(dd->info_blk->header[0]), 3);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to find touch IC.\n",
+ __func__);
+ dd->client->addr = dd->util->addr[0];
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_BOOTLOADER);
+ if (dd->info_blk->header[0] & 0x20) {
+ printk(KERN_INFO "%s: %s: 0x%02X, %s: 0x%02X\n",
+ __func__,
+ "Bootloader ID", dd->info_blk->header[1],
+ "Bootloader Version", dd->info_blk->header[2]);
+ } else {
+ printk(KERN_INFO "%s: Bootloader ID: 0x%02X\n",
+ __func__, dd->info_blk->header[0] & 0x1F);
+ }
+
+ if ((dd->info_blk->header[0] & 0xC0) == 0x40) {
+ if (update_complete) {
+ printk(KERN_ERR "%s: Firmware CRC failure.\n",
+ __func__);
+ } else {
+ printk(KERN_INFO
+ "%s: Firmware CRC failure; %s.\n",
+ __func__, "going to try reflashing IC");
+ }
+ }
+
+ if (update_complete) {
+ printk(KERN_ERR "%s: %s--%s.\n", __func__,
+ "Still in bootloader mode after reflash",
+ "check firmware image");
+ dd->client->addr = dd->util->addr[0];
+ err = -EINVAL;
+ goto atmxt_restart_ic_fail;
+ }
+
+ cur_drv_state = atmxt_get_drv_state(dd);
+ atmxt_set_drv_state(dd, ATMXT_DRV_REFLASH);
+ err = atmxt_flash_firmware(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update IC firmware.\n",
+ __func__);
+ dd->client->addr = dd->util->addr[0];
+ atmxt_set_drv_state(dd, cur_drv_state);
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_set_drv_state(dd, cur_drv_state);
+ dd->client->addr = dd->util->addr[0];
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Reflash completed. Re-starting cycle...\n",
+ __func__);
+ update_complete = true;
+ goto atmxt_restart_ic_start;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_PRESENT);
+ printk(KERN_INFO "%s: Family ID: 0x%02X, Variant ID: 0x%02X, " \
+ "Version: 0x%02X, Build: 0x%02X, Matrix: %ux%u, Objects: %u\n",
+ __func__, dd->info_blk->header[0], dd->info_blk->header[1],
+ dd->info_blk->header[2], dd->info_blk->header[3],
+ dd->info_blk->header[4], dd->info_blk->header[5],
+ dd->info_blk->header[6]);
+
+ if (atmxt_get_drv_state(dd) == ATMXT_DRV_INIT) {
+ update_fw = atmxt_check_firmware_update(dd);
+ if (update_fw & update_complete) {
+ printk(KERN_ERR "%s: %s %s %s.\n", __func__,
+ "Platform firmware version",
+ "does not match platform firmware",
+ "after update");
+ update_fw = false;
+ }
+ }
+
+ size = dd->info_blk->header[6] * 6;
+ if (size > 255) {
+ printk(KERN_ERR "%s: Too many objects present.\n", __func__);
+ err = -EOVERFLOW;
+ goto atmxt_restart_ic_fail;
+ }
+ dd->info_blk->size = size;
+
+ dd->info_blk->data = kzalloc(sizeof(uint8_t) * size, GFP_KERNEL);
+ if (dd->info_blk->data == NULL) {
+ printk(KERN_ERR "%s: Unable to create table data.\n", __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ err = atmxt_get_object_table(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error getting object table.\n", __func__);
+ if (update_fw)
+ goto atmxt_restart_updatefw_check;
+ else
+ goto atmxt_restart_ic_fail;
+ }
+
+atmxt_restart_updatefw_check:
+ if (update_fw) {
+ printk(KERN_INFO "%s: Resetting IC to update firmware...\n",
+ __func__);
+ err = atmxt_force_bootloader(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to force flash mode.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+ goto atmxt_restart_ic_start;
+ }
+
+ dd->nvm = kzalloc(sizeof(struct atmxt_nvm), GFP_KERNEL);
+ if (dd->nvm == NULL) {
+ printk(KERN_ERR "%s: Unable to create NVM struct.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ dd->addr = kzalloc(sizeof(struct atmxt_addr), GFP_KERNEL);
+ if (dd->addr == NULL) {
+ printk(KERN_ERR "%s: Unable to create address book.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ dd->data = kzalloc(sizeof(struct atmxt_data), GFP_KERNEL);
+ if (dd->data == NULL) {
+ printk(KERN_ERR "%s: Unable to create data book.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ err = atmxt_process_object_table(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Processing info block failed.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+
+ err = atmxt_save_internal_data(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to save internal data.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_compute_checksum(dd);
+
+ err = atmxt_check_settings(dd, &need_reset);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to check/update IC %s.\n",
+ __func__, "with platform settings");
+ goto atmxt_restart_ic_fail;
+ } else if (need_reset) {
+ update_fw = false;
+ update_complete = false;
+ goto atmxt_restart_ic_start;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_ACTIVE);
+
+ err = atmxt_start_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to start IC calibration fix.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+
+atmxt_restart_ic_fail:
+ return err;
+}
+
+static irqreturn_t atmxt_isr(int irq, void *handle)
+{
+ struct atmxt_driver_data *dd = handle;
+ int drv_state;
+ int ic_state;
+
+ mutex_lock(dd->mutex);
+
+ drv_state = atmxt_get_drv_state(dd);
+ ic_state = atmxt_get_ic_state(dd);
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: IRQ Received -- Driver: %s, IC: %s\n", __func__,
+ atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ switch (ic_state) {
+ case ATMXT_IC_SLEEP:
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Servicing IRQ during sleep...\n",
+ __func__);
+ atmxt_active_handler(dd);
+ break;
+ case ATMXT_IC_AOT:
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Servicing AOT IRQ...\n",
+ __func__);
+ case ATMXT_IC_ACTIVE:
+ atmxt_active_handler(dd);
+ break;
+ default:
+ printk(KERN_ERR "%s: Driver %s, IC %s IRQ received.\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ }
+ break;
+ }
+ break;
+
+ case ATMXT_DRV_SUSPENDED:
+ pr_info("%s: IRQ while suspended--%s.\n",
+ __func__, "will re-enable on resume");
+ dd->status = dd->status | (1 << ATMXT_RESUME_HANDLE_ISR);
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Driver state \"%s\" IRQ received.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ }
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: IRQ Serviced.\n", __func__);
+ mutex_unlock(dd->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int atmxt_get_info_header(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ err = atmxt_i2c_write(dd, 0x00, 0x00, NULL, 0);
+ if (err < 0)
+ goto atmxt_get_info_header_fail;
+
+ err = atmxt_i2c_read(dd, &(dd->info_blk->header[0]), 7);
+ if (err < 0)
+ goto atmxt_get_info_header_fail;
+
+atmxt_get_info_header_fail:
+ return err;
+}
+
+static int atmxt_get_object_table(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0; /* Fix Atmel's data order */
+ int top = 0; /* Fix Atmel's data order */
+ int cur = 0; /* Fix Atmel's data order */
+ uint8_t lo_addr = 255; /* Fix Atmel's data order */
+ uint8_t hi_addr = 255; /* Fix Atmel's data order */
+ uint8_t temp[6]; /* Fix Atmel's data order */
+
+ err = atmxt_i2c_write(dd, 0x07, 0x00, NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Unable to set address pointer to object table.\n",
+ __func__);
+ goto atmxt_get_object_table_fail;
+ }
+
+ err = atmxt_i2c_read(dd, dd->info_blk->data, dd->info_blk->size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Object table read failed.\n", __func__);
+ goto atmxt_get_object_table_fail;
+ }
+
+ /* Fix Atmel's data order */
+ while (top < dd->info_blk->size) {
+ for (i = top; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+2] < hi_addr) {
+ lo_addr = dd->info_blk->data[i+1];
+ hi_addr = dd->info_blk->data[i+2];
+ cur = i;
+ } else if ((dd->info_blk->data[i+2] == hi_addr) &&
+ (dd->info_blk->data[i+1] < lo_addr)) {
+ lo_addr = dd->info_blk->data[i+1];
+ hi_addr = dd->info_blk->data[i+2];
+ cur = i;
+ }
+ }
+
+ memcpy(&(temp[0]), &(dd->info_blk->data[top]), 6);
+ memmove(&(dd->info_blk->data[top]),
+ &(dd->info_blk->data[cur]), 6);
+ memcpy(&(dd->info_blk->data[cur]), &(temp[0]), 6);
+
+ lo_addr = 255;
+ hi_addr = 255;
+ top = top + 6;
+ cur = top;
+ }
+
+atmxt_get_object_table_fail:
+ return err;
+}
+
+static int atmxt_process_object_table(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ uint32_t ids = 0;
+ uint32_t nvm_size = 0;
+ int usr_start = 0;
+ bool usr_start_seen = false;
+ uint8_t *tsett = NULL;
+ int id_iter = 0;
+ int nvm_iter = 0;
+
+ ids++;
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ ids += dd->info_blk->data[i+5] * (dd->info_blk->data[i+4] + 1);
+ if (usr_start_seen) {
+ nvm_size += (dd->info_blk->data[i+3] + 1)
+ * (dd->info_blk->data[i+4] + 1);
+ } else if (dd->info_blk->data[i+0] == 38) {
+ usr_start = i;
+ usr_start_seen = true;
+ }
+ }
+
+ if (ids > 255) {
+ printk(KERN_ERR "%s: Too many report IDs used.\n", __func__);
+ err = -EOVERFLOW;
+ goto atmxt_process_object_table_fail;
+ }
+
+ dd->info_blk->msg_id = kzalloc(sizeof(uint8_t) * ids, GFP_KERNEL);
+ if (dd->info_blk->msg_id == NULL) {
+ printk(KERN_ERR "%s: Unable to create ID table.\n", __func__);
+ err = -ENOMEM;
+ goto atmxt_process_object_table_fail;
+ }
+ dd->info_blk->id_size = ids;
+
+ dd->nvm->data = kzalloc(sizeof(uint8_t) * nvm_size, GFP_KERNEL);
+ if (dd->nvm->data == NULL) {
+ printk(KERN_ERR "%s: Unable to create NVM block.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_process_object_table_fail;
+ }
+ dd->nvm->size = nvm_size;
+ dd->nvm->addr[0] = dd->info_blk->data[usr_start+6+1];
+ dd->nvm->addr[1] = dd->info_blk->data[usr_start+6+2];
+
+ dd->info_blk->msg_id[id_iter] = 0;
+ id_iter++;
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ for (j = 0; j <= dd->info_blk->data[i+4]; j++) {
+ for (k = 0; k < dd->info_blk->data[i+5]; k++) {
+ dd->info_blk->msg_id[id_iter] =
+ dd->info_blk->data[i+0];
+ id_iter++;
+ }
+ }
+
+ if (i <= usr_start)
+ continue;
+
+ tsett = atmxt_get_settings_entry(dd, dd->info_blk->data[i+0]);
+ err = atmxt_copy_platform_data(&(dd->nvm->data[nvm_iter]),
+ &(dd->info_blk->data[i+0]), tsett);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to copy platform data.\n",
+ __func__);
+ goto atmxt_process_object_table_fail;
+ }
+
+ nvm_iter += (dd->info_blk->data[i+3] + 1) *
+ (dd->info_blk->data[i+4] + 1);
+ }
+
+atmxt_process_object_table_fail:
+ return err;
+}
+
+static uint8_t *atmxt_get_settings_entry(struct atmxt_driver_data *dd,
+ uint16_t num)
+{
+ uint8_t *entry = NULL;
+ uint32_t iter = 0;
+
+ while (iter < dd->util->tsett_size) {
+ if (num == ((dd->util->tsett[iter+1] << 8) |
+ dd->util->tsett[iter])) {
+ entry = &(dd->util->tsett[iter]);
+ break;
+ } else {
+ iter += 5 + ((dd->util->tsett[iter+4] << 8) |
+ dd->util->tsett[iter+3]);
+ }
+ }
+
+ return entry;
+}
+
+static int atmxt_copy_platform_data(uint8_t *reg, uint8_t *entry,
+ uint8_t *tsett)
+{
+ int err = 0;
+ int i = 0;
+ int iter = 0;
+ int size = 0;
+ uint8_t *data = NULL;
+ int data_size = 0;
+ int obj_size = 0;
+ int obj_inst = 0;
+ uint8_t inst_count = 0x00;
+ uint16_t tsett_size = 0x0000;
+
+ if (tsett == NULL)
+ goto atmxt_copy_platform_data_fail;
+
+ tsett_size = (tsett[4] << 8) | tsett[3];
+ if (tsett[2] == 0) {
+ inst_count = 1;
+ data_size = tsett_size;
+ data = &(tsett[5]);
+ } else {
+ inst_count = tsett[2];
+
+ if ((tsett_size % inst_count) != 0) {
+ printk(KERN_ERR "%s: Settings data unevenly packed.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_copy_platform_data_fail;
+ }
+
+ data_size = tsett_size / inst_count;
+ data = &(tsett[5]);
+ }
+
+ obj_size = entry[3] + 1;
+ obj_inst = entry[4] + 1;
+
+ if (data_size > obj_size)
+ size = obj_size;
+ else
+ size = data_size;
+
+ while ((i < inst_count) && (i < obj_inst)) {
+ memcpy(&(reg[i*obj_size]), &(data[iter]), size);
+ iter += data_size;
+ i++;
+ }
+
+atmxt_copy_platform_data_fail:
+ return err;
+}
+
+static int atmxt_check_settings(struct atmxt_driver_data *dd, bool *reset)
+{
+ int err = 0;
+ uint8_t *msg_buf = NULL;
+ char *contents = NULL;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Checking IC settings...\n", __func__);
+
+ if (dd->data->max_msg_size < 5) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_check_settings_fail;
+ }
+
+ msg_buf = kzalloc(sizeof(uint8_t) * dd->data->max_msg_size, GFP_KERNEL);
+ if (msg_buf == NULL) {
+ printk(KERN_ERR
+ "%s: Unable to allocate memory for message buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_check_settings_fail;
+ }
+
+ err = atmxt_i2c_write(dd, dd->addr->msg[0], dd->addr->msg[1], NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to set message buffer pointer.\n",
+ __func__);
+ goto atmxt_check_settings_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_SET_MESSAGE_POINTER);
+
+ err = atmxt_i2c_read(dd, msg_buf, dd->data->max_msg_size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read message.\n", __func__);
+ goto atmxt_check_settings_fail;
+ }
+
+ if (msg_buf[0] <= (dd->info_blk->id_size-1)) {
+ if (dd->info_blk->msg_id[msg_buf[0]] == 6) {
+ if (!(msg_buf[1] & 0x80)) {
+ printk(KERN_ERR "%s: %s: 0x%02X\n", __func__,
+ "Received checksum without reset",
+ msg_buf[1]);
+ }
+ } else {
+ contents = atmxt_msg2str(msg_buf,
+ dd->data->max_msg_size);
+ printk(KERN_ERR "%s: %s--%s %u instead: %s.\n",
+ __func__, "Failed to receive reset message",
+ "received this from Object",
+ dd->info_blk->msg_id[msg_buf[0]], contents);
+ err = -EIO;
+ goto atmxt_check_settings_fail;
+ }
+ } else {
+ contents = atmxt_msg2str(msg_buf, dd->data->max_msg_size);
+ printk(KERN_ERR "%s: %s--%s: %s.\n",
+ __func__, "Failed to receive reset message",
+ "received unknown message instead", contents);
+ err = -EIO;
+ goto atmxt_check_settings_fail;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: %s 0x%02X%02X%02X, %s 0x%02X%02X%02X.\n", __func__,
+ "Driver checksum is", dd->nvm->chksum[0],
+ dd->nvm->chksum[1], dd->nvm->chksum[2],
+ "IC checksum is", msg_buf[2], msg_buf[3], msg_buf[4]);
+
+ if ((msg_buf[2] == dd->nvm->chksum[0]) &&
+ (msg_buf[3] == dd->nvm->chksum[1]) &&
+ (msg_buf[4] == dd->nvm->chksum[2])) {
+ err = atmxt_process_message(dd,
+ msg_buf, dd->data->max_msg_size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error processing first message.\n",
+ __func__);
+ goto atmxt_check_settings_fail;
+ }
+ *reset = false;
+ } else if (*reset) {
+ printk(KERN_ERR "%s: %s.\n", __func__,
+ "Previous attempt to write platform settings failed");
+ err = -EINVAL;
+ goto atmxt_check_settings_fail;
+ } else {
+ printk(KERN_INFO "%s: Updating IC settings...\n", __func__);
+ err = atmxt_send_settings(dd, true);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update IC settings.\n",
+ __func__);
+ goto atmxt_check_settings_fail;
+ }
+
+ msleep(500);
+ *reset = true;
+ }
+
+atmxt_check_settings_fail:
+ kfree(msg_buf);
+ kfree(contents);
+ return err;
+}
+
+static int atmxt_send_settings(struct atmxt_driver_data *dd, bool save_nvm)
+{
+ int err = 0;
+ uint8_t nvm_cmd = 0x55;
+
+ err = atmxt_i2c_write(dd, dd->nvm->addr[0], dd->nvm->addr[1],
+ dd->nvm->data, dd->nvm->size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error writing settings to IC.\n",
+ __func__);
+ goto atmxt_send_settings_fail;
+ }
+
+ if (save_nvm) {
+ err = atmxt_i2c_write(dd, dd->addr->nvm[0], dd->addr->nvm[1],
+ &nvm_cmd, sizeof(uint8_t));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error backing up to NVM.\n",
+ __func__);
+ goto atmxt_send_settings_fail;
+ }
+ }
+
+atmxt_send_settings_fail:
+ return err;
+}
+
+static int atmxt_start_ic_calibration_fix(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ uint8_t sett[6] = {0x05, 0x00, 0x00, 0x00, 0x01, 0x80};
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Starting IC calibration fix...\n",
+ __func__);
+
+ sett[1] = dd->data->acq[1];
+ err = atmxt_i2c_write(dd, dd->addr->acq[0], dd->addr->acq[1],
+ &(sett[0]), 6);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update acquisition settings.\n",
+ __func__);
+ goto atmxt_start_ic_calibration_fix_fail;
+ }
+
+ dd->data->timer = 0;
+ dd->status = dd->status & ~(1 << ATMXT_RECEIVED_CALIBRATION);
+ dd->status = dd->status | (1 << ATMXT_FIXING_CALIBRATION);
+
+atmxt_start_ic_calibration_fix_fail:
+ return err;
+}
+
+static int atmxt_verify_ic_calibration_fix(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ unsigned long toc = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Verifying IC calibration fix...\n",
+ __func__);
+
+ toc = jiffies;
+ if (dd->status & (1 << ATMXT_RECEIVED_CALIBRATION)) {
+ dd->data->timer = 0;
+ dd->status = dd->status & ~(1 << ATMXT_RECEIVED_CALIBRATION);
+ } else if (dd->status & (1 << ATMXT_REPORT_TOUCHES)) {
+ if (dd->data->timer == 0)
+ dd->data->timer = toc;
+
+ if (((toc - dd->data->timer) * 1000 / HZ) >= 2500) {
+ err = atmxt_stop_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s %s.\n", __func__,
+ "Failed to stop",
+ "fixing IC calibration");
+ goto atmxt_verify_ic_calibration_fix_fail;
+ }
+ }
+ }
+
+atmxt_verify_ic_calibration_fix_fail:
+ return err;
+}
+
+static int atmxt_stop_ic_calibration_fix(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Stopping IC calibration fix...\n",
+ __func__);
+
+ err = atmxt_i2c_write(dd, dd->addr->acq[0], dd->addr->acq[1],
+ &(dd->data->acq[0]), 6);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update acquisition settings.\n",
+ __func__);
+ goto atmxt_stop_ic_calibration_fix_fail;
+ }
+
+ dd->status = dd->status & ~(1 << ATMXT_FIXING_CALIBRATION);
+
+atmxt_stop_ic_calibration_fix_fail:
+ return err;
+}
+
+static int atmxt_i2c_write(struct atmxt_driver_data *dd,
+ uint8_t addr_lo, uint8_t addr_hi, uint8_t *buf, int size)
+{
+ int err = 0;
+ uint8_t *data_out = NULL;
+ int size_out = 0;
+ int i = 0;
+ char *str = NULL;
+
+ dd->status = dd->status & ~(1 << ATMXT_SET_MESSAGE_POINTER);
+
+ size_out = size + 2;
+ data_out = kzalloc(sizeof(uint8_t) * size_out, GFP_KERNEL);
+ if (data_out == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate write memory.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_i2c_write_exit;
+ }
+
+ data_out[0] = addr_lo;
+ data_out[1] = addr_hi;
+ if (buf != NULL && size > 0)
+ memcpy(&(data_out[2]), buf, size);
+
+ for (i = 1; i <= ATMXT_I2C_ATTEMPTS; i++) {
+ err = i2c_master_send(dd->client, data_out, size_out);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: %s %d, failed with error code %d.\n",
+ __func__, "On I2C write attempt", i, err);
+ } else if (err < size_out) {
+ printk(KERN_ERR
+ "%s: %s %d, wrote %d bytes instead of %d.\n",
+ __func__, "On I2C write attempt", i, err,
+ size_out);
+ err = -EBADE;
+ } else {
+ break;
+ }
+
+ usleep_range(ATMXT_I2C_WAIT_TIME, ATMXT_I2C_WAIT_TIME_MAX);
+ }
+
+ if (err < 0)
+ printk(KERN_ERR "%s: I2C write failed.\n", __func__);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if ((dd->dbg->dbg_lvl) >= ATMXT_DBG2)
+ str = atmxt_msg2str(data_out, size_out);
+#endif
+ atmxt_dbg(dd, ATMXT_DBG2, "%s: %s\n", __func__, str);
+ kfree(str);
+
+atmxt_i2c_write_exit:
+ kfree(data_out);
+
+ return err;
+}
+
+static int atmxt_i2c_read(struct atmxt_driver_data *dd, uint8_t *buf, int size)
+{
+ int err = 0;
+ int i = 0;
+ char *str = NULL;
+
+ for (i = 1; i <= ATMXT_I2C_ATTEMPTS; i++) {
+ err = i2c_master_recv(dd->client, buf, size);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: %s %d, failed with error code %d.\n",
+ __func__, "On I2C read attempt", i, err);
+ } else if (err < size) {
+ printk(KERN_ERR
+ "%s: %s %d, received %d bytes instead of %d.\n",
+ __func__, "On I2C read attempt", i, err, size);
+ err = -EBADE;
+ } else {
+ break;
+ }
+
+ usleep_range(ATMXT_I2C_WAIT_TIME, ATMXT_I2C_WAIT_TIME_MAX);
+ }
+
+ if (err < 0)
+ printk(KERN_ERR "%s: I2C read failed.\n", __func__);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if ((dd->dbg->dbg_lvl) >= ATMXT_DBG1)
+ str = atmxt_msg2str(buf, size);
+#endif
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: %s\n", __func__, str);
+ kfree(str);
+
+ return err;
+}
+
+static int atmxt_save_internal_data(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ bool usr_start_seen = false;
+ int nvm_iter = 0;
+ bool chk_5 = false;
+ bool chk_6 = false;
+ bool chk_7 = false;
+ bool chk_8 = false;
+ bool chk_9 = false;
+ bool chk_42 = false;
+ bool chk_46 = false;
+ bool chk_62 = false;
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ switch (dd->info_blk->data[i+0]) {
+ case 5:
+ chk_5 = true;
+ err = atmxt_save_data5(dd, &(dd->info_blk->data[i+0]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 6:
+ chk_6 = true;
+ err = atmxt_save_data6(dd, &(dd->info_blk->data[i+0]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 7:
+ chk_7 = true;
+ err = atmxt_save_data7(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 8:
+ chk_8 = true;
+ err = atmxt_save_data8(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 9:
+ chk_9 = true;
+ err = atmxt_save_data9(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 38:
+ usr_start_seen = true;
+ break;
+
+ case 42:
+ chk_42 = true;
+ err = atmxt_save_data42(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 46:
+ chk_46 = true;
+ err = atmxt_save_data46(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 62:
+ chk_62 = true;
+ err = atmxt_save_data62(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ default:
+ break;
+ }
+
+ if (usr_start_seen && (dd->info_blk->data[i+0] != 38)) {
+ nvm_iter += (dd->info_blk->data[i+3] + 1) *
+ (dd->info_blk->data[i+4] + 1);
+ }
+ }
+
+ if (!chk_5) {
+ printk(KERN_ERR "%s: Object 5 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_6) {
+ printk(KERN_ERR "%s: Object 6 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_7) {
+ printk(KERN_ERR "%s: Object 7 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_8) {
+ printk(KERN_ERR "%s: Object 8 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_9) {
+ printk(KERN_ERR "%s: Object 9 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_42) {
+ pr_err("%s: Object 42 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_46) {
+ pr_err("%s: Object 46 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_62) {
+ pr_err("%s: Object 62 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+atmxt_save_internal_data_fail:
+ return err;
+}
+
+static int atmxt_save_data5(struct atmxt_driver_data *dd, uint8_t *entry)
+{
+ int err = 0;
+
+ dd->addr->msg[0] = entry[1];
+ dd->addr->msg[1] = entry[2];
+ dd->data->max_msg_size = entry[3];
+
+ return err;
+}
+
+static int atmxt_save_data6(struct atmxt_driver_data *dd, uint8_t *entry)
+{
+ int err = 0;
+
+ if (entry[3] < 2) {
+ printk(KERN_ERR "%s: Command object is too small.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_save_data6_fail;
+ }
+
+ dd->addr->rst[0] = entry[1];
+ dd->addr->rst[1] = entry[2];
+
+ dd->addr->nvm[0] = entry[1] + 1;
+ dd->addr->nvm[1] = entry[2];
+ if (dd->addr->nvm[0] < entry[1]) /* Check for 16-bit addr overflow */
+ dd->addr->nvm[1]++;
+
+ dd->addr->cal[0] = entry[1] + 2;
+ dd->addr->cal[1] = entry[2];
+ if (dd->addr->cal[0] < entry[1]) /* Check for 16-bit addr overflow */
+ dd->addr->cal[1]++;
+
+atmxt_save_data6_fail:
+ return err;
+}
+
+static int atmxt_save_data7(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 3) {
+ printk(KERN_ERR "%s: Power object is too small.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_save_data7_fail;
+ }
+
+ dd->addr->pwr[0] = entry[1];
+ dd->addr->pwr[1] = entry[2];
+ dd->data->pwr[0] = reg[0];
+ dd->data->pwr[1] = reg[1];
+ dd->data->pwr[2] = reg[2];
+ dd->data->pwr[3] = reg[3];
+
+atmxt_save_data7_fail:
+ return err;
+}
+
+static int atmxt_save_data8(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 9) {
+ printk(KERN_ERR "%s: Acquisition object is too small.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_save_data8_fail;
+ }
+
+ dd->addr->acq[0] = entry[1] + 4;
+ dd->addr->acq[1] = entry[2];
+ if (dd->addr->acq[0] < entry[1]) /* Check for 16-bit addr overflow */
+ dd->addr->acq[1]++;
+
+ dd->data->acq[0] = reg[4];
+ dd->data->acq[1] = reg[5];
+ dd->data->acq[2] = reg[6];
+ dd->data->acq[3] = reg[7];
+ dd->data->acq[4] = reg[8];
+ dd->data->acq[5] = reg[9];
+
+atmxt_save_data8_fail:
+ return err;
+}
+
+static int atmxt_save_data9(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+ int i = 0;
+
+ for (i = 1; i < dd->info_blk->id_size; i++) {
+ if (dd->info_blk->msg_id[i] == 9) {
+ dd->data->touch_id_offset = i;
+ break;
+ }
+ }
+
+ if (dd->data->touch_id_offset == 0) {
+ printk(KERN_ERR
+ "%s: Touch object has reporting error.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_save_data9_fail;
+ }
+
+ dd->data->res[0] = false;
+ dd->data->res[1] = false;
+
+ if (entry[3] < 21) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Only 10-bit resolution is available.\n", __func__);
+ } else {
+ if (reg[19] >= 0x04)
+ dd->data->res[0] = true;
+
+ if (reg[21] >= 0x04)
+ dd->data->res[1] = true;
+ }
+
+atmxt_save_data9_fail:
+ return err;
+}
+
+static int atmxt_save_data42(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 4) {
+ printk(KERN_ERR "%s: Suppression object is too small.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_save_data42_fail;
+ }
+
+ dd->addr->sup[0] = entry[1];
+ dd->addr->sup[1] = entry[2];
+ dd->data->sup[0] = reg[0];
+ dd->data->sup[1] = reg[1];
+ dd->data->sup[2] = reg[2];
+ dd->data->sup[3] = reg[3];
+ dd->data->sup[4] = reg[4];
+
+atmxt_save_data42_fail:
+ return err;
+}
+
+static int atmxt_save_data46(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 3) {
+ pr_err("%s: CTE object is too small.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_save_data46_fail;
+ }
+
+ dd->addr->adx[0] = entry[1] + 2;
+ dd->addr->adx[1] = entry[2];
+ if (dd->addr->adx[0] < entry[1]) /* Check for 16-bit addr overflow */
+ dd->addr->adx[1]++;
+
+ dd->data->adx[0] = reg[2];
+ dd->data->adx[1] = reg[3];
+
+atmxt_save_data46_fail:
+ return err;
+}
+
+static int atmxt_save_data62(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 25) {
+ pr_err("%s: Noise object is too small.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_save_data62_fail;
+ }
+
+ dd->addr->mxd[0] = entry[1] + 24;
+ dd->addr->mxd[1] = entry[2];
+ if (dd->addr->mxd[0] < entry[1]) /* Check for 16-bit addr overflow */
+ dd->addr->mxd[1]++;
+
+ dd->data->mxd = reg[24];
+
+atmxt_save_data62_fail:
+ return err;
+}
+
+static void atmxt_compute_checksum(struct atmxt_driver_data *dd)
+{
+ uint8_t low = 0x00;
+ uint8_t mid = 0x00;
+ uint8_t high = 0x00;
+ uint8_t byte1 = 0x00;
+ uint8_t byte2 = 0x00;
+ uint32_t iter = 0;
+ uint32_t range = 0;
+
+ range = dd->nvm->size - (dd->nvm->size % 2);
+
+ while (iter < range) {
+ byte1 = dd->nvm->data[iter];
+ iter++;
+ byte2 = dd->nvm->data[iter];
+ iter++;
+ atmxt_compute_partial_checksum(&byte1, &byte2,
+ &low, &mid, &high);
+ }
+
+ if ((dd->nvm->size % 2) != 0) {
+ byte1 = dd->nvm->data[iter];
+ byte2 = 0x00;
+ atmxt_compute_partial_checksum(&byte1, &byte2,
+ &low, &mid, &high);
+ }
+
+ dd->nvm->chksum[0] = low;
+ dd->nvm->chksum[1] = mid;
+ dd->nvm->chksum[2] = high;
+
+ return;
+}
+
+static void atmxt_compute_partial_checksum(uint8_t *byte1, uint8_t *byte2,
+ uint8_t *low, uint8_t *mid, uint8_t *high)
+{
+ bool xor_result = false;
+
+ if (*high & 0x80)
+ xor_result = true;
+
+ *high = *high << 1;
+ if (*mid & 0x80)
+ (*high)++;
+
+ *mid = *mid << 1;
+ if (*low & 0x80)
+ (*mid)++;
+
+ *low = *low << 1;
+
+ *low = *low ^ *byte1;
+ *mid = *mid ^ *byte2;
+
+ if (xor_result) {
+ *low = *low ^ 0x1B;
+ *high = *high ^ 0x80;
+ }
+
+ return;
+}
+
+static void atmxt_active_handler(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ uint8_t *msg_buf = NULL;
+ int size = 0;
+ char *contents = NULL;
+ bool msg_fail = false;
+ int last_err = 0;
+ int msg_size = 0;
+ bool inv_msg_seen = false;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Starting active handler...\n", __func__);
+
+ msg_size = dd->data->max_msg_size;
+ size = (dd->rdat->active_touches + 1) * msg_size;
+ if (size == msg_size)
+ size = msg_size * 2;
+
+ msg_buf = kzalloc(sizeof(uint8_t) * size, GFP_KERNEL);
+ if (msg_buf == NULL) {
+ printk(KERN_ERR
+ "%s: Unable to allocate memory for message buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_active_handler_fail;
+ }
+
+ if (!(dd->status & (1 << ATMXT_SET_MESSAGE_POINTER))) {
+ err = atmxt_i2c_write(dd, dd->addr->msg[0], dd->addr->msg[1],
+ NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to set message buffer pointer.\n",
+ __func__);
+ goto atmxt_active_handler_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_SET_MESSAGE_POINTER);
+ }
+
+ err = atmxt_i2c_read(dd, msg_buf, size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read messages.\n", __func__);
+ goto atmxt_active_handler_fail;
+ }
+
+ if (msg_buf[0] == 0xFF) {
+ contents = atmxt_msg2str(msg_buf, size);
+ printk(KERN_ERR "%s: Received invalid data: %s.\n",
+ __func__, contents);
+ err = -EINVAL;
+ goto atmxt_active_handler_fail;
+ }
+
+ for (i = 0; i < size; i += msg_size) {
+ if (msg_buf[i] == 0xFF) {
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Reached 0xFF message.\n",
+ __func__);
+ inv_msg_seen = true;
+ continue;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Processing message %d...\n", __func__,
+ (i + 1) / msg_size);
+
+ if (inv_msg_seen) {
+ printk(KERN_INFO "%s: %s %s (%d).\n", __func__,
+ "System response time lagging",
+ "IC report rate",
+ (i / msg_size) + 1);
+ }
+
+ err = atmxt_process_message(dd, &(msg_buf[i]), msg_size);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Processing message %d failed %s %d.\n",
+ __func__, (i / msg_size) + 1,
+ "with error code", err);
+ msg_fail = true;
+ last_err = err;
+ }
+ }
+
+ if (dd->status & (1 << ATMXT_RESTART_REQUIRED)) {
+ printk(KERN_ERR "%s: Restarting touch IC...\n", __func__);
+ dd->status = dd->status & ~(1 << ATMXT_RESTART_REQUIRED);
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to restart touch IC.\n",
+ __func__);
+ goto atmxt_active_handler_fail;
+ } else if (msg_fail) {
+ err = last_err;
+ goto atmxt_active_handler_fail;
+ } else {
+ goto atmxt_active_handler_pass;
+ }
+ }
+
+ if (dd->status & (1 << ATMXT_FIXING_CALIBRATION)) {
+ err = atmxt_verify_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to verify IC calibration.\n",
+ __func__);
+ goto atmxt_active_handler_fail;
+ }
+ }
+
+ if (dd->status & (1 << ATMXT_REPORT_TOUCHES)) {
+ atmxt_report_touches(dd);
+ dd->status = dd->status & ~(1 << ATMXT_REPORT_TOUCHES);
+ }
+
+ if (msg_fail) {
+ err = last_err;
+ goto atmxt_active_handler_fail;
+ }
+
+ goto atmxt_active_handler_pass;
+
+atmxt_active_handler_fail:
+ printk(KERN_ERR "%s: Touch active handler failed with error code %d.\n",
+ __func__, err);
+
+atmxt_active_handler_pass:
+ kfree(msg_buf);
+ kfree(contents);
+ return;
+}
+
+static int atmxt_process_message(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+ char *contents = NULL;
+
+ if (msg[0] <= (dd->info_blk->id_size-1)) {
+ switch (dd->info_blk->msg_id[msg[0]]) {
+ case 6:
+ err = atmxt_message_handler6(dd, msg, size);
+ break;
+ case 9:
+ err = atmxt_message_handler9(dd, msg, size);
+ break;
+ case 42:
+ err = atmxt_message_handler42(dd, msg, size);
+ break;
+ default:
+ contents = atmxt_msg2str(msg, size);
+ printk(KERN_ERR "%s: Object %u sent this: %s.\n",
+ __func__, dd->info_blk->msg_id[msg[0]],
+ contents);
+ break;
+ }
+ } else {
+ contents = atmxt_msg2str(msg, size);
+ printk(KERN_ERR "%s: Received unknown message: %s.\n",
+ __func__, contents);
+ }
+
+ if (err < 0)
+ printk(KERN_ERR "%s: Message processing failed.\n", __func__);
+
+ kfree(contents);
+ return err;
+}
+
+static void atmxt_report_touches(struct atmxt_driver_data *dd)
+{
+ int i = 0;
+ int j = 0;
+ int rval = 0;
+ int id = 0;
+ int x = 0;
+ int y = 0;
+ int p = 0;
+ int w = 0;
+
+ dd->rdat->active_touches = 0;
+
+ for (i = 0; i < ATMXT_MAX_TOUCHES; i++) {
+ input_mt_slot(dd->in_dev, i);
+ input_mt_report_slot_state(dd->in_dev, MT_TOOL_FINGER,
+ dd->rdat->tchdat[i].active);
+
+ if (!(dd->rdat->tchdat[i].active))
+ continue;
+
+ id = dd->rdat->tchdat[i].id;
+ x = dd->rdat->tchdat[i].x;
+ y = dd->rdat->tchdat[i].y;
+ p = dd->rdat->tchdat[i].p;
+ w = dd->rdat->tchdat[i].w;
+
+ dd->rdat->active_touches++;
+
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: ID=%d, X=%d, Y=%d, P=%d, W=%d\n",
+ __func__, id, x, y, p, w);
+
+ for (j = 0; j < ARRAY_SIZE(dd->rdat->axis); j++) {
+ switch (j) {
+ case 0:
+ rval = x;
+ break;
+ case 1:
+ rval = y;
+ break;
+ case 2:
+ rval = p;
+ break;
+ case 3:
+ rval = w;
+ break;
+ case 4:
+ rval = id;
+ break;
+ }
+ if (dd->rdat->axis[j] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[j], rval);
+ }
+ }
+ }
+
+ if (dd->rdat->active_touches == 0) {
+ if (atmxt_get_ic_state(dd) == ATMXT_IC_AOT) {
+ /*
+ * If there are no active touches, but we are
+ * running this code, it can be because we saw
+ * a press and release message at the same time,
+ * so we need to generate an explicit press and
+ * release to wake the device.
+ */
+ input_mt_slot(dd->in_dev, 0);
+ input_mt_report_slot_state(dd->in_dev, MT_TOOL_FINGER,
+ true);
+
+ if (dd->rdat->axis[0] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[0],
+ dd->rdat->tchdat[i].x);
+ }
+
+ if (dd->rdat->axis[1] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[1],
+ dd->rdat->tchdat[i].y);
+ }
+
+ if (dd->rdat->axis[2] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[2],
+ dd->rdat->tchdat[i].p);
+ }
+
+ if (dd->rdat->axis[3] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[3],
+ dd->rdat->tchdat[i].w);
+ }
+
+ if (dd->rdat->axis[4] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[4],
+ dd->rdat->tchdat[i].id);
+ }
+
+ input_sync(dd->in_dev);
+
+ /* The release sync is sent on the fall-through */
+ input_mt_slot(dd->in_dev, 0);
+ input_mt_report_slot_state(dd->in_dev, MT_TOOL_FINGER,
+ false);
+ }
+ }
+
+ input_sync(dd->in_dev);
+#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY
+ if (atmxt_get_ic_state(dd) == ATMXT_IC_AOT)
+ notify_display_wakeup(TOUCH);
+#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */
+
+ /* Hold to allow events time to propagate up */
+ wake_lock_timeout(&dd->timed_lock, 0.5 * HZ);
+
+ return;
+}
+
+static void atmxt_release_touches(struct atmxt_driver_data *dd)
+{
+ int i = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: Releasing all touches...\n", __func__);
+
+ for (i = 0; i < ATMXT_MAX_TOUCHES; i++)
+ dd->rdat->tchdat[i].active = false;
+
+ atmxt_report_touches(dd);
+ dd->status = dd->status & ~(1 << ATMXT_REPORT_TOUCHES);
+
+ return;
+}
+
+static int atmxt_message_handler6(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Handling message type 6...\n", __func__);
+
+ if (size < 5) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_message_handler6_fail;
+ }
+
+ if (msg[1] & 0x80) {
+ printk(KERN_INFO "%s: Touch IC reset complete.\n", __func__);
+ dd->data->last_stat = 0x00;
+ }
+
+ if ((msg[1] & 0x40) && !(dd->data->last_stat & 0x40)) {
+ printk(KERN_ERR "%s: Acquisition cycle overflow.\n", __func__);
+ } else if (!(msg[1] & 0x40) && (dd->data->last_stat & 0x40)) {
+ printk(KERN_INFO "%s: Acquisition cycle now normal.\n",
+ __func__);
+ }
+
+ if ((msg[1] & 0x20) && !(dd->data->last_stat & 0x20)) {
+ printk(KERN_ERR "%s: Signal error in IC acquisition.\n",
+ __func__);
+ } else if (!(msg[1] & 0x20) && (dd->data->last_stat & 0x20)) {
+ printk(KERN_INFO "%s: IC acquisition signal now in range.\n",
+ __func__);
+ }
+
+ if ((msg[1] & 0x10) && !(dd->data->last_stat & 0x10)) {
+ printk(KERN_INFO "%s: Touch IC is calibrating.\n", __func__);
+ dd->status = dd->status | (1 << ATMXT_RECEIVED_CALIBRATION);
+ } else if (!(msg[1] & 0x10) && (dd->data->last_stat & 0x10)) {
+ printk(KERN_INFO "%s: Touch IC calibration complete.\n",
+ __func__);
+ dd->status = dd->status | (1 << ATMXT_RECEIVED_CALIBRATION);
+ }
+
+ if (msg[1] & 0x08) {
+ printk(KERN_ERR "%s: Hardware configuration error--%s.\n",
+ __func__, "check platform settings");
+ dd->data->last_stat = dd->data->last_stat & 0xF7;
+ } else if (!(msg[1] & 0x08) && (dd->data->last_stat & 0x08)) {
+ printk(KERN_INFO
+ "%s: Hardware configuration error corrected.\n",
+ __func__);
+ }
+
+ if (msg[1] & 0x04) {
+ printk(KERN_ERR "%s: IC reports I2C communication error.\n",
+ __func__);
+ }
+
+ if (msg[1] == dd->data->last_stat) {
+ printk(KERN_INFO "%s: Received checksum 0x%02X%02X%02X.\n",
+ __func__, msg[2], msg[3], msg[4]);
+ }
+
+ dd->data->last_stat = msg[1];
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if (dd->status & (1 << ATMXT_IGNORE_CHECKSUM))
+ goto atmxt_message_handler6_fail;
+#endif
+
+ if ((msg[2] != dd->nvm->chksum[0]) ||
+ (msg[3] != dd->nvm->chksum[1]) ||
+ (msg[4] != dd->nvm->chksum[2])) {
+ if (!(dd->status & (1 << ATMXT_CHECKSUM_FAILED))) {
+ printk(KERN_ERR "%s: IC settings checksum fail.\n",
+ __func__);
+ dd->status = dd->status | (1 << ATMXT_RESTART_REQUIRED);
+ dd->status = dd->status | (1 << ATMXT_CHECKSUM_FAILED);
+ } else {
+ printk(KERN_ERR "%s: IC settings checksum fail. %s\n",
+ __func__, "Sending settings (no backup)...");
+ err = atmxt_send_settings(dd, false);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to update IC settings.\n",
+ __func__);
+ goto atmxt_message_handler6_fail;
+ }
+ }
+ }
+
+atmxt_message_handler6_fail:
+ return err;
+}
+
+static int atmxt_message_handler9(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+ uint8_t tchidx = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Handling message type 9...\n", __func__);
+
+ if (size < 7) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_message_handler9_fail;
+ }
+
+ tchidx = msg[0] - dd->data->touch_id_offset;
+ if (tchidx >= ARRAY_SIZE(dd->rdat->tchdat)) {
+ printk(KERN_ERR "%s: Touch %hu is unsupported.\n",
+ __func__, tchidx);
+ err = -EOVERFLOW;
+ goto atmxt_message_handler9_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_REPORT_TOUCHES);
+
+ dd->rdat->tchdat[tchidx].id = tchidx;
+
+ dd->rdat->tchdat[tchidx].x = (msg[2] << 4) | ((msg[4] & 0xF0) >> 4);
+ if (!(dd->data->res[0]))
+ dd->rdat->tchdat[tchidx].x = dd->rdat->tchdat[tchidx].x >> 2;
+
+ dd->rdat->tchdat[tchidx].y = (msg[3] << 4) | (msg[4] & 0x0F);
+ if (!(dd->data->res[1]))
+ dd->rdat->tchdat[tchidx].y = dd->rdat->tchdat[tchidx].y >> 2;
+
+ dd->rdat->tchdat[tchidx].p = msg[6];
+ dd->rdat->tchdat[tchidx].w = msg[5];
+
+ if (((msg[1] & 0x40) && (msg[1] & 0x20)) ||
+ ((msg[1] & 0x40) && (msg[1] & 0x02)) ||
+ ((msg[1] & 0x20) && (msg[1] & 0x02))) {
+ printk(KERN_ERR "%s: System too slow %s 0x%02X.\n",
+ __func__, "to see all touch events for report id",
+ msg[0]);
+ }
+
+ if (msg[1] & 0x22) {
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: Touch ID %hu released.\n",
+ __func__, tchidx);
+ dd->rdat->tchdat[tchidx].active = false;
+ } else {
+ dd->rdat->tchdat[tchidx].active = true;
+ }
+
+ if (msg[1] & 0x02) {
+ printk(KERN_INFO "%s: Touch ID %hu suppressed.\n",
+ __func__, tchidx);
+ }
+
+atmxt_message_handler9_fail:
+ return err;
+}
+
+static int atmxt_message_handler42(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Handling message type 42...\n", __func__);
+
+ if (size < 2) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_message_handler42_fail;
+ }
+
+ if (msg[1] & 0x01) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Touch suppression is active.\n",
+ __func__);
+ input_report_key(dd->in_dev, KEY_SLEEP, 1);
+ } else {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Touch suppression is disabled.\n",
+ __func__);
+ input_report_key(dd->in_dev, KEY_SLEEP, 0);
+ }
+
+ /* Hold to allow events time to propagate up */
+ wake_lock_timeout(&dd->timed_lock, 0.5 * HZ);
+ input_sync(dd->in_dev);
+
+atmxt_message_handler42_fail:
+ return err;
+}
+
+static int atmxt_resume_restart(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Resume restarting IC...\n", __func__);
+
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status & ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+
+ err = atmxt_restart_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to restart the touch IC.\n",
+ __func__);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ goto atmxt_resume_restart_fail;
+ }
+
+ if (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))) {
+ if (atmxt_get_drv_state(dd) != ATMXT_DRV_ACTIVE)
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ dd->status = dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ }
+
+atmxt_resume_restart_fail:
+ return err;
+}
+
+static int atmxt_force_bootloader(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ uint8_t cmd = 0x00;
+ bool chg_used = false;
+ uint8_t chg_cmd[2] = {0x00, 0x00};
+ uint8_t rst[2] = {0x00, 0x00};
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == 18) {
+ if (dd->info_blk->data[i+3] < 1) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Comm is too small--%s.\n",
+ __func__, "will pool instead");
+ goto atmxt_force_bootloader_check_reset;
+ }
+ chg_cmd[0] = dd->info_blk->data[i+1] + 1;
+ chg_cmd[1] = dd->info_blk->data[i+2];
+ if (chg_cmd[0] < dd->info_blk->data[i+1])
+ chg_cmd[1]++;
+ break;
+ }
+ }
+
+ if ((chg_cmd[0] == 0x00) && (chg_cmd[1] == 0x00)) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: No interrupt force available--%s.\n",
+ __func__, "will pool instead");
+ goto atmxt_force_bootloader_check_reset;
+ }
+
+ cmd = 0x02;
+ err = atmxt_i2c_write(dd, chg_cmd[0], chg_cmd[1], &cmd, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to force interrupt low--%s.\n",
+ __func__, "will poll instead");
+ } else {
+ chg_used = true;
+ }
+
+atmxt_force_bootloader_check_reset:
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == 6) {
+ rst[0] = dd->info_blk->data[i+1];
+ rst[1] = dd->info_blk->data[i+2];
+ break;
+ }
+ }
+
+ if ((rst[0] == 0x00) && (rst[1] == 0x00)) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: No soft reset available--%s.\n", __func__,
+ "will try hardware recovery instead");
+ goto atmxt_force_bootloader_use_recov;
+ }
+
+ cmd = 0xA5;
+ err = atmxt_i2c_write(dd, rst[0], rst[1], &cmd, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to send flash reset command.\n",
+ __func__);
+ goto atmxt_force_bootloader_use_recov;
+ }
+
+ if (!chg_used) {
+ for (i = 0; i < 128; i++) {
+ if (gpio_get_value(dd->pdata->gpio_interrupt) == 1)
+ break;
+ else
+ udelay(2000);
+ }
+
+ if (i == 128) {
+ printk(KERN_ERR "%s: %s.\n", __func__,
+ "Waiting for flash reset timed out");
+ err = -ETIME;
+ goto atmxt_force_bootloader_exit;
+ }
+ }
+
+ goto atmxt_force_bootloader_exit;
+
+atmxt_force_bootloader_use_recov:
+ atmxt_dbg(dd, ATMXT_DBG2, "%s: Using hardware recovery...\n", __func__);
+ printk(KERN_ERR "%s: Forced hardware recovery failed--%s.\n",
+ __func__, "unable to reflash IC");
+ err = -ENOSYS;
+
+atmxt_force_bootloader_exit:
+ return err;
+}
+
+static bool atmxt_check_firmware_update(struct atmxt_driver_data *dd)
+{
+ bool update_fw = false;
+
+ if ((dd->util->fw[1] != dd->info_blk->header[0]) ||
+ (dd->util->fw[2] != dd->info_blk->header[1])) {
+ printk(KERN_ERR
+ "%s: Platform firmware does not match touch IC. %s\n",
+ __func__, "Unable to check for firmware update.");
+ goto atmxt_check_firmware_update_exit;
+ }
+
+ update_fw = !((dd->util->fw[3] == dd->info_blk->header[2]) &&
+ (dd->util->fw[4] == dd->info_blk->header[3]));
+
+atmxt_check_firmware_update_exit:
+ return update_fw;
+}
+
+static int atmxt_validate_firmware(uint8_t *data, uint32_t size)
+{
+ int err = 0;
+ uint32_t iter = 0;
+ int length = 0;
+
+ if (data == NULL || size == 0) {
+ printk(KERN_ERR "%s: No firmware data found.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_validate_firmware_fail;
+ }
+
+ if (data[0] < 4) {
+ printk(KERN_ERR "%s: Invalid firmware header.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_validate_firmware_fail;
+ } else if ((data[0] + 1) >= size) {
+ printk(KERN_ERR "%s: Firmware is malformed.\n", __func__);
+ err = -EOVERFLOW;
+ goto atmxt_validate_firmware_fail;
+ }
+
+ iter = iter + data[0] + 1;
+
+ while (iter < (size - 1)) {
+ length = (data[iter+0] << 8) | data[iter+1];
+ if ((iter + length + 2) > size) {
+ printk(KERN_ERR
+ "%s: Overflow in firmware image %s %u.\n",
+ __func__, "on iter", iter);
+ err = -EOVERFLOW;
+ goto atmxt_validate_firmware_fail;
+ }
+
+ iter = iter + length + 2;
+ }
+
+ if (iter != size) {
+ printk(KERN_ERR "%s: Firmware image misaligned.\n", __func__);
+ err = -ENOEXEC;
+ goto atmxt_validate_firmware_fail;
+ }
+
+atmxt_validate_firmware_fail:
+ return err;
+}
+
+static int atmxt_flash_firmware(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ uint8_t *img = dd->util->fw;
+ uint32_t size = dd->util->fw_size;
+ uint32_t iter = 0;
+ uint8_t status = 0x00;
+ bool irq_low = false;
+ bool frame_crc_failed = false;
+
+ printk(KERN_INFO "%s: Reflashing touch IC...\n", __func__);
+
+ err = atmxt_i2c_write(dd, 0xDC, 0xAA, NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to send unlock command.\n",
+ __func__);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ iter = img[0] + 1;
+ while (iter < (size - 1)) {
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low) {
+ printk(KERN_ERR
+ "%s: Timeout waiting %s for iter %u.\n",
+ __func__, "for frame interrupt", iter);
+ err = -ETIME;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_read(dd, &status, 1);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Error reading frame byte for iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ if ((status & 0xC0) != 0x80) {
+ printk(KERN_ERR "%s: %s 0x%02X %s %u.\n", __func__,
+ "Unexpected wait status", status,
+ "received for iter", iter);
+ err = -EPROTO;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_write(dd, img[iter+0], img[iter+1],
+ &(img[iter+2]), (img[iter+0] << 8) | img[iter+1]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error sending frame iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low) {
+ printk(KERN_ERR
+ "%s: Timeout waiting %s for iter %u.\n",
+ __func__, "for check interrupt", iter);
+ err = -ETIME;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_read(dd, &status, 1);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Error reading check byte for iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ if (status != 0x02) {
+ printk(KERN_ERR "%s: %s 0x%02X %s %u.\n", __func__,
+ "Unexpected frame status", status,
+ "received for iter", iter);
+ err = -EPROTO;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low) {
+ printk(KERN_ERR
+ "%s: Timeout waiting %s for iter %u.\n",
+ __func__, "for result interrupt", iter);
+ err = -ETIME;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_read(dd, &status, 1);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Error reading result byte for iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ if (status == 0x04) {
+ iter = iter + ((img[iter+0] << 8) | img[iter+1]) + 2;
+ frame_crc_failed = false;
+ } else if (!frame_crc_failed) {
+ printk(KERN_ERR "%s: %s %u--%s.\n",
+ __func__, "Frame CRC failed for iter",
+ iter, "will try to re-send");
+ frame_crc_failed = true;
+ } else {
+ printk(KERN_ERR "%s: %s %u--%s.\n",
+ __func__, "Frame CRC failed for iter",
+ iter, "check firmware image");
+ err = -ECOMM;
+ goto atmxt_flash_firmware_fail;
+ }
+ }
+
+atmxt_flash_firmware_fail:
+ return err;
+}
+
+static char *atmxt_msg2str(const uint8_t *msg, uint8_t size)
+{
+ char *str = NULL;
+ int i = 0;
+ int err = 0;
+
+ str = kzalloc(sizeof(char) * (size * 5), GFP_KERNEL);
+ if (str == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate message string.\n",
+ __func__);
+ goto atmxt_msg2str_exit;
+ }
+
+ for (i = 0; i < size; i++) {
+ err = sprintf(str, "%s0x%02X ", str, msg[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in sprintf on pass %d",
+ __func__, i);
+ goto atmxt_msg2str_exit;
+ }
+ }
+
+ str[err-1] = '\0';
+
+atmxt_msg2str_exit:
+ return str;
+}
+
+static bool atmxt_wait4irq(struct atmxt_driver_data *dd)
+{
+ bool irq_low = false;
+ int i = 0;
+
+ for (i = 0; i < 500; i++) {
+ if (gpio_get_value(dd->pdata->gpio_interrupt) != 0) {
+ msleep(20);
+ } else {
+ irq_low = true;
+ break;
+ }
+ }
+
+ return irq_low;
+}
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_drv_debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Debug Level: %hu\n", dd->dbg->dbg_lvl);
+}
+static ssize_t atmxt_debug_drv_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_drv_debug_store_exit;
+ }
+
+ if (value > 255) {
+ printk(KERN_ERR "%s: Invalid debug level %lu--setting to %u.\n",
+ __func__, value, ATMXT_DBG3);
+ dd->dbg->dbg_lvl = ATMXT_DBG3;
+ } else {
+ dd->dbg->dbg_lvl = value;
+ printk(KERN_INFO "%s: Debug level is now %hu.\n",
+ __func__, dd->dbg->dbg_lvl);
+ }
+
+ err = size;
+
+atmxt_debug_drv_debug_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_debug, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_debug_show, atmxt_debug_drv_debug_store);
+
+static ssize_t atmxt_debug_drv_flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Driver Flags: 0x%04X\n", dd->settings);
+}
+static ssize_t atmxt_debug_drv_flags_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 16, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_drv_flags_store_exit;
+ }
+
+ if (value > 65535) {
+ printk(KERN_ERR "%s: Invalid flag settings 0x%08lX passed.\n",
+ __func__, value);
+ err = -EOVERFLOW;
+ goto atmxt_debug_drv_flags_store_exit;
+ } else {
+ dd->settings = value;
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Driver flags now set to 0x%04X.\n",
+ __func__, dd->settings);
+ }
+
+ err = size;
+
+atmxt_debug_drv_flags_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_flags, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_flags_show, atmxt_debug_drv_flags_store);
+#endif
+
+static ssize_t atmxt_debug_drv_irq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))
+ return sprintf(buf, "Driver interrupt is ENABLED.\n");
+ else
+ return sprintf(buf, "Driver interrupt is DISABLED.\n");
+}
+static ssize_t atmxt_debug_drv_irq_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_drv_irq_store_exit;
+ }
+
+ if ((atmxt_get_drv_state(dd) != ATMXT_DRV_ACTIVE) &&
+ (atmxt_get_drv_state(dd) != ATMXT_DRV_IDLE)) {
+ printk(KERN_ERR "%s: %s %s or %s states.\n",
+ __func__, "Interrupt can be changed only in",
+ atmxt_driver_state_string[ATMXT_DRV_ACTIVE],
+ atmxt_driver_state_string[ATMXT_DRV_IDLE]);
+ err = -EACCES;
+ goto atmxt_debug_drv_irq_store_exit;
+ }
+
+ switch (value) {
+ case 0:
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ dd->status =
+ dd->status & ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+ break;
+
+ case 1:
+ if (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))) {
+ dd->status =
+ dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Invalid value passed.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_debug_drv_irq_store_exit;
+ }
+
+ err = size;
+
+atmxt_debug_drv_irq_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_irq, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_irq_show, atmxt_debug_drv_irq_store);
+
+static ssize_t atmxt_debug_drv_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Driver state is %s.\nIC state is %s.\n",
+ atmxt_driver_state_string[atmxt_get_drv_state(dd)],
+ atmxt_ic_state_string[atmxt_get_ic_state(dd)]);
+}
+static DEVICE_ATTR(drv_stat, S_IRUGO, atmxt_debug_drv_stat_show, NULL);
+
+
+static ssize_t atmxt_drv_interactivemode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ unsigned long value = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ int err = 0;
+
+ err = kstrtoul(buf, 10, &value);
+ if (err < 0) {
+ pr_err("%s: Failed to convert value.\n", __func__);
+ return err;
+ }
+
+ mutex_lock(dd->mutex);
+
+ switch (value) {
+ case 0:
+ err = atmxt_enter_aot(dd);
+ break;
+
+ case 1:
+ err = atmxt_exit_aot(dd);
+ break;
+
+ default:
+ pr_err("%s: Invalid value %lu passed.\n", __func__, value);
+ err = -EINVAL;
+ }
+
+ mutex_unlock(dd->mutex);
+ return err;
+}
+static ssize_t atmxt_drv_interactivemode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ int ic_state;
+ mutex_lock(dd->mutex);
+ ic_state = atmxt_get_ic_state(dd);
+ mutex_unlock(dd->mutex);
+ return snprintf(buf, PAGE_SIZE, "%s",
+ (ic_state == ATMXT_IC_ACTIVE ? "1" : "0"));
+}
+static DEVICE_ATTR(interactivemode, S_IRUGO | S_IWUSR | S_IWGRP,
+ atmxt_drv_interactivemode_show,
+ atmxt_drv_interactivemode_store);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_drv_tdat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ if (dd->status & (1 << ATMXT_WAITING_FOR_TDAT))
+ return sprintf(buf, "Driver is waiting for data load.\n");
+ else
+ return sprintf(buf, "No data loading in progress.\n");
+}
+static ssize_t atmxt_debug_drv_tdat_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ mutex_lock(dd->mutex);
+
+ if (dd->status & (1 << ATMXT_WAITING_FOR_TDAT)) {
+ printk(KERN_ERR "%s: Driver is already waiting for data.\n",
+ __func__);
+ err = -EALREADY;
+ goto atmxt_debug_drv_tdat_store_fail;
+ }
+
+ printk(KERN_INFO "%s: Enabling firmware class loader...\n", __func__);
+
+ err = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_NOHOTPLUG, "atmxt", &(dd->client->dev),
+ GFP_KERNEL, dd, atmxt_tdat_callback);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Firmware request failed with error code %d.\n",
+ __func__, err);
+ goto atmxt_debug_drv_tdat_store_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_WAITING_FOR_TDAT);
+ err = size;
+
+atmxt_debug_drv_tdat_store_fail:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_tdat, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_tdat_show, atmxt_debug_drv_tdat_store);
+#endif
+
+static ssize_t atmxt_debug_drv_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Driver: %s\nVersion: %s\nDate: %s\n",
+ ATMXT_I2C_NAME, ATMXT_DRIVER_VERSION, ATMXT_DRIVER_DATE);
+}
+static DEVICE_ATTR(drv_ver, S_IRUGO, atmxt_debug_drv_ver_show, NULL);
+
+static ssize_t atmxt_debug_hw_irqstat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ err = gpio_get_value(dd->pdata->gpio_interrupt);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to read irq level with error code %d.\n",
+ __func__, err);
+ err = sprintf(buf,
+ "Failed to read irq level with error code %d.\n",
+ err);
+ goto atmxt_debug_hw_irqstat_show_exit;
+ }
+
+ switch (err) {
+ case 0:
+ err = sprintf(buf, "Interrupt line is LOW.\n");
+ break;
+ case 1:
+ err = sprintf(buf, "Interrupt line is HIGH.\n");
+ break;
+ default:
+ err = sprintf(buf, "Read irq level of %d.\n", err);
+ break;
+ }
+
+atmxt_debug_hw_irqstat_show_exit:
+ return err;
+}
+static DEVICE_ATTR(hw_irqstat, S_IRUGO, atmxt_debug_hw_irqstat_show, NULL);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_hw_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ mutex_lock(dd->mutex);
+
+ if (atmxt_get_drv_state(dd) == ATMXT_DRV_INIT) {
+ printk(KERN_ERR "%s: %s %s.\n", __func__,
+ "Unable to restart IC in driver state",
+ atmxt_driver_state_string[ATMXT_DRV_INIT]);
+ err = -EACCES;
+ goto atmxt_debug_hw_reset_store_fail;
+ }
+
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status & ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+
+ err = atmxt_restart_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to %s with error code %d.\n",
+ __func__, "re-initialize the touch IC", err);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ goto atmxt_debug_hw_reset_store_fail;
+ }
+
+ if (((atmxt_get_drv_state(dd) == ATMXT_DRV_ACTIVE)) &&
+ (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)))) {
+ dd->status = dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ }
+
+ err = size;
+
+atmxt_debug_hw_reset_store_fail:
+ mutex_unlock(dd->mutex);
+ return err;
+}
+static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, atmxt_debug_hw_reset_store);
+#endif
+
+static ssize_t atmxt_debug_hw_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Touch Data File: %s\n", dd->pdata->filename);
+}
+static DEVICE_ATTR(hw_ver, S_IRUGO, atmxt_debug_hw_ver_show, NULL);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_ic_grpdata_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ int i = 0;
+ uint8_t addr_lo = 0x00;
+ uint8_t addr_hi = 0x00;
+ uint8_t *entry = NULL;
+ int size = 0;
+ uint8_t *data_in = NULL;
+
+ mutex_lock(dd->mutex);
+
+ if ((atmxt_get_ic_state(dd) != ATMXT_IC_ACTIVE) &&
+ (atmxt_get_ic_state(dd) != ATMXT_IC_SLEEP) &&
+ (atmxt_get_ic_state(dd) != ATMXT_IC_AOT)) {
+ pr_err("%s: %s %s, %s, or %s states.\n", __func__,
+ "Group data can be read only in IC",
+ atmxt_ic_state_string[ATMXT_IC_ACTIVE],
+ atmxt_ic_state_string[ATMXT_IC_SLEEP],
+ atmxt_ic_state_string[ATMXT_IC_AOT]);
+ err = sprintf(buf, "%s %s %s, or %s states.\n",
+ "Group data can be read only in IC",
+ atmxt_ic_state_string[ATMXT_IC_ACTIVE],
+ atmxt_ic_state_string[ATMXT_IC_SLEEP],
+ atmxt_ic_state_string[ATMXT_IC_AOT]);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == dd->dbg->grp_num) {
+ entry = &(dd->info_blk->data[i]);
+ break;
+ }
+ }
+
+ if (entry == NULL) {
+ printk(KERN_ERR "%s: Group %hu does not exist.\n",
+ __func__, dd->dbg->grp_num);
+ err = sprintf(buf, "Group %hu does not exist.\n",
+ dd->dbg->grp_num);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ if (dd->dbg->grp_off > entry[3]) {
+ printk(KERN_ERR "%s: Offset %hu exceeds group size of %u.\n",
+ __func__, dd->dbg->grp_off, entry[3]+1);
+ err = sprintf(buf, "Offset %hu exceeds group size of %u.\n",
+ dd->dbg->grp_off, entry[3]+1);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ addr_lo = entry[1] + dd->dbg->grp_off;
+ addr_hi = entry[2];
+ if (addr_lo < entry[1]) /* Check for 16-bit addr overflow */
+ addr_hi++;
+
+ err = atmxt_i2c_write(dd, addr_lo, addr_hi, NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to set group pointer with error code %d.\n",
+ __func__, err);
+ err = sprintf(buf,
+ "Failed to set group pointer with error code %d.\n",
+ err);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ size = entry[3] - dd->dbg->grp_off + 1;
+ data_in = kzalloc(sizeof(uint8_t) * size, GFP_KERNEL);
+ if (data_in == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate memory buffer.\n",
+ __func__);
+ err = sprintf(buf, "Unable to allocate memory buffer.\n");
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ err = atmxt_i2c_read(dd, data_in, size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read group data.\n", __func__);
+ err = sprintf(buf, "Failed to read group data.\n");
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ err = sprintf(buf, "Group %hu, Offset %hu:\n",
+ dd->dbg->grp_num, dd->dbg->grp_off);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in header sprintf.\n", __func__);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ for (i = 0; i < size; i++) {
+ err = sprintf(buf, "%s0x%02hX\n", buf, data_in[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in sprintf loop %d.\n",
+ __func__, i);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+ }
+
+ err = sprintf(buf, "%s(%u bytes)\n", buf, size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in byte count sprintf.\n", __func__);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+atmxt_debug_ic_grpdata_show_exit:
+ kfree(data_in);
+ mutex_unlock(dd->mutex);
+
+ return (ssize_t) err;
+}
+static ssize_t atmxt_debug_ic_grpdata_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ int i = 0;
+ uint8_t addr_lo = 0x00;
+ uint8_t addr_hi = 0x00;
+ uint8_t *entry = NULL;
+ int data_size = 0;
+ uint8_t *data_out = NULL;
+ unsigned long value = 0;
+ uint8_t *conv_buf = NULL;
+
+ mutex_lock(dd->mutex);
+
+ if ((atmxt_get_ic_state(dd) != ATMXT_IC_ACTIVE) &&
+ (atmxt_get_ic_state(dd) != ATMXT_IC_SLEEP) &&
+ (atmxt_get_ic_state(dd) != ATMXT_IC_AOT)) {
+ pr_err("%s: %s %s, %s, or %s states.\n", __func__,
+ "Group data can be written only in IC",
+ atmxt_ic_state_string[ATMXT_IC_ACTIVE],
+ atmxt_ic_state_string[ATMXT_IC_SLEEP],
+ atmxt_ic_state_string[ATMXT_IC_AOT]);
+ err = -EACCES;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == dd->dbg->grp_num) {
+ entry = &(dd->info_blk->data[i]);
+ break;
+ }
+ }
+
+ if (entry == NULL) {
+ printk(KERN_ERR "%s: Group %hu does not exist.\n",
+ __func__, dd->dbg->grp_num);
+ err = -ENOENT;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ if ((dd->dbg->grp_off) > entry[3]) {
+ printk(KERN_ERR "%s: Offset %hu exceeds data size.\n",
+ __func__, dd->dbg->grp_off);
+ err = -EOVERFLOW;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ if ((size % 5 != 0) || (size == 0)) {
+ printk(KERN_ERR "%s: Invalid data format. %s\n",
+ __func__, "Use \"0xHH,0xHH,...,0xHH\" instead.");
+ err = -EINVAL;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ data_out = kzalloc(sizeof(uint8_t) * (size / 5), GFP_KERNEL);
+ if (data_out == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate output buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ conv_buf = kzalloc(sizeof(uint8_t) * 5, GFP_KERNEL);
+ if (conv_buf == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate conversion buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ for (i = 0; i < size; i += 5) {
+ memcpy(conv_buf, &(buf[i]), 4);
+ err = strict_strtoul(conv_buf, 16, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Argument conversion failed.\n",
+ __func__);
+ goto atmxt_debug_ic_grpdata_store_exit;
+ } else if (value > 255) {
+ printk(KERN_ERR "%s: Value 0x%lX is too large.\n",
+ __func__, value);
+ err = -EOVERFLOW;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ data_out[data_size] = value;
+ data_size++;
+ }
+
+ if ((dd->dbg->grp_off + data_size) > (entry[3] + 1)) {
+ printk(KERN_ERR "%s: Trying to write %d bytes at offset %hu, "
+ "which exceeds group size of %hu.\n", __func__,
+ data_size, dd->dbg->grp_off, entry[3]+1);
+ err = -EOVERFLOW;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ addr_lo = entry[1] + dd->dbg->grp_off;
+ addr_hi = entry[2];
+ if (addr_lo < entry[1]) /* Check for 16-bit addr overflow */
+ addr_hi++;
+
+ err = atmxt_i2c_write(dd, addr_lo, addr_hi, data_out, data_size);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to write data with error code %d.\n",
+ __func__, err);
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ if (!(dd->status & (1 << ATMXT_IGNORE_CHECKSUM))) {
+ printk(KERN_INFO
+ "%s: Disabled settings checksum verification %s.\n",
+ __func__, "until next boot");
+ }
+ dd->status = dd->status | (1 << ATMXT_IGNORE_CHECKSUM);
+
+ err = size;
+
+atmxt_debug_ic_grpdata_store_exit:
+ kfree(data_out);
+ kfree(conv_buf);
+ mutex_unlock(dd->mutex);
+
+ return (ssize_t) err;
+}
+static DEVICE_ATTR(ic_grpdata, S_IRUSR | S_IWUSR,
+ atmxt_debug_ic_grpdata_show, atmxt_debug_ic_grpdata_store);
+
+static ssize_t atmxt_debug_ic_grpnum_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Group: %hu\n", dd->dbg->grp_num);
+}
+static ssize_t atmxt_debug_ic_grpnum_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_ic_grpnum_store_exit;
+ }
+
+ if (value > 255) {
+ printk(KERN_ERR "%s: Invalid group number %lu--%s.\n",
+ __func__, value, "setting to 255");
+ dd->dbg->grp_num = 255;
+ } else {
+ dd->dbg->grp_num = value;
+ }
+
+ err = size;
+
+atmxt_debug_ic_grpnum_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(ic_grpnum, S_IRUSR | S_IWUSR,
+ atmxt_debug_ic_grpnum_show, atmxt_debug_ic_grpnum_store);
+
+static ssize_t atmxt_debug_ic_grpoffset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Offset: %hu\n", dd->dbg->grp_off);
+}
+static ssize_t atmxt_debug_ic_grpoffset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_ic_grpoffset_store_exit;
+ }
+
+ if (value > 255) {
+ printk(KERN_ERR "%s: Invalid offset %lu--setting to 255.\n",
+ __func__, value);
+ dd->dbg->grp_off = 255;
+ } else {
+ dd->dbg->grp_off = value;
+ }
+
+ err = size;
+
+atmxt_debug_ic_grpoffset_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(ic_grpoffset, S_IRUSR | S_IWUSR,
+ atmxt_debug_ic_grpoffset_show, atmxt_debug_ic_grpoffset_store);
+#endif
+
+static ssize_t atmxt_debug_ic_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ if (dd->info_blk == NULL) {
+ return sprintf(buf,
+ "No touch IC version information is available.\n");
+ } else {
+ return sprintf(buf, "%s0x%02X\n%s0x%02X\n%s0x%02X\n%s0x%02X\n",
+ "Family ID: ", dd->info_blk->header[0],
+ "Variant ID: ", dd->info_blk->header[1],
+ "Version: ", dd->info_blk->header[2],
+ "Build: ", dd->info_blk->header[3]);
+ }
+}
+static DEVICE_ATTR(ic_ver, S_IRUGO, atmxt_debug_ic_ver_show, NULL);
+
+static int atmxt_create_sysfs_files(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int check = 0;
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_debug);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_debug.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_flags);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_flags.\n", __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_irq);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_irq.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_stat);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_stat.\n", __func__);
+ err = check;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_tdat);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_tdat.\n", __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_ver);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_ver.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_hw_irqstat);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create hw_irqstat.\n", __func__);
+ err = check;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_hw_reset);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create hw_reset.\n", __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_hw_ver);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create hw_ver.\n", __func__);
+ err = check;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_grpdata);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_grpdata.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_grpnum);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_grpnum.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_grpoffset);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_grpoffset.\n",
+ __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_ver);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_ver.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev),
+ &dev_attr_interactivemode);
+ if (check < 0) {
+ pr_err("%s: Failed to create ic_ver.\n", __func__);
+ err = check;
+ }
+
+ return err;
+}
+
+static void atmxt_remove_sysfs_files(struct atmxt_driver_data *dd)
+{
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_debug);
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_flags);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_irq);
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_stat);
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_tdat);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_ver);
+ device_remove_file(&(dd->client->dev), &dev_attr_hw_irqstat);
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_hw_reset);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_hw_ver);
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_grpdata);
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_grpnum);
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_grpoffset);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_ver);
+ device_remove_file(&(dd->client->dev), &dev_attr_interactivemode);
+ return;
+}
diff --git a/drivers/input/touchscreen/atmxt.h b/drivers/input/touchscreen/atmxt.h
new file mode 100644
index 00000000000..04b9fc7f20e
--- /dev/null
+++ b/drivers/input/touchscreen/atmxt.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2010-2014 Motorola Mobility, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/* Local header for Atmel maXTouch touchscreens that uses tdat files */
+#ifndef _LINUX_ATMXT_H
+#define _LINUX_ATMXT_H
+
+#include <linux/types.h>
+#include <linux/input/touch_platform.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+#define ATMXT_DRIVER_VERSION "YN-04-01"
+#define ATMXT_DRIVER_DATE "2012-06-28"
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+#define atmxt_dbg(dd, level, format, args...) \
+{\
+ if ((dd->dbg->dbg_lvl) >= level) \
+ printk(KERN_INFO format, ## args); \
+}
+#else
+#define atmxt_dbg(dd, level, format, args...) {}
+#endif
+
+#define ATMXT_DBG0 0
+#define ATMXT_DBG1 1
+#define ATMXT_DBG2 2
+#define ATMXT_DBG3 3
+
+#define ATMXT_IRQ_ENABLED_FLAG 0
+#define ATMXT_WAITING_FOR_TDAT 1
+#define ATMXT_CHECKSUM_FAILED 2
+#define ATMXT_IGNORE_CHECKSUM 3
+#define ATMXT_REPORT_TOUCHES 4
+#define ATMXT_FIXING_CALIBRATION 5
+#define ATMXT_RECEIVED_CALIBRATION 6
+#define ATMXT_RESTART_REQUIRED 7
+#define ATMXT_SET_MESSAGE_POINTER 8
+#define ATMXT_RESUME_HANDLE_ISR 9
+
+#define ATMXT_I2C_ATTEMPTS 10
+#define ATMXT_I2C_WAIT_TIME 50
+#define ATMXT_I2C_WAIT_TIME_MAX 75
+#define ATMXT_MAX_TOUCHES 10
+#define ATMXT_ABS_RESERVED 0xFFFF
+#define ATMXT_IC_RESET_HOLD_TIME 1000
+
+
+enum atmxt_driver_state {
+ ATMXT_DRV_ACTIVE,
+ ATMXT_DRV_IDLE,
+ ATMXT_DRV_REFLASH,
+ ATMXT_DRV_INIT,
+ ATMXT_DRV_SUSPENDED,
+};
+static const char * const atmxt_driver_state_string[] = {
+ "ACTIVE",
+ "IDLE",
+ "REFLASH",
+ "INIT",
+ "SUSPENDED",
+};
+
+enum atmxt_ic_state {
+ ATMXT_IC_ACTIVE,
+ ATMXT_IC_SLEEP,
+ ATMXT_IC_UNKNOWN,
+ ATMXT_IC_BOOTLOADER,
+ ATMXT_IC_PRESENT,
+ ATMXT_IC_AOT,
+};
+static const char * const atmxt_ic_state_string[] = {
+ "ACTIVE",
+ "SLEEP",
+ "UNKNOWN",
+ "BOOTLOADER",
+ "PRESENT",
+ "AOT",
+};
+
+
+struct atmxt_util_data {
+ uint8_t *data;
+ size_t size;
+ uint8_t *tsett;
+ uint32_t tsett_size;
+ uint8_t *fw;
+ uint32_t fw_size;
+ uint8_t addr[2];
+} __packed;
+
+struct atmxt_nvm {
+ uint8_t *data;
+ uint16_t size;
+ uint8_t addr[2];
+ uint8_t chksum[3];
+} __packed;
+
+struct atmxt_info_block {
+ uint8_t header[7];
+ uint8_t *data;
+ uint8_t size;
+ uint8_t *msg_id;
+ uint8_t id_size;
+} __packed;
+
+struct atmxt_addr {
+ uint8_t msg[2];
+ uint8_t pwr[2];
+ uint8_t rst[2];
+ uint8_t nvm[2];
+ uint8_t cal[2];
+ uint8_t acq[2];
+ uint8_t adx[2];
+ uint8_t gse[2];
+ uint8_t tse[2];
+ uint8_t mxd[2];
+ uint8_t sup[2];
+} __packed;
+
+struct atmxt_data {
+ uint8_t pwr[4];
+ uint8_t max_msg_size;
+ uint8_t touch_id_offset;
+ bool res[2];
+ uint8_t acq[6];
+ uint8_t adx[2];
+ uint8_t mxd;
+ uint8_t sup[5];
+ unsigned long timer;
+ uint8_t last_stat;
+} __packed;
+
+struct atmxt_touch_data {
+ bool active;
+ uint16_t x;
+ uint16_t y;
+ uint8_t p;
+ uint8_t w;
+ uint8_t id;
+} __packed;
+
+struct atmxt_report_data {
+ uint16_t axis[5];
+ uint8_t active_touches;
+ struct atmxt_touch_data tchdat[ATMXT_MAX_TOUCHES];
+} __packed;
+
+struct atmxt_debug {
+ uint8_t dbg_lvl;
+ uint8_t grp_num;
+ uint8_t grp_off;
+} __packed;
+
+
+struct atmxt_driver_data {
+ struct touch_platform_data *pdata;
+ struct atmxt_util_data *util;
+ struct i2c_client *client;
+ struct mutex *mutex;
+ struct input_dev *in_dev;
+ struct wake_lock timed_lock;
+
+ enum atmxt_driver_state drv_stat;
+ enum atmxt_ic_state ic_stat;
+
+ struct atmxt_info_block *info_blk;
+ struct atmxt_nvm *nvm;
+ struct atmxt_addr *addr;
+ struct atmxt_data *data;
+ struct atmxt_report_data *rdat;
+ struct atmxt_debug *dbg;
+
+ uint16_t status;
+ uint16_t settings;
+} __packed;
+
+#endif /* _LINUX_ATMXT_H */