diff options
Diffstat (limited to 'drivers/input')
| -rw-r--r-- | drivers/input/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/input/Makefile | 1 | ||||
| -rw-r--r-- | drivers/input/evdev.c | 53 | ||||
| -rw-r--r-- | drivers/input/keyreset.c | 239 | ||||
| -rw-r--r-- | drivers/input/misc/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/input/misc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/input/misc/gpio_axis.c | 192 | ||||
| -rw-r--r-- | drivers/input/misc/gpio_event.c | 228 | ||||
| -rw-r--r-- | drivers/input/misc/gpio_input.c | 390 | ||||
| -rw-r--r-- | drivers/input/misc/gpio_matrix.c | 441 | ||||
| -rw-r--r-- | drivers/input/misc/gpio_output.c | 97 | ||||
| -rw-r--r-- | drivers/input/misc/keychord.c | 391 | ||||
| -rw-r--r-- | drivers/input/touchscreen/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
| -rw-r--r-- | drivers/input/touchscreen/atmxt.c | 4154 | ||||
| -rw-r--r-- | drivers/input/touchscreen/atmxt.h | 197 | 
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 */  |