diff options
Diffstat (limited to 'drivers/input')
46 files changed, 5777 insertions, 899 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 2ee6c7a68bd..054edf346e0 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -10,7 +10,8 @@  #define EVDEV_MINOR_BASE	64  #define EVDEV_MINORS		32 -#define EVDEV_BUFFER_SIZE	64 +#define EVDEV_MIN_BUFFER_SIZE	64U +#define EVDEV_BUF_PACKETS	8  #include <linux/poll.h>  #include <linux/sched.h> @@ -23,7 +24,6 @@  #include "input-compat.h"  struct evdev { -	int exist;  	int open;  	int minor;  	struct input_handle handle; @@ -33,16 +33,18 @@ struct evdev {  	spinlock_t client_lock; /* protects client_list */  	struct mutex mutex;  	struct device dev; +	bool exist;  };  struct evdev_client { -	struct input_event buffer[EVDEV_BUFFER_SIZE];  	int head;  	int tail;  	spinlock_t buffer_lock; /* protects access to buffer, head and tail */  	struct fasync_struct *fasync;  	struct evdev *evdev;  	struct list_head node; +	int bufsize; +	struct input_event buffer[];  };  static struct evdev *evdev_table[EVDEV_MINORS]; @@ -52,11 +54,15 @@ static void evdev_pass_event(struct evdev_client *client,  			     struct input_event *event)  {  	/* -	 * Interrupts are disabled, just acquire the lock +	 * Interrupts are disabled, just acquire the lock. +	 * Make sure we don't leave with the client buffer +	 * "empty" by having client->head == client->tail.  	 */  	spin_lock(&client->buffer_lock); -	client->buffer[client->head++] = *event; -	client->head &= EVDEV_BUFFER_SIZE - 1; +	do { +		client->buffer[client->head++] = *event; +		client->head &= client->bufsize - 1; +	} while (client->head == client->tail);  	spin_unlock(&client->buffer_lock);  	if (event->type == EV_SYN) @@ -242,11 +248,21 @@ static int evdev_release(struct inode *inode, struct file *file)  	return 0;  } +static unsigned int evdev_compute_buffer_size(struct input_dev *dev) +{ +	unsigned int n_events = +		max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS, +		    EVDEV_MIN_BUFFER_SIZE); + +	return roundup_pow_of_two(n_events); +} +  static int evdev_open(struct inode *inode, struct file *file)  {  	struct evdev *evdev;  	struct evdev_client *client;  	int i = iminor(inode) - EVDEV_MINOR_BASE; +	unsigned int bufsize;  	int error;  	if (i >= EVDEV_MINORS) @@ -263,12 +279,17 @@ static int evdev_open(struct inode *inode, struct file *file)  	if (!evdev)  		return -ENODEV; -	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); +	bufsize = evdev_compute_buffer_size(evdev->handle.dev); + +	client = kzalloc(sizeof(struct evdev_client) + +				bufsize * sizeof(struct input_event), +			 GFP_KERNEL);  	if (!client) {  		error = -ENOMEM;  		goto err_put_evdev;  	} +	client->bufsize = bufsize;  	spin_lock_init(&client->buffer_lock);  	client->evdev = evdev;  	evdev_attach_client(evdev, client); @@ -334,7 +355,7 @@ static int evdev_fetch_next_event(struct evdev_client *client,  	have_event = client->head != client->tail;  	if (have_event) {  		*event = client->buffer[client->tail++]; -		client->tail &= EVDEV_BUFFER_SIZE - 1; +		client->tail &= client->bufsize - 1;  	}  	spin_unlock_irq(&client->buffer_lock); @@ -382,10 +403,15 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)  {  	struct evdev_client *client = file->private_data;  	struct evdev *evdev = client->evdev; +	unsigned int mask;  	poll_wait(file, &evdev->wait, wait); -	return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | -		(evdev->exist ? 0 : (POLLHUP | POLLERR)); + +	mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; +	if (client->head != client->tail) +		mask |= POLLIN | POLLRDNORM; + +	return mask;  }  #ifdef CONFIG_COMPAT @@ -665,6 +691,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,  								  sizeof(struct input_absinfo))))  					return -EFAULT; +				/* We can't change number of reserved MT slots */ +				if (t == ABS_MT_SLOT) +					return -EINVAL; +  				/*  				 * Take event lock to ensure that we are not  				 * changing device parameters in the middle @@ -768,7 +798,7 @@ static void evdev_remove_chrdev(struct evdev *evdev)  static void evdev_mark_dead(struct evdev *evdev)  {  	mutex_lock(&evdev->mutex); -	evdev->exist = 0; +	evdev->exist = false;  	mutex_unlock(&evdev->mutex);  } @@ -817,7 +847,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,  	init_waitqueue_head(&evdev->wait);  	dev_set_name(&evdev->dev, "event%d", minor); -	evdev->exist = 1; +	evdev->exist = true;  	evdev->minor = minor;  	evdev->handle.dev = input_get_device(dev); diff --git a/drivers/input/input.c b/drivers/input/input.c index 9c79bd56b51..e1243b4b32a 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -33,25 +33,6 @@ MODULE_LICENSE("GPL");  #define INPUT_DEVICES	256 -/* - * EV_ABS events which should not be cached are listed here. - */ -static unsigned int input_abs_bypass_init_data[] __initdata = { -	ABS_MT_TOUCH_MAJOR, -	ABS_MT_TOUCH_MINOR, -	ABS_MT_WIDTH_MAJOR, -	ABS_MT_WIDTH_MINOR, -	ABS_MT_ORIENTATION, -	ABS_MT_POSITION_X, -	ABS_MT_POSITION_Y, -	ABS_MT_TOOL_TYPE, -	ABS_MT_BLOB_ID, -	ABS_MT_TRACKING_ID, -	ABS_MT_PRESSURE, -	0 -}; -static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)]; -  static LIST_HEAD(input_dev_list);  static LIST_HEAD(input_handler_list); @@ -181,6 +162,56 @@ static void input_stop_autorepeat(struct input_dev *dev)  #define INPUT_PASS_TO_DEVICE	2  #define INPUT_PASS_TO_ALL	(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) +static int input_handle_abs_event(struct input_dev *dev, +				  unsigned int code, int *pval) +{ +	bool is_mt_event; +	int *pold; + +	if (code == ABS_MT_SLOT) { +		/* +		 * "Stage" the event; we'll flush it later, when we +		 * get actiual touch data. +		 */ +		if (*pval >= 0 && *pval < dev->mtsize) +			dev->slot = *pval; + +		return INPUT_IGNORE_EVENT; +	} + +	is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + +	if (!is_mt_event) { +		pold = &dev->abs[code]; +	} else if (dev->mt) { +		struct input_mt_slot *mtslot = &dev->mt[dev->slot]; +		pold = &mtslot->abs[code - ABS_MT_FIRST]; +	} else { +		/* +		 * Bypass filtering for multitouch events when +		 * not employing slots. +		 */ +		pold = NULL; +	} + +	if (pold) { +		*pval = input_defuzz_abs_event(*pval, *pold, +						dev->absfuzz[code]); +		if (*pold == *pval) +			return INPUT_IGNORE_EVENT; + +		*pold = *pval; +	} + +	/* Flush pending "slot" event */ +	if (is_mt_event && dev->slot != dev->abs[ABS_MT_SLOT]) { +		dev->abs[ABS_MT_SLOT] = dev->slot; +		input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot); +	} + +	return INPUT_PASS_TO_HANDLERS; +} +  static void input_handle_event(struct input_dev *dev,  			       unsigned int type, unsigned int code, int value)  { @@ -196,12 +227,12 @@ static void input_handle_event(struct input_dev *dev,  		case SYN_REPORT:  			if (!dev->sync) { -				dev->sync = 1; +				dev->sync = true;  				disposition = INPUT_PASS_TO_HANDLERS;  			}  			break;  		case SYN_MT_REPORT: -			dev->sync = 0; +			dev->sync = false;  			disposition = INPUT_PASS_TO_HANDLERS;  			break;  		} @@ -233,21 +264,9 @@ static void input_handle_event(struct input_dev *dev,  		break;  	case EV_ABS: -		if (is_event_supported(code, dev->absbit, ABS_MAX)) { - -			if (test_bit(code, input_abs_bypass)) { -				disposition = INPUT_PASS_TO_HANDLERS; -				break; -			} +		if (is_event_supported(code, dev->absbit, ABS_MAX)) +			disposition = input_handle_abs_event(dev, code, &value); -			value = input_defuzz_abs_event(value, -					dev->abs[code], dev->absfuzz[code]); - -			if (dev->abs[code] != value) { -				dev->abs[code] = value; -				disposition = INPUT_PASS_TO_HANDLERS; -			} -		}  		break;  	case EV_REL: @@ -298,7 +317,7 @@ static void input_handle_event(struct input_dev *dev,  	}  	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) -		dev->sync = 0; +		dev->sync = false;  	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)  		dev->event(dev, type, code, value); @@ -528,12 +547,30 @@ void input_close_device(struct input_handle *handle)  EXPORT_SYMBOL(input_close_device);  /* + * Simulate keyup events for all keys that are marked as pressed. + * The function must be called with dev->event_lock held. + */ +static void input_dev_release_keys(struct input_dev *dev) +{ +	int code; + +	if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { +		for (code = 0; code <= KEY_MAX; code++) { +			if (is_event_supported(code, dev->keybit, KEY_MAX) && +			    __test_and_clear_bit(code, dev->key)) { +				input_pass_event(dev, EV_KEY, code, 0); +			} +		} +		input_pass_event(dev, EV_SYN, SYN_REPORT, 1); +	} +} + +/*   * Prepare device for unregistering   */  static void input_disconnect_device(struct input_dev *dev)  {  	struct input_handle *handle; -	int code;  	/*  	 * Mark device as going away. Note that we take dev->mutex here @@ -552,15 +589,7 @@ static void input_disconnect_device(struct input_dev *dev)  	 * generate events even after we done here but they will not  	 * reach any handlers.  	 */ -	if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { -		for (code = 0; code <= KEY_MAX; code++) { -			if (is_event_supported(code, dev->keybit, KEY_MAX) && -			    __test_and_clear_bit(code, dev->key)) { -				input_pass_event(dev, EV_KEY, code, 0); -			} -		} -		input_pass_event(dev, EV_SYN, SYN_REPORT, 1); -	} +	input_dev_release_keys(dev);  	list_for_each_entry(handle, &dev->h_list, d_node)  		handle->open = 0; @@ -684,7 +713,7 @@ int input_set_keycode(struct input_dev *dev,  		      unsigned int scancode, unsigned int keycode)  {  	unsigned long flags; -	int old_keycode; +	unsigned int old_keycode;  	int retval;  	if (keycode > KEY_MAX) @@ -1278,6 +1307,7 @@ static void input_dev_release(struct device *device)  	struct input_dev *dev = to_input_dev(device);  	input_ff_destroy(dev); +	input_mt_destroy_slots(dev);  	kfree(dev);  	module_put(THIS_MODULE); @@ -1433,6 +1463,15 @@ static int input_dev_resume(struct device *dev)  	mutex_lock(&input_dev->mutex);  	input_dev_reset(input_dev, true); + +	/* +	 * Keys that have been pressed at suspend time are unlikely +	 * to be still pressed when we resume. +	 */ +	spin_lock_irq(&input_dev->event_lock); +	input_dev_release_keys(input_dev); +	spin_unlock_irq(&input_dev->event_lock); +  	mutex_unlock(&input_dev->mutex);  	return 0; @@ -1518,6 +1557,45 @@ void input_free_device(struct input_dev *dev)  EXPORT_SYMBOL(input_free_device);  /** + * input_mt_create_slots() - create MT input slots + * @dev: input device supporting MT events and finger tracking + * @num_slots: number of slots used by the device + * + * This function allocates all necessary memory for MT slot handling + * in the input device, and adds ABS_MT_SLOT to the device capabilities. + */ +int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) +{ +	if (!num_slots) +		return 0; + +	dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); +	if (!dev->mt) +		return -ENOMEM; + +	dev->mtsize = num_slots; +	input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + +	return 0; +} +EXPORT_SYMBOL(input_mt_create_slots); + +/** + * input_mt_destroy_slots() - frees the MT slots of the input device + * @dev: input device with allocated MT slots + * + * This function is only needed in error path as the input core will + * automatically free the MT slots when the device is destroyed. + */ +void input_mt_destroy_slots(struct input_dev *dev) +{ +	kfree(dev->mt); +	dev->mt = NULL; +	dev->mtsize = 0; +} +EXPORT_SYMBOL(input_mt_destroy_slots); + +/**   * input_set_capability - mark device as capable of a certain event   * @dev: device that is capable of emitting or accepting event   * @type: type of the event (EV_KEY, EV_REL, etc...) @@ -1926,20 +2004,10 @@ static const struct file_operations input_fops = {  	.open = input_open_file,  }; -static void __init input_init_abs_bypass(void) -{ -	const unsigned int *p; - -	for (p = input_abs_bypass_init_data; *p; p++) -		input_abs_bypass[BIT_WORD(*p)] |= BIT_MASK(*p); -} -  static int __init input_init(void)  {  	int err; -	input_init_abs_bypass(); -  	err = class_register(&input_class);  	if (err) {  		printk(KERN_ERR "input: unable to register input_dev class\n"); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 34157bb97ed..63834585c28 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -37,7 +37,6 @@ MODULE_LICENSE("GPL");  #define JOYDEV_BUFFER_SIZE	64  struct joydev { -	int exist;  	int open;  	int minor;  	struct input_handle handle; @@ -46,6 +45,7 @@ struct joydev {  	spinlock_t client_lock; /* protects client_list */  	struct mutex mutex;  	struct device dev; +	bool exist;  	struct js_corr corr[ABS_CNT];  	struct JS_DATA_SAVE_TYPE glue; @@ -760,7 +760,7 @@ static void joydev_remove_chrdev(struct joydev *joydev)  static void joydev_mark_dead(struct joydev *joydev)  {  	mutex_lock(&joydev->mutex); -	joydev->exist = 0; +	joydev->exist = false;  	mutex_unlock(&joydev->mutex);  } @@ -817,10 +817,9 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,  	init_waitqueue_head(&joydev->wait);  	dev_set_name(&joydev->dev, "js%d", minor); -	joydev->exist = 1; +	joydev->exist = true;  	joydev->minor = minor; -	joydev->exist = 1;  	joydev->handle.dev = input_get_device(dev);  	joydev->handle.name = dev_name(&joydev->dev);  	joydev->handle.handler = handler; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index c1087ce4cef..269a846f369 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -9,6 +9,7 @@   *               2005 Dominic Cerquetti <binary1230@yahoo.com>   *               2006 Adam Buchbinder <adam.buchbinder@gmail.com>   *               2007 Jan Kratochvil <honza@jikos.cz> + *               2010 Christoph Fritz <chf.fritz@googlemail.com>   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License as @@ -88,6 +89,9 @@     but we map them to axes when possible to simplify things */  #define MAP_DPAD_TO_BUTTONS		(1 << 0)  #define MAP_TRIGGERS_TO_BUTTONS		(1 << 1) +#define MAP_STICKS_TO_NULL		(1 << 2) +#define DANCEPAD_MAP_CONFIG	(MAP_DPAD_TO_BUTTONS |			\ +				MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)  #define XTYPE_XBOX        0  #define XTYPE_XBOX360     1 @@ -102,6 +106,10 @@ static int triggers_to_buttons;  module_param(triggers_to_buttons, bool, S_IRUGO);  MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads"); +static int sticks_to_null; +module_param(sticks_to_null, bool, S_IRUGO); +MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads"); +  static const struct xpad_device {  	u16 idVendor;  	u16 idProduct; @@ -114,7 +122,7 @@ static const struct xpad_device {  	{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },  	{ 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },  	{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, -	{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, +	{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX },  	{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },  	{ 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },  	{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, @@ -151,6 +159,7 @@ static const struct xpad_device {  	{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },  	{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },  	{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, +	{ 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },  	{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },  	{ 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }  }; @@ -158,7 +167,7 @@ static const struct xpad_device {  /* buttons shared with xbox and xbox360 */  static const signed short xpad_common_btn[] = {  	BTN_A, BTN_B, BTN_X, BTN_Y,			/* "analog" buttons */ -	BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR,	/* start/back/sticks */ +	BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR,	/* start/back/sticks */  	-1						/* terminating entry */  }; @@ -168,10 +177,10 @@ static const signed short xpad_btn[] = {  	-1			/* terminating entry */  }; -/* used when dpad is mapped to nuttons */ +/* used when dpad is mapped to buttons */  static const signed short xpad_btn_pad[] = { -	BTN_LEFT, BTN_RIGHT,		/* d-pad left, right */ -	BTN_0, BTN_1,			/* d-pad up, down (XXX names??) */ +	BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2,		/* d-pad left, right */ +	BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4,		/* d-pad up, down */  	-1				/* terminating entry */  }; @@ -279,17 +288,19 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d  {  	struct input_dev *dev = xpad->dev; -	/* left stick */ -	input_report_abs(dev, ABS_X, -			 (__s16) le16_to_cpup((__le16 *)(data + 12))); -	input_report_abs(dev, ABS_Y, -			 ~(__s16) le16_to_cpup((__le16 *)(data + 14))); +	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { +		/* left stick */ +		input_report_abs(dev, ABS_X, +				 (__s16) le16_to_cpup((__le16 *)(data + 12))); +		input_report_abs(dev, ABS_Y, +				 ~(__s16) le16_to_cpup((__le16 *)(data + 14))); -	/* right stick */ -	input_report_abs(dev, ABS_RX, -			 (__s16) le16_to_cpup((__le16 *)(data + 16))); -	input_report_abs(dev, ABS_RY, -			 ~(__s16) le16_to_cpup((__le16 *)(data + 18))); +		/* right stick */ +		input_report_abs(dev, ABS_RX, +				 (__s16) le16_to_cpup((__le16 *)(data + 16))); +		input_report_abs(dev, ABS_RY, +				 ~(__s16) le16_to_cpup((__le16 *)(data + 18))); +	}  	/* triggers left/right */  	if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { @@ -302,10 +313,11 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d  	/* digital pad */  	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { -		input_report_key(dev, BTN_LEFT,  data[2] & 0x04); -		input_report_key(dev, BTN_RIGHT, data[2] & 0x08); -		input_report_key(dev, BTN_0,     data[2] & 0x01); /* up */ -		input_report_key(dev, BTN_1,     data[2] & 0x02); /* down */ +		/* dpad as buttons (left, right, up, down) */ +		input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); +		input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); +		input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); +		input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);  	} else {  		input_report_abs(dev, ABS_HAT0X,  				 !!(data[2] & 0x08) - !!(data[2] & 0x04)); @@ -315,7 +327,7 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d  	/* start/back buttons and stick press left/right */  	input_report_key(dev, BTN_START,  data[2] & 0x10); -	input_report_key(dev, BTN_BACK,   data[2] & 0x20); +	input_report_key(dev, BTN_SELECT, data[2] & 0x20);  	input_report_key(dev, BTN_THUMBL, data[2] & 0x40);  	input_report_key(dev, BTN_THUMBR, data[2] & 0x80); @@ -349,11 +361,11 @@ static void xpad360_process_packet(struct usb_xpad *xpad,  	/* digital pad */  	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { -		/* dpad as buttons (right, left, down, up) */ -		input_report_key(dev, BTN_LEFT, data[2] & 0x04); -		input_report_key(dev, BTN_RIGHT, data[2] & 0x08); -		input_report_key(dev, BTN_0, data[2] & 0x01);	/* up */ -		input_report_key(dev, BTN_1, data[2] & 0x02);	/* down */ +		/* dpad as buttons (left, right, up, down) */ +		input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); +		input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); +		input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); +		input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);  	} else {  		input_report_abs(dev, ABS_HAT0X,  				 !!(data[2] & 0x08) - !!(data[2] & 0x04)); @@ -363,7 +375,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad,  	/* start/back buttons */  	input_report_key(dev, BTN_START,  data[2] & 0x10); -	input_report_key(dev, BTN_BACK,   data[2] & 0x20); +	input_report_key(dev, BTN_SELECT, data[2] & 0x20);  	/* stick press left/right */  	input_report_key(dev, BTN_THUMBL, data[2] & 0x40); @@ -378,17 +390,19 @@ static void xpad360_process_packet(struct usb_xpad *xpad,  	input_report_key(dev, BTN_TR,	data[3] & 0x02);  	input_report_key(dev, BTN_MODE,	data[3] & 0x04); -	/* left stick */ -	input_report_abs(dev, ABS_X, -			 (__s16) le16_to_cpup((__le16 *)(data + 6))); -	input_report_abs(dev, ABS_Y, -			 ~(__s16) le16_to_cpup((__le16 *)(data + 8))); +	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { +		/* left stick */ +		input_report_abs(dev, ABS_X, +				 (__s16) le16_to_cpup((__le16 *)(data + 6))); +		input_report_abs(dev, ABS_Y, +				 ~(__s16) le16_to_cpup((__le16 *)(data + 8))); -	/* right stick */ -	input_report_abs(dev, ABS_RX, -			 (__s16) le16_to_cpup((__le16 *)(data + 10))); -	input_report_abs(dev, ABS_RY, -			 ~(__s16) le16_to_cpup((__le16 *)(data + 12))); +		/* right stick */ +		input_report_abs(dev, ABS_RX, +				 (__s16) le16_to_cpup((__le16 *)(data + 10))); +		input_report_abs(dev, ABS_RY, +				 ~(__s16) le16_to_cpup((__le16 *)(data + 12))); +	}  	/* triggers left/right */  	if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { @@ -814,6 +828,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id  			xpad->mapping |= MAP_DPAD_TO_BUTTONS;  		if (triggers_to_buttons)  			xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS; +		if (sticks_to_null) +			xpad->mapping |= MAP_STICKS_TO_NULL;  	}  	xpad->dev = input_dev; @@ -830,16 +846,20 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id  	input_dev->open = xpad_open;  	input_dev->close = xpad_close; -	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	input_dev->evbit[0] = BIT_MASK(EV_KEY); -	/* set up standard buttons and axes */ +	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { +		input_dev->evbit[0] |= BIT_MASK(EV_ABS); +		/* set up axes */ +		for (i = 0; xpad_abs[i] >= 0; i++) +			xpad_set_up_abs(input_dev, xpad_abs[i]); +	} + +	/* set up standard buttons */  	for (i = 0; xpad_common_btn[i] >= 0; i++)  		__set_bit(xpad_common_btn[i], input_dev->keybit); -	for (i = 0; xpad_abs[i] >= 0; i++) -		xpad_set_up_abs(input_dev, xpad_abs[i]); - -	/* Now set up model-specific ones */ +	/* set up model-specific ones */  	if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) {  		for (i = 0; xpad360_btn[i] >= 0; i++)  			__set_bit(xpad360_btn[i], input_dev->keybit); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 1ba25145b33..b171f63fe4d 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -297,6 +297,18 @@ config KEYBOARD_MAX7359  	  To compile this driver as a module, choose M here: the  	  module will be called max7359_keypad. +config KEYBOARD_MCS +	tristate "MELFAS MCS Touchkey" +	depends on I2C +	help +	  Say Y here if you have the MELFAS MCS5000/5080 touchkey controller +	  chip in your system. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called mcs_touchkey. +  config KEYBOARD_IMX  	tristate "IMX keypad support"  	depends on ARCH_MXC @@ -342,6 +354,15 @@ config KEYBOARD_PXA930_ROTARY  	  To compile this driver as a module, choose M here: the  	  module will be called pxa930_rotary. +config KEYBOARD_SAMSUNG +	tristate "Samsung keypad support" +	depends on SAMSUNG_DEV_KEYPAD +	help +	  Say Y here if you want to use the Samsung keypad. + +	  To compile this driver as a module, choose M here: the +	  module will be called samsung-keypad. +  config KEYBOARD_STOWAWAY  	tristate "Stowaway keyboard"  	select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 4596d0c6f92..1a66d5f1ca8 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -26,12 +26,14 @@ obj-$(CONFIG_KEYBOARD_LOCOMO)		+= locomokbd.o  obj-$(CONFIG_KEYBOARD_MAPLE)		+= maple_keyb.o  obj-$(CONFIG_KEYBOARD_MATRIX)		+= matrix_keypad.o  obj-$(CONFIG_KEYBOARD_MAX7359)		+= max7359_keypad.o +obj-$(CONFIG_KEYBOARD_MCS)		+= mcs_touchkey.o  obj-$(CONFIG_KEYBOARD_NEWTON)		+= newtonkbd.o  obj-$(CONFIG_KEYBOARD_OMAP)		+= omap-keypad.o  obj-$(CONFIG_KEYBOARD_OPENCORES)	+= opencores-kbd.o  obj-$(CONFIG_KEYBOARD_PXA27x)		+= pxa27x_keypad.o  obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)	+= pxa930_rotary.o  obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o +obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o  obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o  obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o  obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 744600eff22..d6918cb966c 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -19,6 +19,7 @@  #include <linux/platform_device.h>  #include <linux/input.h>  #include <linux/i2c.h> +#include <linux/gpio.h>  #include <linux/slab.h>  #include <linux/i2c/adp5588.h> @@ -54,6 +55,10 @@  #define KEYP_MAX_EVENT		10 +#define MAXGPIO			18 +#define ADP_BANK(offs)		((offs) >> 3) +#define ADP_BIT(offs)		(1u << ((offs) & 0x7)) +  /*   * Early pre 4.0 Silicon required to delay readout by at least 25ms,   * since the Event Counter Register updated 25ms after the interrupt @@ -67,6 +72,16 @@ struct adp5588_kpad {  	struct delayed_work work;  	unsigned long delay;  	unsigned short keycode[ADP5588_KEYMAPSIZE]; +	const struct adp5588_gpi_map *gpimap; +	unsigned short gpimapsize; +#ifdef CONFIG_GPIOLIB +	unsigned char gpiomap[MAXGPIO]; +	bool export_gpio; +	struct gpio_chip gc; +	struct mutex gpio_lock;	/* Protect cached dir, dat_out */ +	u8 dat_out[3]; +	u8 dir[3]; +#endif  };  static int adp5588_read(struct i2c_client *client, u8 reg) @@ -84,12 +99,222 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)  	return i2c_smbus_write_byte_data(client, reg, val);  } +#ifdef CONFIG_GPIOLIB +static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ +	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); +	unsigned int bank = ADP_BANK(kpad->gpiomap[off]); +	unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + +	return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); +} + +static void adp5588_gpio_set_value(struct gpio_chip *chip, +				   unsigned off, int val) +{ +	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); +	unsigned int bank = ADP_BANK(kpad->gpiomap[off]); +	unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + +	mutex_lock(&kpad->gpio_lock); + +	if (val) +		kpad->dat_out[bank] |= bit; +	else +		kpad->dat_out[bank] &= ~bit; + +	adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, +			   kpad->dat_out[bank]); + +	mutex_unlock(&kpad->gpio_lock); +} + +static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ +	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); +	unsigned int bank = ADP_BANK(kpad->gpiomap[off]); +	unsigned int bit = ADP_BIT(kpad->gpiomap[off]); +	int ret; + +	mutex_lock(&kpad->gpio_lock); + +	kpad->dir[bank] &= ~bit; +	ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]); + +	mutex_unlock(&kpad->gpio_lock); + +	return ret; +} + +static int adp5588_gpio_direction_output(struct gpio_chip *chip, +					 unsigned off, int val) +{ +	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); +	unsigned int bank = ADP_BANK(kpad->gpiomap[off]); +	unsigned int bit = ADP_BIT(kpad->gpiomap[off]); +	int ret; + +	mutex_lock(&kpad->gpio_lock); + +	kpad->dir[bank] |= bit; + +	if (val) +		kpad->dat_out[bank] |= bit; +	else +		kpad->dat_out[bank] &= ~bit; + +	ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, +				 kpad->dat_out[bank]); +	ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank, +				 kpad->dir[bank]); + +	mutex_unlock(&kpad->gpio_lock); + +	return ret; +} + +static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, +				const struct adp5588_kpad_platform_data *pdata) +{ +	bool pin_used[MAXGPIO]; +	int n_unused = 0; +	int i; + +	memset(pin_used, 0, sizeof(pin_used)); + +	for (i = 0; i < pdata->rows; i++) +		pin_used[i] = true; + +	for (i = 0; i < pdata->cols; i++) +		pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true; + +	for (i = 0; i < kpad->gpimapsize; i++) +		pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true; + +	for (i = 0; i < MAXGPIO; i++) +		if (!pin_used[i]) +			kpad->gpiomap[n_unused++] = i; + +	return n_unused; +} + +static int __devinit adp5588_gpio_add(struct adp5588_kpad *kpad) +{ +	struct device *dev = &kpad->client->dev; +	const struct adp5588_kpad_platform_data *pdata = dev->platform_data; +	const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; +	int i, error; + +	if (!gpio_data) +		return 0; + +	kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata); +	if (kpad->gc.ngpio == 0) { +		dev_info(dev, "No unused gpios left to export\n"); +		return 0; +	} + +	kpad->export_gpio = true; + +	kpad->gc.direction_input = adp5588_gpio_direction_input; +	kpad->gc.direction_output = adp5588_gpio_direction_output; +	kpad->gc.get = adp5588_gpio_get_value; +	kpad->gc.set = adp5588_gpio_set_value; +	kpad->gc.can_sleep = 1; + +	kpad->gc.base = gpio_data->gpio_start; +	kpad->gc.label = kpad->client->name; +	kpad->gc.owner = THIS_MODULE; + +	mutex_init(&kpad->gpio_lock); + +	error = gpiochip_add(&kpad->gc); +	if (error) { +		dev_err(dev, "gpiochip_add failed, err: %d\n", error); +		return error; +	} + +	for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { +		kpad->dat_out[i] = adp5588_read(kpad->client, +						GPIO_DAT_OUT1 + i); +		kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); +	} + +	if (gpio_data->setup) { +		error = gpio_data->setup(kpad->client, +					 kpad->gc.base, kpad->gc.ngpio, +					 gpio_data->context); +		if (error) +			dev_warn(dev, "setup failed, %d\n", error); +	} + +	return 0; +} + +static void __devexit adp5588_gpio_remove(struct adp5588_kpad *kpad) +{ +	struct device *dev = &kpad->client->dev; +	const struct adp5588_kpad_platform_data *pdata = dev->platform_data; +	const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; +	int error; + +	if (!kpad->export_gpio) +		return; + +	if (gpio_data->teardown) { +		error = gpio_data->teardown(kpad->client, +					    kpad->gc.base, kpad->gc.ngpio, +					    gpio_data->context); +		if (error) +			dev_warn(dev, "teardown failed %d\n", error); +	} + +	error = gpiochip_remove(&kpad->gc); +	if (error) +		dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) +{ +	return 0; +} + +static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad) +{ +} +#endif + +static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) +{ +	int i, j; + +	for (i = 0; i < ev_cnt; i++) { +		int key = adp5588_read(kpad->client, Key_EVENTA + i); +		int key_val = key & KEY_EV_MASK; + +		if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) { +			for (j = 0; j < kpad->gpimapsize; j++) { +				if (key_val == kpad->gpimap[j].pin) { +					input_report_switch(kpad->input, +							kpad->gpimap[j].sw_evt, +							key & KEY_EV_PRESSED); +					break; +				} +			} +		} else { +			input_report_key(kpad->input, +					 kpad->keycode[key_val - 1], +					 key & KEY_EV_PRESSED); +		} +	} +} +  static void adp5588_work(struct work_struct *work)  {  	struct adp5588_kpad *kpad = container_of(work,  						struct adp5588_kpad, work.work);  	struct i2c_client *client = kpad->client; -	int i, key, status, ev_cnt; +	int status, ev_cnt;  	status = adp5588_read(client, INT_STAT); @@ -99,12 +324,7 @@ static void adp5588_work(struct work_struct *work)  	if (status & KE_INT) {  		ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC;  		if (ev_cnt) { -			for (i = 0; i < ev_cnt; i++) { -				key = adp5588_read(client, Key_EVENTA + i); -				input_report_key(kpad->input, -					kpad->keycode[(key & KEY_EV_MASK) - 1], -					key & KEY_EV_PRESSED); -			} +			adp5588_report_events(kpad, ev_cnt);  			input_sync(kpad->input);  		}  	} @@ -128,8 +348,10 @@ static irqreturn_t adp5588_irq(int irq, void *handle)  static int __devinit adp5588_setup(struct i2c_client *client)  { -	struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; +	const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; +	const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;  	int i, ret; +	unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;  	ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));  	ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF); @@ -144,6 +366,32 @@ static int __devinit adp5588_setup(struct i2c_client *client)  	for (i = 0; i < KEYP_MAX_EVENT; i++)  		ret |= adp5588_read(client, Key_EVENTA); +	for (i = 0; i < pdata->gpimapsize; i++) { +		unsigned short pin = pdata->gpimap[i].pin; + +		if (pin <= GPI_PIN_ROW_END) { +			evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE)); +		} else { +			evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF); +			evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8); +		} +	} + +	if (pdata->gpimapsize) { +		ret |= adp5588_write(client, GPI_EM1, evt_mode1); +		ret |= adp5588_write(client, GPI_EM2, evt_mode2); +		ret |= adp5588_write(client, GPI_EM3, evt_mode3); +	} + +	if (gpio_data) { +		for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { +			int pull_mask = gpio_data->pullup_dis_mask; + +			ret |= adp5588_write(client, GPIO_PULL1 + i, +				(pull_mask >> (8 * i)) & 0xFF); +		} +	} +  	ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT |  					OVR_FLOW_INT | K_LCK_INT |  					GPI_INT | KE_INT); /* Status is W1C */ @@ -158,11 +406,49 @@ static int __devinit adp5588_setup(struct i2c_client *client)  	return 0;  } +static void __devinit adp5588_report_switch_state(struct adp5588_kpad *kpad) +{ +	int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1); +	int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2); +	int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3); +	int gpi_stat_tmp, pin_loc; +	int i; + +	for (i = 0; i < kpad->gpimapsize; i++) { +		unsigned short pin = kpad->gpimap[i].pin; + +		if (pin <= GPI_PIN_ROW_END) { +			gpi_stat_tmp = gpi_stat1; +			pin_loc = pin - GPI_PIN_ROW_BASE; +		} else if ((pin - GPI_PIN_COL_BASE) < 8) { +			gpi_stat_tmp = gpi_stat2; +			pin_loc = pin - GPI_PIN_COL_BASE; +		} else { +			gpi_stat_tmp = gpi_stat3; +			pin_loc = pin - GPI_PIN_COL_BASE - 8; +		} + +		if (gpi_stat_tmp < 0) { +			dev_err(&kpad->client->dev, +				"Can't read GPIO_DAT_STAT switch %d default to OFF\n", +				pin); +			gpi_stat_tmp = 0; +		} + +		input_report_switch(kpad->input, +				    kpad->gpimap[i].sw_evt, +				    !(gpi_stat_tmp & (1 << pin_loc))); +	} + +	input_sync(kpad->input); +} + +  static int __devinit adp5588_probe(struct i2c_client *client,  					const struct i2c_device_id *id)  {  	struct adp5588_kpad *kpad; -	struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; +	const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;  	struct input_dev *input;  	unsigned int revid;  	int ret, i; @@ -189,6 +475,37 @@ static int __devinit adp5588_probe(struct i2c_client *client,  		return -EINVAL;  	} +	if (!pdata->gpimap && pdata->gpimapsize) { +		dev_err(&client->dev, "invalid gpimap from pdata\n"); +		return -EINVAL; +	} + +	if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) { +		dev_err(&client->dev, "invalid gpimapsize\n"); +		return -EINVAL; +	} + +	for (i = 0; i < pdata->gpimapsize; i++) { +		unsigned short pin = pdata->gpimap[i].pin; + +		if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) { +			dev_err(&client->dev, "invalid gpi pin data\n"); +			return -EINVAL; +		} + +		if (pin <= GPI_PIN_ROW_END) { +			if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) { +				dev_err(&client->dev, "invalid gpi row data\n"); +				return -EINVAL; +			} +		} else { +			if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) { +				dev_err(&client->dev, "invalid gpi col data\n"); +				return -EINVAL; +			} +		} +	} +  	if (!client->irq) {  		dev_err(&client->dev, "no IRQ?\n");  		return -EINVAL; @@ -233,6 +550,9 @@ static int __devinit adp5588_probe(struct i2c_client *client,  	memcpy(kpad->keycode, pdata->keymap,  		pdata->keymapsize * input->keycodesize); +	kpad->gpimap = pdata->gpimap; +	kpad->gpimapsize = pdata->gpimapsize; +  	/* setup input device */  	__set_bit(EV_KEY, input->evbit); @@ -243,6 +563,11 @@ static int __devinit adp5588_probe(struct i2c_client *client,  		__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);  	__clear_bit(KEY_RESERVED, input->keybit); +	if (kpad->gpimapsize) +		__set_bit(EV_SW, input->evbit); +	for (i = 0; i < kpad->gpimapsize; i++) +		__set_bit(kpad->gpimap[i].sw_evt, input->swbit); +  	error = input_register_device(input);  	if (error) {  		dev_err(&client->dev, "unable to register input device\n"); @@ -261,6 +586,13 @@ static int __devinit adp5588_probe(struct i2c_client *client,  	if (error)  		goto err_free_irq; +	if (kpad->gpimapsize) +		adp5588_report_switch_state(kpad); + +	error = adp5588_gpio_add(kpad); +	if (error) +		goto err_free_irq; +  	device_init_wakeup(&client->dev, 1);  	i2c_set_clientdata(client, kpad); @@ -287,6 +619,7 @@ static int __devexit adp5588_remove(struct i2c_client *client)  	free_irq(client->irq, kpad);  	cancel_delayed_work_sync(&kpad->work);  	input_unregister_device(kpad->input); +	adp5588_gpio_remove(kpad);  	kfree(kpad);  	return 0; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index b8213fd13c3..a9fd147f2ba 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -31,6 +31,7 @@ struct gpio_button_data {  	struct input_dev *input;  	struct timer_list timer;  	struct work_struct work; +	int timer_debounce;	/* in msecs */  	bool disabled;  }; @@ -109,7 +110,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)  		 * Disable IRQ and possible debouncing timer.  		 */  		disable_irq(gpio_to_irq(bdata->button->gpio)); -		if (bdata->button->debounce_interval) +		if (bdata->timer_debounce)  			del_timer_sync(&bdata->timer);  		bdata->disabled = true; @@ -347,9 +348,9 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)  	BUG_ON(irq != gpio_to_irq(button->gpio)); -	if (button->debounce_interval) +	if (bdata->timer_debounce)  		mod_timer(&bdata->timer, -			jiffies + msecs_to_jiffies(button->debounce_interval)); +			jiffies + msecs_to_jiffies(bdata->timer_debounce));  	else  		schedule_work(&bdata->work); @@ -383,6 +384,14 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,  		goto fail3;  	} +	if (button->debounce_interval) { +		error = gpio_set_debounce(button->gpio, +					  button->debounce_interval * 1000); +		/* use timer if gpiolib doesn't provide debounce */ +		if (error < 0) +			bdata->timer_debounce = button->debounce_interval; +	} +  	irq = gpio_to_irq(button->gpio);  	if (irq < 0) {  		error = irq; @@ -498,7 +507,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)   fail2:  	while (--i >= 0) {  		free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); -		if (pdata->buttons[i].debounce_interval) +		if (ddata->data[i].timer_debounce)  			del_timer_sync(&ddata->data[i].timer);  		cancel_work_sync(&ddata->data[i].work);  		gpio_free(pdata->buttons[i].gpio); @@ -526,7 +535,7 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)  	for (i = 0; i < pdata->nbuttons; i++) {  		int irq = gpio_to_irq(pdata->buttons[i].gpio);  		free_irq(irq, &ddata->data[i]); -		if (pdata->buttons[i].debounce_interval) +		if (ddata->data[i].timer_debounce)  			del_timer_sync(&ddata->data[i].timer);  		cancel_work_sync(&ddata->data[i].work);  		gpio_free(pdata->buttons[i].gpio); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 40b032f0e32..f7c2a166576 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -642,6 +642,7 @@ static int __devinit lm8323_probe(struct i2c_client *client,  	struct lm8323_platform_data *pdata = client->dev.platform_data;  	struct input_dev *idev;  	struct lm8323_chip *lm; +	int pwm;  	int i, err;  	unsigned long tmo;  	u8 data[2]; @@ -710,8 +711,9 @@ static int __devinit lm8323_probe(struct i2c_client *client,  		goto fail1;  	} -	for (i = 0; i < LM8323_NUM_PWMS; i++) { -		err = init_pwm(lm, i + 1, &client->dev, pdata->pwm_names[i]); +	for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) { +		err = init_pwm(lm, pwm + 1, &client->dev, +			       pdata->pwm_names[pwm]);  		if (err < 0)  			goto fail2;  	} @@ -764,9 +766,9 @@ fail4:  fail3:  	device_remove_file(&client->dev, &dev_attr_disable_kp);  fail2: -	while (--i >= 0) -		if (lm->pwm[i].enabled) -			led_classdev_unregister(&lm->pwm[i].cdev); +	while (--pwm >= 0) +		if (lm->pwm[pwm].enabled) +			led_classdev_unregister(&lm->pwm[pwm].cdev);  fail1:  	input_free_device(idev);  	kfree(lm); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index b443e088fd3..b02e4268e18 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -37,6 +37,7 @@ struct matrix_keypad {  	spinlock_t lock;  	bool scan_pending;  	bool stopped; +	bool gpio_all_disabled;  };  /* @@ -87,8 +88,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad)  	const struct matrix_keypad_platform_data *pdata = keypad->pdata;  	int i; -	for (i = 0; i < pdata->num_row_gpios; i++) -		enable_irq(gpio_to_irq(pdata->row_gpios[i])); +	if (pdata->clustered_irq > 0) +		enable_irq(pdata->clustered_irq); +	else { +		for (i = 0; i < pdata->num_row_gpios; i++) +			enable_irq(gpio_to_irq(pdata->row_gpios[i])); +	}  }  static void disable_row_irqs(struct matrix_keypad *keypad) @@ -96,8 +101,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad)  	const struct matrix_keypad_platform_data *pdata = keypad->pdata;  	int i; -	for (i = 0; i < pdata->num_row_gpios; i++) -		disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); +	if (pdata->clustered_irq > 0) +		disable_irq_nosync(pdata->clustered_irq); +	else { +		for (i = 0; i < pdata->num_row_gpios; i++) +			disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); +	}  }  /* @@ -216,45 +225,69 @@ static void matrix_keypad_stop(struct input_dev *dev)  }  #ifdef CONFIG_PM -static int matrix_keypad_suspend(struct device *dev) +static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)  { -	struct platform_device *pdev = to_platform_device(dev); -	struct matrix_keypad *keypad = platform_get_drvdata(pdev);  	const struct matrix_keypad_platform_data *pdata = keypad->pdata; +	unsigned int gpio;  	int i; -	matrix_keypad_stop(keypad->input_dev); +	if (pdata->clustered_irq > 0) { +		if (enable_irq_wake(pdata->clustered_irq) == 0) +			keypad->gpio_all_disabled = true; +	} else { -	if (device_may_wakeup(&pdev->dev)) {  		for (i = 0; i < pdata->num_row_gpios; i++) {  			if (!test_bit(i, keypad->disabled_gpios)) { -				unsigned int gpio = pdata->row_gpios[i]; +				gpio = pdata->row_gpios[i];  				if (enable_irq_wake(gpio_to_irq(gpio)) == 0)  					__set_bit(i, keypad->disabled_gpios);  			}  		}  	} - -	return 0;  } -static int matrix_keypad_resume(struct device *dev) +static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)  { -	struct platform_device *pdev = to_platform_device(dev); -	struct matrix_keypad *keypad = platform_get_drvdata(pdev);  	const struct matrix_keypad_platform_data *pdata = keypad->pdata; +	unsigned int gpio;  	int i; -	if (device_may_wakeup(&pdev->dev)) { +	if (pdata->clustered_irq > 0) { +		if (keypad->gpio_all_disabled) { +			disable_irq_wake(pdata->clustered_irq); +			keypad->gpio_all_disabled = false; +		} +	} else {  		for (i = 0; i < pdata->num_row_gpios; i++) {  			if (test_and_clear_bit(i, keypad->disabled_gpios)) { -				unsigned int gpio = pdata->row_gpios[i]; - +				gpio = pdata->row_gpios[i];  				disable_irq_wake(gpio_to_irq(gpio));  			}  		}  	} +} + +static int matrix_keypad_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct matrix_keypad *keypad = platform_get_drvdata(pdev); + +	matrix_keypad_stop(keypad->input_dev); + +	if (device_may_wakeup(&pdev->dev)) +		matrix_keypad_enable_wakeup(keypad); + +	return 0; +} + +static int matrix_keypad_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct matrix_keypad *keypad = platform_get_drvdata(pdev); + +	if (device_may_wakeup(&pdev->dev)) +		matrix_keypad_disable_wakeup(keypad);  	matrix_keypad_start(keypad->input_dev); @@ -296,17 +329,31 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,  		gpio_direction_input(pdata->row_gpios[i]);  	} -	for (i = 0; i < pdata->num_row_gpios; i++) { -		err = request_irq(gpio_to_irq(pdata->row_gpios[i]), +	if (pdata->clustered_irq > 0) { +		err = request_irq(pdata->clustered_irq,  				matrix_keypad_interrupt, -				IRQF_DISABLED | -				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +				pdata->clustered_irq_flags,  				"matrix-keypad", keypad);  		if (err) {  			dev_err(&pdev->dev, -				"Unable to acquire interrupt for GPIO line %i\n", -				pdata->row_gpios[i]); -			goto err_free_irqs; +				"Unable to acquire clustered interrupt\n"); +			goto err_free_rows; +		} +	} else { +		for (i = 0; i < pdata->num_row_gpios; i++) { +			err = request_irq(gpio_to_irq(pdata->row_gpios[i]), +					matrix_keypad_interrupt, +					IRQF_DISABLED | +					IRQF_TRIGGER_RISING | +					IRQF_TRIGGER_FALLING, +					"matrix-keypad", keypad); +			if (err) { +				dev_err(&pdev->dev, +					"Unable to acquire interrupt " +					"for GPIO line %i\n", +					pdata->row_gpios[i]); +				goto err_free_irqs; +			}  		}  	} @@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev)  	device_init_wakeup(&pdev->dev, 0); -	for (i = 0; i < pdata->num_row_gpios; i++) { -		free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); -		gpio_free(pdata->row_gpios[i]); +	if (pdata->clustered_irq > 0) { +		free_irq(pdata->clustered_irq, keypad); +	} else { +		for (i = 0; i < pdata->num_row_gpios; i++) +			free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);  	} +	for (i = 0; i < pdata->num_row_gpios; i++) +		gpio_free(pdata->row_gpios[i]); +  	for (i = 0; i < pdata->num_col_gpios; i++)  		gpio_free(pdata->col_gpios[i]); diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c new file mode 100644 index 00000000000..63b849d7e90 --- /dev/null +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -0,0 +1,239 @@ +/* + * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: HeungJun Kim <riverful.kim@samsung.com> + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c/mcs.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/slab.h> + +/* MCS5000 Touchkey */ +#define MCS5000_TOUCHKEY_STATUS		0x04 +#define MCS5000_TOUCHKEY_STATUS_PRESS	7 +#define MCS5000_TOUCHKEY_FW		0x0a +#define MCS5000_TOUCHKEY_BASE_VAL	0x61 + +/* MCS5080 Touchkey */ +#define MCS5080_TOUCHKEY_STATUS		0x00 +#define MCS5080_TOUCHKEY_STATUS_PRESS	3 +#define MCS5080_TOUCHKEY_FW		0x01 +#define MCS5080_TOUCHKEY_BASE_VAL	0x1 + +enum mcs_touchkey_type { +	MCS5000_TOUCHKEY, +	MCS5080_TOUCHKEY, +}; + +struct mcs_touchkey_chip { +	unsigned int status_reg; +	unsigned int pressbit; +	unsigned int press_invert; +	unsigned int baseval; +}; + +struct mcs_touchkey_data { +	struct i2c_client *client; +	struct input_dev *input_dev; +	struct mcs_touchkey_chip chip; +	unsigned int key_code; +	unsigned int key_val; +	unsigned short keycodes[]; +}; + +static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id) +{ +	struct mcs_touchkey_data *data = dev_id; +	struct mcs_touchkey_chip *chip = &data->chip; +	struct i2c_client *client = data->client; +	struct input_dev *input = data->input_dev; +	unsigned int key_val; +	unsigned int pressed; +	int val; + +	val = i2c_smbus_read_byte_data(client, chip->status_reg); +	if (val < 0) { +		dev_err(&client->dev, "i2c read error [%d]\n", val); +		goto out; +	} + +	pressed = (val & (1 << chip->pressbit)) >> chip->pressbit; +	if (chip->press_invert) +		pressed ^= chip->press_invert; + +	/* key_val is 0 when released, so we should use key_val of press. */ +	if (pressed) { +		key_val = val & (0xff >> (8 - chip->pressbit)); +		if (!key_val) +			goto out; +		key_val -= chip->baseval; +		data->key_code = data->keycodes[key_val]; +		data->key_val = key_val; +	} + +	input_event(input, EV_MSC, MSC_SCAN, data->key_val); +	input_report_key(input, data->key_code, pressed); +	input_sync(input); + +	dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code, +		pressed ? "pressed" : "released"); + + out: +	return IRQ_HANDLED; +} + +static int __devinit mcs_touchkey_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	const struct mcs_platform_data *pdata; +	struct mcs_touchkey_data *data; +	struct input_dev *input_dev; +	unsigned int fw_reg; +	int fw_ver; +	int error; +	int i; + +	pdata = client->dev.platform_data; +	if (!pdata) { +		dev_err(&client->dev, "no platform data defined\n"); +		return -EINVAL; +	} + +	data = kzalloc(sizeof(struct mcs_touchkey_data) + +			sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), +			GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!data || !input_dev) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		error = -ENOMEM; +		goto err_free_mem; +	} + +	data->client = client; +	data->input_dev = input_dev; + +	if (id->driver_data == MCS5000_TOUCHKEY) { +		data->chip.status_reg = MCS5000_TOUCHKEY_STATUS; +		data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS; +		data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL; +		fw_reg = MCS5000_TOUCHKEY_FW; +	} else { +		data->chip.status_reg = MCS5080_TOUCHKEY_STATUS; +		data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS; +		data->chip.press_invert = 1; +		data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL; +		fw_reg = MCS5080_TOUCHKEY_FW; +	} + +	fw_ver = i2c_smbus_read_byte_data(client, fw_reg); +	if (fw_ver < 0) { +		error = fw_ver; +		dev_err(&client->dev, "i2c read error[%d]\n", error); +		goto err_free_mem; +	} +	dev_info(&client->dev, "Firmware version: %d\n", fw_ver); + +	input_dev->name = "MELPAS MCS Touchkey"; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; +	input_dev->evbit[0] = BIT_MASK(EV_KEY); +	if (!pdata->no_autorepeat) +		input_dev->evbit[0] |= BIT_MASK(EV_REP); +	input_dev->keycode = data->keycodes; +	input_dev->keycodesize = sizeof(data->keycodes[0]); +	input_dev->keycodemax = pdata->key_maxval + 1; + +	for (i = 0; i < pdata->keymap_size; i++) { +		unsigned int val = MCS_KEY_VAL(pdata->keymap[i]); +		unsigned int code = MCS_KEY_CODE(pdata->keymap[i]); + +		data->keycodes[val] = code; +		__set_bit(code, input_dev->keybit); +	} + +	input_set_capability(input_dev, EV_MSC, MSC_SCAN); +	input_set_drvdata(input_dev, data); + +	if (pdata->cfg_pin) +		pdata->cfg_pin(); + +	error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, +			IRQF_TRIGGER_FALLING, client->dev.driver->name, data); +	if (error) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		goto err_free_mem; +	} + +	error = input_register_device(input_dev); +	if (error) +		goto err_free_irq; + +	i2c_set_clientdata(client, data); +	return 0; + +err_free_irq: +	free_irq(client->irq, data); +err_free_mem: +	input_free_device(input_dev); +	kfree(data); +	return error; +} + +static int __devexit mcs_touchkey_remove(struct i2c_client *client) +{ +	struct mcs_touchkey_data *data = i2c_get_clientdata(client); + +	free_irq(client->irq, data); +	input_unregister_device(data->input_dev); +	kfree(data); + +	return 0; +} + +static const struct i2c_device_id mcs_touchkey_id[] = { +	{ "mcs5000_touchkey", MCS5000_TOUCHKEY }, +	{ "mcs5080_touchkey", MCS5080_TOUCHKEY }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id); + +static struct i2c_driver mcs_touchkey_driver = { +	.driver = { +		.name	= "mcs_touchkey", +		.owner	= THIS_MODULE, +	}, +	.probe		= mcs_touchkey_probe, +	.remove		= __devexit_p(mcs_touchkey_remove), +	.id_table	= mcs_touchkey_id, +}; + +static int __init mcs_touchkey_init(void) +{ +	return i2c_add_driver(&mcs_touchkey_driver); +} + +static void __exit mcs_touchkey_exit(void) +{ +	i2c_del_driver(&mcs_touchkey_driver); +} + +module_init(mcs_touchkey_init); +module_exit(mcs_touchkey_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); +MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c new file mode 100644 index 00000000000..f689f49e310 --- /dev/null +++ b/drivers/input/keyboard/samsung-keypad.c @@ -0,0 +1,491 @@ +/* + * Samsung keypad driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <plat/keypad.h> + +#define SAMSUNG_KEYIFCON			0x00 +#define SAMSUNG_KEYIFSTSCLR			0x04 +#define SAMSUNG_KEYIFCOL			0x08 +#define SAMSUNG_KEYIFROW			0x0c +#define SAMSUNG_KEYIFFC				0x10 + +/* SAMSUNG_KEYIFCON */ +#define SAMSUNG_KEYIFCON_INT_F_EN		(1 << 0) +#define SAMSUNG_KEYIFCON_INT_R_EN		(1 << 1) +#define SAMSUNG_KEYIFCON_DF_EN			(1 << 2) +#define SAMSUNG_KEYIFCON_FC_EN			(1 << 3) +#define SAMSUNG_KEYIFCON_WAKEUPEN		(1 << 4) + +/* SAMSUNG_KEYIFSTSCLR */ +#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK		(0xff << 0) +#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK		(0xff << 8) +#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET	8 +#define S5PV210_KEYIFSTSCLR_P_INT_MASK		(0x3fff << 0) +#define S5PV210_KEYIFSTSCLR_R_INT_MASK		(0x3fff << 16) +#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET	16 + +/* SAMSUNG_KEYIFCOL */ +#define SAMSUNG_KEYIFCOL_MASK			(0xff << 0) +#define S5PV210_KEYIFCOLEN_MASK			(0xff << 8) + +/* SAMSUNG_KEYIFROW */ +#define SAMSUNG_KEYIFROW_MASK			(0xff << 0) +#define S5PV210_KEYIFROW_MASK			(0x3fff << 0) + +/* SAMSUNG_KEYIFFC */ +#define SAMSUNG_KEYIFFC_MASK			(0x3ff << 0) + +enum samsung_keypad_type { +	KEYPAD_TYPE_SAMSUNG, +	KEYPAD_TYPE_S5PV210, +}; + +struct samsung_keypad { +	struct input_dev *input_dev; +	struct clk *clk; +	void __iomem *base; +	wait_queue_head_t wait; +	bool stopped; +	int irq; +	unsigned int row_shift; +	unsigned int rows; +	unsigned int cols; +	unsigned int row_state[SAMSUNG_MAX_COLS]; +	unsigned short keycodes[]; +}; + +static int samsung_keypad_is_s5pv210(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	enum samsung_keypad_type type = +		platform_get_device_id(pdev)->driver_data; + +	return type == KEYPAD_TYPE_S5PV210; +} + +static void samsung_keypad_scan(struct samsung_keypad *keypad, +				unsigned int *row_state) +{ +	struct device *dev = keypad->input_dev->dev.parent; +	unsigned int col; +	unsigned int val; + +	for (col = 0; col < keypad->cols; col++) { +		if (samsung_keypad_is_s5pv210(dev)) { +			val = S5PV210_KEYIFCOLEN_MASK; +			val &= ~(1 << col) << 8; +		} else { +			val = SAMSUNG_KEYIFCOL_MASK; +			val &= ~(1 << col); +		} + +		writel(val, keypad->base + SAMSUNG_KEYIFCOL); +		mdelay(1); + +		val = readl(keypad->base + SAMSUNG_KEYIFROW); +		row_state[col] = ~val & ((1 << keypad->rows) - 1); +	} + +	/* KEYIFCOL reg clear */ +	writel(0, keypad->base + SAMSUNG_KEYIFCOL); +} + +static bool samsung_keypad_report(struct samsung_keypad *keypad, +				  unsigned int *row_state) +{ +	struct input_dev *input_dev = keypad->input_dev; +	unsigned int changed; +	unsigned int pressed; +	unsigned int key_down = 0; +	unsigned int val; +	unsigned int col, row; + +	for (col = 0; col < keypad->cols; col++) { +		changed = row_state[col] ^ keypad->row_state[col]; +		key_down |= row_state[col]; +		if (!changed) +			continue; + +		for (row = 0; row < keypad->rows; row++) { +			if (!(changed & (1 << row))) +				continue; + +			pressed = row_state[col] & (1 << row); + +			dev_dbg(&keypad->input_dev->dev, +				"key %s, row: %d, col: %d\n", +				pressed ? "pressed" : "released", row, col); + +			val = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + +			input_event(input_dev, EV_MSC, MSC_SCAN, val); +			input_report_key(input_dev, +					keypad->keycodes[val], pressed); +		} +		input_sync(keypad->input_dev); +	} + +	memcpy(keypad->row_state, row_state, sizeof(keypad->row_state)); + +	return key_down; +} + +static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) +{ +	struct samsung_keypad *keypad = dev_id; +	unsigned int row_state[SAMSUNG_MAX_COLS]; +	unsigned int val; +	bool key_down; + +	do { +		val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); +		/* Clear interrupt. */ +		writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + +		samsung_keypad_scan(keypad, row_state); + +		key_down = samsung_keypad_report(keypad, row_state); +		if (key_down) +			wait_event_timeout(keypad->wait, keypad->stopped, +					   msecs_to_jiffies(50)); + +	} while (key_down && !keypad->stopped); + +	return IRQ_HANDLED; +} + +static void samsung_keypad_start(struct samsung_keypad *keypad) +{ +	unsigned int val; + +	/* Tell IRQ thread that it may poll the device. */ +	keypad->stopped = false; + +	clk_enable(keypad->clk); + +	/* Enable interrupt bits. */ +	val = readl(keypad->base + SAMSUNG_KEYIFCON); +	val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN; +	writel(val, keypad->base + SAMSUNG_KEYIFCON); + +	/* KEYIFCOL reg clear. */ +	writel(0, keypad->base + SAMSUNG_KEYIFCOL); +} + +static void samsung_keypad_stop(struct samsung_keypad *keypad) +{ +	unsigned int val; + +	/* Signal IRQ thread to stop polling and disable the handler. */ +	keypad->stopped = true; +	wake_up(&keypad->wait); +	disable_irq(keypad->irq); + +	/* Clear interrupt. */ +	writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + +	/* Disable interrupt bits. */ +	val = readl(keypad->base + SAMSUNG_KEYIFCON); +	val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN); +	writel(val, keypad->base + SAMSUNG_KEYIFCON); + +	clk_disable(keypad->clk); + +	/* +	 * Now that chip should not generate interrupts we can safely +	 * re-enable the handler. +	 */ +	enable_irq(keypad->irq); +} + +static int samsung_keypad_open(struct input_dev *input_dev) +{ +	struct samsung_keypad *keypad = input_get_drvdata(input_dev); + +	samsung_keypad_start(keypad); + +	return 0; +} + +static void samsung_keypad_close(struct input_dev *input_dev) +{ +	struct samsung_keypad *keypad = input_get_drvdata(input_dev); + +	samsung_keypad_stop(keypad); +} + +static int __devinit samsung_keypad_probe(struct platform_device *pdev) +{ +	const struct samsung_keypad_platdata *pdata; +	const struct matrix_keymap_data *keymap_data; +	struct samsung_keypad *keypad; +	struct resource *res; +	struct input_dev *input_dev; +	unsigned int row_shift; +	unsigned int keymap_size; +	int error; + +	pdata = pdev->dev.platform_data; +	if (!pdata) { +		dev_err(&pdev->dev, "no platform data defined\n"); +		return -EINVAL; +	} + +	keymap_data = pdata->keymap_data; +	if (!keymap_data) { +		dev_err(&pdev->dev, "no keymap data defined\n"); +		return -EINVAL; +	} + +	if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS) +		return -EINVAL; + +	if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS) +		return -EINVAL; + +	/* initialize the gpio */ +	if (pdata->cfg_gpio) +		pdata->cfg_gpio(pdata->rows, pdata->cols); + +	row_shift = get_count_order(pdata->cols); +	keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); + +	keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!keypad || !input_dev) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		error = -ENODEV; +		goto err_free_mem; +	} + +	keypad->base = ioremap(res->start, resource_size(res)); +	if (!keypad->base) { +		error = -EBUSY; +		goto err_free_mem; +	} + +	keypad->clk = clk_get(&pdev->dev, "keypad"); +	if (IS_ERR(keypad->clk)) { +		dev_err(&pdev->dev, "failed to get keypad clk\n"); +		error = PTR_ERR(keypad->clk); +		goto err_unmap_base; +	} + +	keypad->input_dev = input_dev; +	keypad->row_shift = row_shift; +	keypad->rows = pdata->rows; +	keypad->cols = pdata->cols; +	init_waitqueue_head(&keypad->wait); + +	input_dev->name = pdev->name; +	input_dev->id.bustype = BUS_HOST; +	input_dev->dev.parent = &pdev->dev; +	input_set_drvdata(input_dev, keypad); + +	input_dev->open = samsung_keypad_open; +	input_dev->close = samsung_keypad_close; + +	input_dev->evbit[0] = BIT_MASK(EV_KEY); +	if (!pdata->no_autorepeat) +		input_dev->evbit[0] |= BIT_MASK(EV_REP); + +	input_set_capability(input_dev, EV_MSC, MSC_SCAN); + +	input_dev->keycode = keypad->keycodes; +	input_dev->keycodesize = sizeof(keypad->keycodes[0]); +	input_dev->keycodemax = pdata->rows << row_shift; + +	matrix_keypad_build_keymap(keymap_data, row_shift, +			input_dev->keycode, input_dev->keybit); + +	keypad->irq = platform_get_irq(pdev, 0); +	if (keypad->irq < 0) { +		error = keypad->irq; +		goto err_put_clk; +	} + +	error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq, +			IRQF_ONESHOT, dev_name(&pdev->dev), keypad); +	if (error) { +		dev_err(&pdev->dev, "failed to register keypad interrupt\n"); +		goto err_put_clk; +	} + +	error = input_register_device(keypad->input_dev); +	if (error) +		goto err_free_irq; + +	device_init_wakeup(&pdev->dev, pdata->wakeup); +	platform_set_drvdata(pdev, keypad); +	return 0; + +err_free_irq: +	free_irq(keypad->irq, keypad); +err_put_clk: +	clk_put(keypad->clk); +err_unmap_base: +	iounmap(keypad->base); +err_free_mem: +	input_free_device(input_dev); +	kfree(keypad); + +	return error; +} + +static int __devexit samsung_keypad_remove(struct platform_device *pdev) +{ +	struct samsung_keypad *keypad = platform_get_drvdata(pdev); + +	device_init_wakeup(&pdev->dev, 0); +	platform_set_drvdata(pdev, NULL); + +	input_unregister_device(keypad->input_dev); + +	/* +	 * It is safe to free IRQ after unregistering device because +	 * samsung_keypad_close will shut off interrupts. +	 */ +	free_irq(keypad->irq, keypad); + +	clk_put(keypad->clk); + +	iounmap(keypad->base); +	kfree(keypad); + +	return 0; +} + +#ifdef CONFIG_PM +static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, +					 bool enable) +{ +	struct device *dev = keypad->input_dev->dev.parent; +	unsigned int val; + +	clk_enable(keypad->clk); + +	val = readl(keypad->base + SAMSUNG_KEYIFCON); +	if (enable) { +		val |= SAMSUNG_KEYIFCON_WAKEUPEN; +		if (device_may_wakeup(dev)) +			enable_irq_wake(keypad->irq); +	} else { +		val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; +		if (device_may_wakeup(dev)) +			disable_irq_wake(keypad->irq); +	} +	writel(val, keypad->base + SAMSUNG_KEYIFCON); + +	clk_disable(keypad->clk); +} + +static int samsung_keypad_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct samsung_keypad *keypad = platform_get_drvdata(pdev); +	struct input_dev *input_dev = keypad->input_dev; + +	mutex_lock(&input_dev->mutex); + +	if (input_dev->users) +		samsung_keypad_stop(keypad); + +	samsung_keypad_toggle_wakeup(keypad, true); + +	mutex_unlock(&input_dev->mutex); + +	return 0; +} + +static int samsung_keypad_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct samsung_keypad *keypad = platform_get_drvdata(pdev); +	struct input_dev *input_dev = keypad->input_dev; + +	mutex_lock(&input_dev->mutex); + +	samsung_keypad_toggle_wakeup(keypad, false); + +	if (input_dev->users) +		samsung_keypad_start(keypad); + +	mutex_unlock(&input_dev->mutex); + +	return 0; +} + +static const struct dev_pm_ops samsung_keypad_pm_ops = { +	.suspend	= samsung_keypad_suspend, +	.resume		= samsung_keypad_resume, +}; +#endif + +static struct platform_device_id samsung_keypad_driver_ids[] = { +	{ +		.name		= "samsung-keypad", +		.driver_data	= KEYPAD_TYPE_SAMSUNG, +	}, { +		.name		= "s5pv210-keypad", +		.driver_data	= KEYPAD_TYPE_S5PV210, +	}, +	{ }, +}; +MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); + +static struct platform_driver samsung_keypad_driver = { +	.probe		= samsung_keypad_probe, +	.remove		= __devexit_p(samsung_keypad_remove), +	.driver		= { +		.name	= "samsung-keypad", +		.owner	= THIS_MODULE, +#ifdef CONFIG_PM +		.pm	= &samsung_keypad_pm_ops, +#endif +	}, +	.id_table	= samsung_keypad_driver_ids, +}; + +static int __init samsung_keypad_init(void) +{ +	return platform_driver_register(&samsung_keypad_driver); +} +module_init(samsung_keypad_init); + +static void __exit samsung_keypad_exit(void) +{ +	platform_driver_unregister(&samsung_keypad_driver); +} +module_exit(samsung_keypad_exit); + +MODULE_DESCRIPTION("Samsung keypad driver"); +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-keypad"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c44b9eafc55..b49e2337972 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -327,6 +327,17 @@ config INPUT_PCF8574  	  To compile this driver as a module, choose M here: the  	  module will be called pcf8574_keypad. +config INPUT_PWM_BEEPER +	tristate "PWM beeper support" +	depends on HAVE_PWM +	help +	  Say Y here to get support for PWM based beeper devices. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the module will be +	  called pwm-beeper. +  config INPUT_GPIO_ROTARY_ENCODER  	tristate "Rotary encoders connected to GPIO pins"  	depends on GPIOLIB && GENERIC_GPIO @@ -390,4 +401,41 @@ config INPUT_PCAP  	  To compile this driver as a module, choose M here: the  	  module will be called pcap_keys. +config INPUT_ADXL34X +	tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer" +	default n +	help +	  Say Y here if you have a Accelerometer interface using the +	  ADXL345/6 controller, and your board-specific initialization +	  code includes that in its table of devices. + +	  This driver can use either I2C or SPI communication to the +	  ADXL345/6 controller.  Select the appropriate method for +	  your system. + +	  If unsure, say N (but it's safe to say "Y"). + +	  To compile this driver as a module, choose M here: the +	  module will be called adxl34x. + +config INPUT_ADXL34X_I2C +	tristate "support I2C bus connection" +	depends on INPUT_ADXL34X && I2C +	default y +	help +	  Say Y here if you have ADXL345/6 hooked to an I2C bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called adxl34x-i2c. + +config INPUT_ADXL34X_SPI +	tristate "support SPI bus connection" +	depends on INPUT_ADXL34X && SPI +	default y +	help +	  Say Y here if you have ADXL345/6 hooked to a SPI bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called adxl34x-spi. +  endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 71fe57d8023..19ccca78fa7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -8,6 +8,9 @@ obj-$(CONFIG_INPUT_88PM860X_ONKEY)	+= 88pm860x_onkey.o  obj-$(CONFIG_INPUT_AD714X)		+= ad714x.o  obj-$(CONFIG_INPUT_AD714X_I2C)		+= ad714x-i2c.o  obj-$(CONFIG_INPUT_AD714X_SPI)		+= ad714x-spi.o +obj-$(CONFIG_INPUT_ADXL34X)		+= adxl34x.o +obj-$(CONFIG_INPUT_ADXL34X_I2C)		+= adxl34x-i2c.o +obj-$(CONFIG_INPUT_ADXL34X_SPI)		+= adxl34x-spi.o  obj-$(CONFIG_INPUT_APANEL)		+= apanel.o  obj-$(CONFIG_INPUT_ATI_REMOTE)		+= ati_remote.o  obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o @@ -26,6 +29,7 @@ obj-$(CONFIG_INPUT_PCF50633_PMU)	+= pcf50633-input.o  obj-$(CONFIG_INPUT_PCF8574)		+= pcf8574_keypad.o  obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o  obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o +obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o  obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o  obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o  obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index 0fe27baf5e7..c431d09e401 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -1118,7 +1118,7 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,  	if (error)  		goto err_free_mem; -	/* initilize and request sw/hw resources */ +	/* initialize and request sw/hw resources */  	ad714x_hw_init(ad714x);  	mutex_init(&ad714x->mutex); diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c new file mode 100644 index 00000000000..0779724af7e --- /dev/null +++ b/drivers/input/misc/adxl34x-i2c.c @@ -0,0 +1,163 @@ +/* + * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h>	/* BUS_I2C */ +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> +#include "adxl34x.h" + +static int adxl34x_smbus_read(struct device *dev, unsigned char reg) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return i2c_smbus_read_byte_data(client, reg); +} + +static int adxl34x_smbus_write(struct device *dev, +			       unsigned char reg, unsigned char val) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return i2c_smbus_write_byte_data(client, reg, val); +} + +static int adxl34x_smbus_read_block(struct device *dev, +				    unsigned char reg, int count, +				    void *buf) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return i2c_smbus_read_i2c_block_data(client, reg, count, buf); +} + +static int adxl34x_i2c_read_block(struct device *dev, +				  unsigned char reg, int count, +				  void *buf) +{ +	struct i2c_client *client = to_i2c_client(dev); +	int ret; + +	ret = i2c_master_send(client, ®, 1); +	if (ret < 0) +		return ret; + +	ret = i2c_master_recv(client, buf, count); +	if (ret < 0) +		return ret; + +	if (ret != count) +		return -EIO; + +	return 0; +} + +static const struct adxl34x_bus_ops adxl34x_smbus_bops = { +	.bustype	= BUS_I2C, +	.write		= adxl34x_smbus_write, +	.read		= adxl34x_smbus_read, +	.read_block	= adxl34x_smbus_read_block, +}; + +static const struct adxl34x_bus_ops adxl34x_i2c_bops = { +	.bustype	= BUS_I2C, +	.write		= adxl34x_smbus_write, +	.read		= adxl34x_smbus_read, +	.read_block	= adxl34x_i2c_read_block, +}; + +static int __devinit adxl34x_i2c_probe(struct i2c_client *client, +				       const struct i2c_device_id *id) +{ +	struct adxl34x *ac; +	int error; + +	error = i2c_check_functionality(client->adapter, +			I2C_FUNC_SMBUS_BYTE_DATA); +	if (!error) { +		dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); +		return -EIO; +	} + +	ac = adxl34x_probe(&client->dev, client->irq, false, +			   i2c_check_functionality(client->adapter, +						   I2C_FUNC_SMBUS_READ_I2C_BLOCK) ? +				&adxl34x_smbus_bops : &adxl34x_i2c_bops); +	if (IS_ERR(ac)) +		return PTR_ERR(ac); + +	i2c_set_clientdata(client, ac); + +	return 0; +} + +static int __devexit adxl34x_i2c_remove(struct i2c_client *client) +{ +	struct adxl34x *ac = i2c_get_clientdata(client); + +	return adxl34x_remove(ac); +} + +#ifdef CONFIG_PM +static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ +	struct adxl34x *ac = i2c_get_clientdata(client); + +	adxl34x_suspend(ac); + +	return 0; +} + +static int adxl34x_i2c_resume(struct i2c_client *client) +{ +	struct adxl34x *ac = i2c_get_clientdata(client); + +	adxl34x_resume(ac); + +	return 0; +} +#else +# define adxl34x_i2c_suspend NULL +# define adxl34x_i2c_resume  NULL +#endif + +static const struct i2c_device_id adxl34x_id[] = { +	{ "adxl34x", 0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, adxl34x_id); + +static struct i2c_driver adxl34x_driver = { +	.driver = { +		.name = "adxl34x", +		.owner = THIS_MODULE, +	}, +	.probe    = adxl34x_i2c_probe, +	.remove   = __devexit_p(adxl34x_i2c_remove), +	.suspend  = adxl34x_i2c_suspend, +	.resume   = adxl34x_i2c_resume, +	.id_table = adxl34x_id, +}; + +static int __init adxl34x_i2c_init(void) +{ +	return i2c_add_driver(&adxl34x_driver); +} +module_init(adxl34x_i2c_init); + +static void __exit adxl34x_i2c_exit(void) +{ +	i2c_del_driver(&adxl34x_driver); +} +module_exit(adxl34x_i2c_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c new file mode 100644 index 00000000000..782de9e8982 --- /dev/null +++ b/drivers/input/misc/adxl34x-spi.c @@ -0,0 +1,145 @@ +/* + * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h>	/* BUS_SPI */ +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include "adxl34x.h" + +#define MAX_SPI_FREQ_HZ		5000000 +#define MAX_FREQ_NO_FIFODELAY	1500000 +#define ADXL34X_CMD_MULTB	(1 << 6) +#define ADXL34X_CMD_READ	(1 << 7) +#define ADXL34X_WRITECMD(reg)	(reg & 0x3F) +#define ADXL34X_READCMD(reg)	(ADXL34X_CMD_READ | (reg & 0x3F)) +#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \ +					| (reg & 0x3F)) + +static int adxl34x_spi_read(struct device *dev, unsigned char reg) +{ +	struct spi_device *spi = to_spi_device(dev); +	unsigned char cmd; + +	cmd = ADXL34X_READCMD(reg); + +	return spi_w8r8(spi, cmd); +} + +static int adxl34x_spi_write(struct device *dev, +			     unsigned char reg, unsigned char val) +{ +	struct spi_device *spi = to_spi_device(dev); +	unsigned char buf[2]; + +	buf[0] = ADXL34X_WRITECMD(reg); +	buf[1] = val; + +	return spi_write(spi, buf, sizeof(buf)); +} + +static int adxl34x_spi_read_block(struct device *dev, +				  unsigned char reg, int count, +				  void *buf) +{ +	struct spi_device *spi = to_spi_device(dev); +	ssize_t status; + +	reg = ADXL34X_READMB_CMD(reg); +	status = spi_write_then_read(spi, ®, 1, buf, count); + +	return (status < 0) ? status : 0; +} + +static const struct adxl34x_bus_ops adx134x_spi_bops = { +	.bustype	= BUS_SPI, +	.write		= adxl34x_spi_write, +	.read		= adxl34x_spi_read, +	.read_block	= adxl34x_spi_read_block, +}; + +static int __devinit adxl34x_spi_probe(struct spi_device *spi) +{ +	struct adxl34x *ac; + +	/* don't exceed max specified SPI CLK frequency */ +	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { +		dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz); +		return -EINVAL; +	} + +	ac = adxl34x_probe(&spi->dev, spi->irq, +			   spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY, +			   &adx134x_spi_bops); + +	if (IS_ERR(ac)) +		return PTR_ERR(ac); + +	spi_set_drvdata(spi, ac); + +	return 0; +} + +static int __devexit adxl34x_spi_remove(struct spi_device *spi) +{ +	struct adxl34x *ac = dev_get_drvdata(&spi->dev); + +	return adxl34x_remove(ac); +} + +#ifdef CONFIG_PM +static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message) +{ +	struct adxl34x *ac = dev_get_drvdata(&spi->dev); + +	adxl34x_suspend(ac); + +	return 0; +} + +static int adxl34x_spi_resume(struct spi_device *spi) +{ +	struct adxl34x *ac = dev_get_drvdata(&spi->dev); + +	adxl34x_resume(ac); + +	return 0; +} +#else +# define adxl34x_spi_suspend NULL +# define adxl34x_spi_resume  NULL +#endif + +static struct spi_driver adxl34x_driver = { +	.driver = { +		.name = "adxl34x", +		.bus = &spi_bus_type, +		.owner = THIS_MODULE, +	}, +	.probe   = adxl34x_spi_probe, +	.remove  = __devexit_p(adxl34x_spi_remove), +	.suspend = adxl34x_spi_suspend, +	.resume  = adxl34x_spi_resume, +}; + +static int __init adxl34x_spi_init(void) +{ +	return spi_register_driver(&adxl34x_driver); +} +module_init(adxl34x_spi_init); + +static void __exit adxl34x_spi_exit(void) +{ +	spi_unregister_driver(&adxl34x_driver); +} +module_exit(adxl34x_spi_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c new file mode 100644 index 00000000000..e2ca0170808 --- /dev/null +++ b/drivers/input/misc/adxl34x.c @@ -0,0 +1,915 @@ +/* + * ADXL345/346 Three-Axis Digital Accelerometers + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/input/adxl34x.h> + +#include "adxl34x.h" + +/* ADXL345/6 Register Map */ +#define DEVID		0x00	/* R   Device ID */ +#define THRESH_TAP	0x1D	/* R/W Tap threshold */ +#define OFSX		0x1E	/* R/W X-axis offset */ +#define OFSY		0x1F	/* R/W Y-axis offset */ +#define OFSZ		0x20	/* R/W Z-axis offset */ +#define DUR		0x21	/* R/W Tap duration */ +#define LATENT		0x22	/* R/W Tap latency */ +#define WINDOW		0x23	/* R/W Tap window */ +#define THRESH_ACT	0x24	/* R/W Activity threshold */ +#define THRESH_INACT	0x25	/* R/W Inactivity threshold */ +#define TIME_INACT	0x26	/* R/W Inactivity time */ +#define ACT_INACT_CTL	0x27	/* R/W Axis enable control for activity and */ +				/* inactivity detection */ +#define THRESH_FF	0x28	/* R/W Free-fall threshold */ +#define TIME_FF		0x29	/* R/W Free-fall time */ +#define TAP_AXES	0x2A	/* R/W Axis control for tap/double tap */ +#define ACT_TAP_STATUS	0x2B	/* R   Source of tap/double tap */ +#define BW_RATE		0x2C	/* R/W Data rate and power mode control */ +#define POWER_CTL	0x2D	/* R/W Power saving features control */ +#define INT_ENABLE	0x2E	/* R/W Interrupt enable control */ +#define INT_MAP		0x2F	/* R/W Interrupt mapping control */ +#define INT_SOURCE	0x30	/* R   Source of interrupts */ +#define DATA_FORMAT	0x31	/* R/W Data format control */ +#define DATAX0		0x32	/* R   X-Axis Data 0 */ +#define DATAX1		0x33	/* R   X-Axis Data 1 */ +#define DATAY0		0x34	/* R   Y-Axis Data 0 */ +#define DATAY1		0x35	/* R   Y-Axis Data 1 */ +#define DATAZ0		0x36	/* R   Z-Axis Data 0 */ +#define DATAZ1		0x37	/* R   Z-Axis Data 1 */ +#define FIFO_CTL	0x38	/* R/W FIFO control */ +#define FIFO_STATUS	0x39	/* R   FIFO status */ +#define TAP_SIGN	0x3A	/* R   Sign and source for tap/double tap */ +/* Orientation ADXL346 only */ +#define ORIENT_CONF	0x3B	/* R/W Orientation configuration */ +#define ORIENT		0x3C	/* R   Orientation status */ + +/* DEVIDs */ +#define ID_ADXL345	0xE5 +#define ID_ADXL346	0xE6 + +/* INT_ENABLE/INT_MAP/INT_SOURCE Bits */ +#define DATA_READY	(1 << 7) +#define SINGLE_TAP	(1 << 6) +#define DOUBLE_TAP	(1 << 5) +#define ACTIVITY	(1 << 4) +#define INACTIVITY	(1 << 3) +#define FREE_FALL	(1 << 2) +#define WATERMARK	(1 << 1) +#define OVERRUN		(1 << 0) + +/* ACT_INACT_CONTROL Bits */ +#define ACT_ACDC	(1 << 7) +#define ACT_X_EN	(1 << 6) +#define ACT_Y_EN	(1 << 5) +#define ACT_Z_EN	(1 << 4) +#define INACT_ACDC	(1 << 3) +#define INACT_X_EN	(1 << 2) +#define INACT_Y_EN	(1 << 1) +#define INACT_Z_EN	(1 << 0) + +/* TAP_AXES Bits */ +#define SUPPRESS	(1 << 3) +#define TAP_X_EN	(1 << 2) +#define TAP_Y_EN	(1 << 1) +#define TAP_Z_EN	(1 << 0) + +/* ACT_TAP_STATUS Bits */ +#define ACT_X_SRC	(1 << 6) +#define ACT_Y_SRC	(1 << 5) +#define ACT_Z_SRC	(1 << 4) +#define ASLEEP		(1 << 3) +#define TAP_X_SRC	(1 << 2) +#define TAP_Y_SRC	(1 << 1) +#define TAP_Z_SRC	(1 << 0) + +/* BW_RATE Bits */ +#define LOW_POWER	(1 << 4) +#define RATE(x)		((x) & 0xF) + +/* POWER_CTL Bits */ +#define PCTL_LINK	(1 << 5) +#define PCTL_AUTO_SLEEP (1 << 4) +#define PCTL_MEASURE	(1 << 3) +#define PCTL_SLEEP	(1 << 2) +#define PCTL_WAKEUP(x)	((x) & 0x3) + +/* DATA_FORMAT Bits */ +#define SELF_TEST	(1 << 7) +#define SPI		(1 << 6) +#define INT_INVERT	(1 << 5) +#define FULL_RES	(1 << 3) +#define JUSTIFY		(1 << 2) +#define RANGE(x)	((x) & 0x3) +#define RANGE_PM_2g	0 +#define RANGE_PM_4g	1 +#define RANGE_PM_8g	2 +#define RANGE_PM_16g	3 + +/* + * Maximum value our axis may get in full res mode for the input device + * (signed 13 bits) + */ +#define ADXL_FULLRES_MAX_VAL 4096 + +/* + * Maximum value our axis may get in fixed res mode for the input device + * (signed 10 bits) + */ +#define ADXL_FIXEDRES_MAX_VAL 512 + +/* FIFO_CTL Bits */ +#define FIFO_MODE(x)	(((x) & 0x3) << 6) +#define FIFO_BYPASS	0 +#define FIFO_FIFO	1 +#define FIFO_STREAM	2 +#define FIFO_TRIGGER	3 +#define TRIGGER		(1 << 5) +#define SAMPLES(x)	((x) & 0x1F) + +/* FIFO_STATUS Bits */ +#define FIFO_TRIG	(1 << 7) +#define ENTRIES(x)	((x) & 0x3F) + +/* TAP_SIGN Bits ADXL346 only */ +#define XSIGN		(1 << 6) +#define YSIGN		(1 << 5) +#define ZSIGN		(1 << 4) +#define XTAP		(1 << 3) +#define YTAP		(1 << 2) +#define ZTAP		(1 << 1) + +/* ORIENT_CONF ADXL346 only */ +#define ORIENT_DEADZONE(x)	(((x) & 0x7) << 4) +#define ORIENT_DIVISOR(x)	((x) & 0x7) + +/* ORIENT ADXL346 only */ +#define ADXL346_2D_VALID		(1 << 6) +#define ADXL346_2D_ORIENT(x)		(((x) & 0x3) >> 4) +#define ADXL346_3D_VALID		(1 << 3) +#define ADXL346_3D_ORIENT(x)		((x) & 0x7) +#define ADXL346_2D_PORTRAIT_POS		0	/* +X */ +#define ADXL346_2D_PORTRAIT_NEG		1	/* -X */ +#define ADXL346_2D_LANDSCAPE_POS	2	/* +Y */ +#define ADXL346_2D_LANDSCAPE_NEG	3	/* -Y */ + +#define ADXL346_3D_FRONT		3	/* +X */ +#define ADXL346_3D_BACK			4	/* -X */ +#define ADXL346_3D_RIGHT		2	/* +Y */ +#define ADXL346_3D_LEFT			5	/* -Y */ +#define ADXL346_3D_TOP			1	/* +Z */ +#define ADXL346_3D_BOTTOM		6	/* -Z */ + +#undef ADXL_DEBUG + +#define ADXL_X_AXIS			0 +#define ADXL_Y_AXIS			1 +#define ADXL_Z_AXIS			2 + +#define AC_READ(ac, reg)	((ac)->bops->read((ac)->dev, reg)) +#define AC_WRITE(ac, reg, val)	((ac)->bops->write((ac)->dev, reg, val)) + +struct axis_triple { +	int x; +	int y; +	int z; +}; + +struct adxl34x { +	struct device *dev; +	struct input_dev *input; +	struct mutex mutex;	/* reentrant protection for struct */ +	struct adxl34x_platform_data pdata; +	struct axis_triple swcal; +	struct axis_triple hwcal; +	struct axis_triple saved; +	char phys[32]; +	unsigned orient2d_saved; +	unsigned orient3d_saved; +	bool disabled;	/* P: mutex */ +	bool opened;	/* P: mutex */ +	bool suspended;	/* P: mutex */ +	bool fifo_delay; +	int irq; +	unsigned model; +	unsigned int_mask; + +	const struct adxl34x_bus_ops *bops; +}; + +static const struct adxl34x_platform_data adxl34x_default_init = { +	.tap_threshold = 35, +	.tap_duration = 3, +	.tap_latency = 20, +	.tap_window = 20, +	.tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN, +	.act_axis_control = 0xFF, +	.activity_threshold = 6, +	.inactivity_threshold = 4, +	.inactivity_time = 3, +	.free_fall_threshold = 8, +	.free_fall_time = 0x20, +	.data_rate = 8, +	.data_range = ADXL_FULL_RES, + +	.ev_type = EV_ABS, +	.ev_code_x = ABS_X,	/* EV_REL */ +	.ev_code_y = ABS_Y,	/* EV_REL */ +	.ev_code_z = ABS_Z,	/* EV_REL */ + +	.ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */ +	.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK, +	.fifo_mode = FIFO_STREAM, +	.watermark = 0, +}; + +static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis) +{ +	short buf[3]; + +	ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf); + +	mutex_lock(&ac->mutex); +	ac->saved.x = (s16) le16_to_cpu(buf[0]); +	axis->x = ac->saved.x; + +	ac->saved.y = (s16) le16_to_cpu(buf[1]); +	axis->y = ac->saved.y; + +	ac->saved.z = (s16) le16_to_cpu(buf[2]); +	axis->z = ac->saved.z; +	mutex_unlock(&ac->mutex); +} + +static void adxl34x_service_ev_fifo(struct adxl34x *ac) +{ +	struct adxl34x_platform_data *pdata = &ac->pdata; +	struct axis_triple axis; + +	adxl34x_get_triple(ac, &axis); + +	input_event(ac->input, pdata->ev_type, pdata->ev_code_x, +		    axis.x - ac->swcal.x); +	input_event(ac->input, pdata->ev_type, pdata->ev_code_y, +		    axis.y - ac->swcal.y); +	input_event(ac->input, pdata->ev_type, pdata->ev_code_z, +		    axis.z - ac->swcal.z); +} + +static void adxl34x_report_key_single(struct input_dev *input, int key) +{ +	input_report_key(input, key, true); +	input_sync(input); +	input_report_key(input, key, false); +} + +static void adxl34x_send_key_events(struct adxl34x *ac, +		struct adxl34x_platform_data *pdata, int status, int press) +{ +	int i; + +	for (i = ADXL_X_AXIS; i <= ADXL_Z_AXIS; i++) { +		if (status & (1 << (ADXL_Z_AXIS - i))) +			input_report_key(ac->input, +					 pdata->ev_code_tap[i], press); +	} +} + +static void adxl34x_do_tap(struct adxl34x *ac, +		struct adxl34x_platform_data *pdata, int status) +{ +	adxl34x_send_key_events(ac, pdata, status, true); +	input_sync(ac->input); +	adxl34x_send_key_events(ac, pdata, status, false); +} + +static irqreturn_t adxl34x_irq(int irq, void *handle) +{ +	struct adxl34x *ac = handle; +	struct adxl34x_platform_data *pdata = &ac->pdata; +	int int_stat, tap_stat, samples, orient, orient_code; + +	/* +	 * ACT_TAP_STATUS should be read before clearing the interrupt +	 * Avoid reading ACT_TAP_STATUS in case TAP detection is disabled +	 */ + +	if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN)) +		tap_stat = AC_READ(ac, ACT_TAP_STATUS); +	else +		tap_stat = 0; + +	int_stat = AC_READ(ac, INT_SOURCE); + +	if (int_stat & FREE_FALL) +		adxl34x_report_key_single(ac->input, pdata->ev_code_ff); + +	if (int_stat & OVERRUN) +		dev_dbg(ac->dev, "OVERRUN\n"); + +	if (int_stat & (SINGLE_TAP | DOUBLE_TAP)) { +		adxl34x_do_tap(ac, pdata, tap_stat); + +		if (int_stat & DOUBLE_TAP) +			adxl34x_do_tap(ac, pdata, tap_stat); +	} + +	if (pdata->ev_code_act_inactivity) { +		if (int_stat & ACTIVITY) +			input_report_key(ac->input, +					 pdata->ev_code_act_inactivity, 1); +		if (int_stat & INACTIVITY) +			input_report_key(ac->input, +					 pdata->ev_code_act_inactivity, 0); +	} + +	/* +	 * ORIENTATION SENSING ADXL346 only +	 */ +	if (pdata->orientation_enable) { +		orient = AC_READ(ac, ORIENT); +		if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) && +		    (orient & ADXL346_2D_VALID)) { + +			orient_code = ADXL346_2D_ORIENT(orient); +			/* Report orientation only when it changes */ +			if (ac->orient2d_saved != orient_code) { +				ac->orient2d_saved = orient_code; +				adxl34x_report_key_single(ac->input, +					pdata->ev_codes_orient_2d[orient_code]); +			} +		} + +		if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) && +		    (orient & ADXL346_3D_VALID)) { + +			orient_code = ADXL346_3D_ORIENT(orient) - 1; +			/* Report orientation only when it changes */ +			if (ac->orient3d_saved != orient_code) { +				ac->orient3d_saved = orient_code; +				adxl34x_report_key_single(ac->input, +					pdata->ev_codes_orient_3d[orient_code]); +			} +		} +	} + +	if (int_stat & (DATA_READY | WATERMARK)) { + +		if (pdata->fifo_mode) +			samples = ENTRIES(AC_READ(ac, FIFO_STATUS)) + 1; +		else +			samples = 1; + +		for (; samples > 0; samples--) { +			adxl34x_service_ev_fifo(ac); +			/* +			 * To ensure that the FIFO has +			 * completely popped, there must be at least 5 us between +			 * the end of reading the data registers, signified by the +			 * transition to register 0x38 from 0x37 or the CS pin +			 * going high, and the start of new reads of the FIFO or +			 * reading the FIFO_STATUS register. For SPI operation at +			 * 1.5 MHz or lower, the register addressing portion of the +			 * transmission is sufficient delay to ensure the FIFO has +			 * completely popped. It is necessary for SPI operation +			 * greater than 1.5 MHz to de-assert the CS pin to ensure a +			 * total of 5 us, which is at most 3.4 us at 5 MHz +			 * operation. +			 */ +			if (ac->fifo_delay && (samples > 1)) +				udelay(3); +		} +	} + +	input_sync(ac->input); + +	return IRQ_HANDLED; +} + +static void __adxl34x_disable(struct adxl34x *ac) +{ +	/* +	 * A '0' places the ADXL34x into standby mode +	 * with minimum power consumption. +	 */ +	AC_WRITE(ac, POWER_CTL, 0); +} + +static void __adxl34x_enable(struct adxl34x *ac) +{ +	AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE); +} + +void adxl34x_suspend(struct adxl34x *ac) +{ +	mutex_lock(&ac->mutex); + +	if (!ac->suspended && !ac->disabled && ac->opened) +		__adxl34x_disable(ac); + +	ac->suspended = true; + +	mutex_unlock(&ac->mutex); +} +EXPORT_SYMBOL_GPL(adxl34x_suspend); + +void adxl34x_resume(struct adxl34x *ac) +{ +	mutex_lock(&ac->mutex); + +	if (ac->suspended && !ac->disabled && ac->opened) +		__adxl34x_enable(ac); + +	ac->suspended = false; + +	mutex_unlock(&ac->mutex); +} +EXPORT_SYMBOL_GPL(adxl34x_resume); + +static ssize_t adxl34x_disable_show(struct device *dev, +				    struct device_attribute *attr, char *buf) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); + +	return sprintf(buf, "%u\n", ac->disabled); +} + +static ssize_t adxl34x_disable_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); +	unsigned long val; +	int error; + +	error = strict_strtoul(buf, 10, &val); +	if (error) +		return error; + +	mutex_lock(&ac->mutex); + +	if (!ac->suspended && ac->opened) { +		if (val) { +			if (!ac->disabled) +				__adxl34x_disable(ac); +		} else { +			if (ac->disabled) +				__adxl34x_enable(ac); +		} +	} + +	ac->disabled = !!val; + +	mutex_unlock(&ac->mutex); + +	return count; +} + +static DEVICE_ATTR(disable, 0664, adxl34x_disable_show, adxl34x_disable_store); + +static ssize_t adxl34x_calibrate_show(struct device *dev, +				      struct device_attribute *attr, char *buf) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); +	ssize_t count; + +	mutex_lock(&ac->mutex); +	count = sprintf(buf, "%d,%d,%d\n", +			ac->hwcal.x * 4 + ac->swcal.x, +			ac->hwcal.y * 4 + ac->swcal.y, +			ac->hwcal.z * 4 + ac->swcal.z); +	mutex_unlock(&ac->mutex); + +	return count; +} + +static ssize_t adxl34x_calibrate_store(struct device *dev, +				       struct device_attribute *attr, +				       const char *buf, size_t count) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); + +	/* +	 * Hardware offset calibration has a resolution of 15.6 mg/LSB. +	 * We use HW calibration and handle the remaining bits in SW. (4mg/LSB) +	 */ + +	mutex_lock(&ac->mutex); +	ac->hwcal.x -= (ac->saved.x / 4); +	ac->swcal.x = ac->saved.x % 4; + +	ac->hwcal.y -= (ac->saved.y / 4); +	ac->swcal.y = ac->saved.y % 4; + +	ac->hwcal.z -= (ac->saved.z / 4); +	ac->swcal.z = ac->saved.z % 4; + +	AC_WRITE(ac, OFSX, (s8) ac->hwcal.x); +	AC_WRITE(ac, OFSY, (s8) ac->hwcal.y); +	AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z); +	mutex_unlock(&ac->mutex); + +	return count; +} + +static DEVICE_ATTR(calibrate, 0664, +		   adxl34x_calibrate_show, adxl34x_calibrate_store); + +static ssize_t adxl34x_rate_show(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); + +	return sprintf(buf, "%u\n", RATE(ac->pdata.data_rate)); +} + +static ssize_t adxl34x_rate_store(struct device *dev, +				  struct device_attribute *attr, +				  const char *buf, size_t count) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); +	unsigned long val; +	int error; + +	error = strict_strtoul(buf, 10, &val); +	if (error) +		return error; + +	mutex_lock(&ac->mutex); + +	ac->pdata.data_rate = RATE(val); +	AC_WRITE(ac, BW_RATE, +		 ac->pdata.data_rate | +			(ac->pdata.low_power_mode ? LOW_POWER : 0)); + +	mutex_unlock(&ac->mutex); + +	return count; +} + +static DEVICE_ATTR(rate, 0664, adxl34x_rate_show, adxl34x_rate_store); + +static ssize_t adxl34x_autosleep_show(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); + +	return sprintf(buf, "%u\n", +		ac->pdata.power_mode & (PCTL_AUTO_SLEEP | PCTL_LINK) ? 1 : 0); +} + +static ssize_t adxl34x_autosleep_store(struct device *dev, +				  struct device_attribute *attr, +				  const char *buf, size_t count) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); +	unsigned long val; +	int error; + +	error = strict_strtoul(buf, 10, &val); +	if (error) +		return error; + +	mutex_lock(&ac->mutex); + +	if (val) +		ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK); +	else +		ac->pdata.power_mode &= ~(PCTL_AUTO_SLEEP | PCTL_LINK); + +	if (!ac->disabled && !ac->suspended && ac->opened) +		AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE); + +	mutex_unlock(&ac->mutex); + +	return count; +} + +static DEVICE_ATTR(autosleep, 0664, +		   adxl34x_autosleep_show, adxl34x_autosleep_store); + +static ssize_t adxl34x_position_show(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); +	ssize_t count; + +	mutex_lock(&ac->mutex); +	count = sprintf(buf, "(%d, %d, %d)\n", +			ac->saved.x, ac->saved.y, ac->saved.z); +	mutex_unlock(&ac->mutex); + +	return count; +} + +static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL); + +#ifdef ADXL_DEBUG +static ssize_t adxl34x_write_store(struct device *dev, +				   struct device_attribute *attr, +				   const char *buf, size_t count) +{ +	struct adxl34x *ac = dev_get_drvdata(dev); +	unsigned long val; +	int error; + +	/* +	 * This allows basic ADXL register write access for debug purposes. +	 */ +	error = strict_strtoul(buf, 16, &val); +	if (error) +		return error; + +	mutex_lock(&ac->mutex); +	AC_WRITE(ac, val >> 8, val & 0xFF); +	mutex_unlock(&ac->mutex); + +	return count; +} + +static DEVICE_ATTR(write, 0664, NULL, adxl34x_write_store); +#endif + +static struct attribute *adxl34x_attributes[] = { +	&dev_attr_disable.attr, +	&dev_attr_calibrate.attr, +	&dev_attr_rate.attr, +	&dev_attr_autosleep.attr, +	&dev_attr_position.attr, +#ifdef ADXL_DEBUG +	&dev_attr_write.attr, +#endif +	NULL +}; + +static const struct attribute_group adxl34x_attr_group = { +	.attrs = adxl34x_attributes, +}; + +static int adxl34x_input_open(struct input_dev *input) +{ +	struct adxl34x *ac = input_get_drvdata(input); + +	mutex_lock(&ac->mutex); + +	if (!ac->suspended && !ac->disabled) +		__adxl34x_enable(ac); + +	ac->opened = true; + +	mutex_unlock(&ac->mutex); + +	return 0; +} + +static void adxl34x_input_close(struct input_dev *input) +{ +	struct adxl34x *ac = input_get_drvdata(input); + +	mutex_lock(&ac->mutex); + +	if (!ac->suspended && !ac->disabled) +		__adxl34x_disable(ac); + +	ac->opened = false; + +	mutex_unlock(&ac->mutex); +} + +struct adxl34x *adxl34x_probe(struct device *dev, int irq, +			      bool fifo_delay_default, +			      const struct adxl34x_bus_ops *bops) +{ +	struct adxl34x *ac; +	struct input_dev *input_dev; +	const struct adxl34x_platform_data *pdata; +	int err, range, i; +	unsigned char revid; + +	if (!irq) { +		dev_err(dev, "no IRQ?\n"); +		err = -ENODEV; +		goto err_out; +	} + +	ac = kzalloc(sizeof(*ac), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!ac || !input_dev) { +		err = -ENOMEM; +		goto err_free_mem; +	} + +	ac->fifo_delay = fifo_delay_default; + +	pdata = dev->platform_data; +	if (!pdata) { +		dev_dbg(dev, +			"No platfrom data: Using default initialization\n"); +		pdata = &adxl34x_default_init; +	} + +	ac->pdata = *pdata; +	pdata = &ac->pdata; + +	ac->input = input_dev; +	ac->disabled = true; +	ac->dev = dev; +	ac->irq = irq; +	ac->bops = bops; + +	mutex_init(&ac->mutex); + +	input_dev->name = "ADXL34x accelerometer"; +	revid = ac->bops->read(dev, DEVID); + +	switch (revid) { +	case ID_ADXL345: +		ac->model = 345; +		break; +	case ID_ADXL346: +		ac->model = 346; +		break; +	default: +		dev_err(dev, "Failed to probe %s\n", input_dev->name); +		err = -ENODEV; +		goto err_free_mem; +	} + +	snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev)); + +	input_dev->phys = ac->phys; +	input_dev->dev.parent = dev; +	input_dev->id.product = ac->model; +	input_dev->id.bustype = bops->bustype; +	input_dev->open = adxl34x_input_open; +	input_dev->close = adxl34x_input_close; + +	input_set_drvdata(input_dev, ac); + +	__set_bit(ac->pdata.ev_type, input_dev->evbit); + +	if (ac->pdata.ev_type == EV_REL) { +		__set_bit(REL_X, input_dev->relbit); +		__set_bit(REL_Y, input_dev->relbit); +		__set_bit(REL_Z, input_dev->relbit); +	} else { +		/* EV_ABS */ +		__set_bit(ABS_X, input_dev->absbit); +		__set_bit(ABS_Y, input_dev->absbit); +		__set_bit(ABS_Z, input_dev->absbit); + +		if (pdata->data_range & FULL_RES) +			range = ADXL_FULLRES_MAX_VAL;	/* Signed 13-bit */ +		else +			range = ADXL_FIXEDRES_MAX_VAL;	/* Signed 10-bit */ + +		input_set_abs_params(input_dev, ABS_X, -range, range, 3, 3); +		input_set_abs_params(input_dev, ABS_Y, -range, range, 3, 3); +		input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3); +	} + +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit); +	__set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit); +	__set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit); + +	if (pdata->ev_code_ff) { +		ac->int_mask = FREE_FALL; +		__set_bit(pdata->ev_code_ff, input_dev->keybit); +	} + +	if (pdata->ev_code_act_inactivity) +		__set_bit(pdata->ev_code_act_inactivity, input_dev->keybit); + +	ac->int_mask |= ACTIVITY | INACTIVITY; + +	if (pdata->watermark) { +		ac->int_mask |= WATERMARK; +		if (!FIFO_MODE(pdata->fifo_mode)) +			ac->pdata.fifo_mode |= FIFO_STREAM; +	} else { +		ac->int_mask |= DATA_READY; +	} + +	if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN)) +		ac->int_mask |= SINGLE_TAP | DOUBLE_TAP; + +	if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS) +		ac->fifo_delay = false; + +	ac->bops->write(dev, POWER_CTL, 0); + +	err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, +				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +				   dev_name(dev), ac); +	if (err) { +		dev_err(dev, "irq %d busy?\n", ac->irq); +		goto err_free_mem; +	} + +	err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group); +	if (err) +		goto err_free_irq; + +	err = input_register_device(input_dev); +	if (err) +		goto err_remove_attr; + +	AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); +	AC_WRITE(ac, OFSX, pdata->x_axis_offset); +	ac->hwcal.x = pdata->x_axis_offset; +	AC_WRITE(ac, OFSY, pdata->y_axis_offset); +	ac->hwcal.y = pdata->y_axis_offset; +	AC_WRITE(ac, OFSZ, pdata->z_axis_offset); +	ac->hwcal.z = pdata->z_axis_offset; +	AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); +	AC_WRITE(ac, DUR, pdata->tap_duration); +	AC_WRITE(ac, LATENT, pdata->tap_latency); +	AC_WRITE(ac, WINDOW, pdata->tap_window); +	AC_WRITE(ac, THRESH_ACT, pdata->activity_threshold); +	AC_WRITE(ac, THRESH_INACT, pdata->inactivity_threshold); +	AC_WRITE(ac, TIME_INACT, pdata->inactivity_time); +	AC_WRITE(ac, THRESH_FF, pdata->free_fall_threshold); +	AC_WRITE(ac, TIME_FF, pdata->free_fall_time); +	AC_WRITE(ac, TAP_AXES, pdata->tap_axis_control); +	AC_WRITE(ac, ACT_INACT_CTL, pdata->act_axis_control); +	AC_WRITE(ac, BW_RATE, RATE(ac->pdata.data_rate) | +		 (pdata->low_power_mode ? LOW_POWER : 0)); +	AC_WRITE(ac, DATA_FORMAT, pdata->data_range); +	AC_WRITE(ac, FIFO_CTL, FIFO_MODE(pdata->fifo_mode) | +			SAMPLES(pdata->watermark)); + +	if (pdata->use_int2) { +		/* Map all INTs to INT2 */ +		AC_WRITE(ac, INT_MAP, ac->int_mask | OVERRUN); +	} else { +		/* Map all INTs to INT1 */ +		AC_WRITE(ac, INT_MAP, 0); +	} + +	if (ac->model == 346 && ac->pdata.orientation_enable) { +		AC_WRITE(ac, ORIENT_CONF, +			ORIENT_DEADZONE(ac->pdata.deadzone_angle) | +			ORIENT_DIVISOR(ac->pdata.divisor_length)); + +		ac->orient2d_saved = 1234; +		ac->orient3d_saved = 1234; + +		if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) +			for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++) +				__set_bit(pdata->ev_codes_orient_3d[i], +					  input_dev->keybit); + +		if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) +			for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++) +				__set_bit(pdata->ev_codes_orient_2d[i], +					  input_dev->keybit); +	} else { +		ac->pdata.orientation_enable = 0; +	} + +	AC_WRITE(ac, INT_ENABLE, ac->int_mask | OVERRUN); + +	ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK); + +	return ac; + + err_remove_attr: +	sysfs_remove_group(&dev->kobj, &adxl34x_attr_group); + err_free_irq: +	free_irq(ac->irq, ac); + err_free_mem: +	input_free_device(input_dev); +	kfree(ac); + err_out: +	return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(adxl34x_probe); + +int adxl34x_remove(struct adxl34x *ac) +{ +	sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group); +	free_irq(ac->irq, ac); +	input_unregister_device(ac->input); +	dev_dbg(ac->dev, "unregistered accelerometer\n"); +	kfree(ac); + +	return 0; +} +EXPORT_SYMBOL_GPL(adxl34x_remove); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h new file mode 100644 index 00000000000..bbbc80fda16 --- /dev/null +++ b/drivers/input/misc/adxl34x.h @@ -0,0 +1,30 @@ +/* + * ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADXL34X_H_ +#define _ADXL34X_H_ + +struct device; +struct adxl34x; + +struct adxl34x_bus_ops { +	u16 bustype; +	int (*read)(struct device *, unsigned char); +	int (*read_block)(struct device *, unsigned char, int, void *); +	int (*write)(struct device *, unsigned char, unsigned char); +}; + +void adxl34x_suspend(struct adxl34x *ac); +void adxl34x_resume(struct adxl34x *ac); +struct adxl34x *adxl34x_probe(struct device *dev, int irq, +			      bool fifo_delay_default, +			      const struct adxl34x_bus_ops *bops); +int adxl34x_remove(struct adxl34x *ac); + +#endif diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index dfaa9a045ed..601f7372f9c 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -21,6 +21,8 @@   *   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/init.h> @@ -60,12 +62,11 @@ static acpi_status acpi_atlas_button_handler(u32 function,  		input_report_key(input_dev, atlas_keymap[code], key_down);  		input_sync(input_dev); -		status = 0; +		status = AE_OK;  	} else { -		printk(KERN_WARNING "atlas: shrugged on unexpected function" -			":function=%x,address=%lx,value=%x\n", +		pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n",  			function, (unsigned long)address, (u32)*value); -		status = -EINVAL; +		status = AE_BAD_PARAMETER;  	}  	return status; @@ -79,7 +80,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)  	input_dev = input_allocate_device();  	if (!input_dev) { -		printk(KERN_ERR "atlas: unable to allocate input device\n"); +		pr_err("unable to allocate input device\n");  		return -ENOMEM;  	} @@ -102,7 +103,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)  	err = input_register_device(input_dev);  	if (err) { -		printk(KERN_ERR "atlas: couldn't register input device\n"); +		pr_err("couldn't register input device\n");  		input_free_device(input_dev);  		return err;  	} @@ -112,12 +113,12 @@ static int atlas_acpi_button_add(struct acpi_device *device)  				0x81, &acpi_atlas_button_handler,  				&acpi_atlas_button_setup, device);  	if (ACPI_FAILURE(status)) { -		printk(KERN_ERR "Atlas: Error installing addr spc handler\n"); +		pr_err("error installing addr spc handler\n");  		input_unregister_device(input_dev); -		status = -EINVAL; +		err = -EINVAL;  	} -	return status; +	return err;  }  static int atlas_acpi_button_remove(struct acpi_device *device, int type) @@ -126,14 +127,12 @@ static int atlas_acpi_button_remove(struct acpi_device *device, int type)  	status = acpi_remove_address_space_handler(device->handle,  				0x81, &acpi_atlas_button_handler); -	if (ACPI_FAILURE(status)) { -		printk(KERN_ERR "Atlas: Error removing addr spc handler\n"); -		status = -EINVAL; -	} +	if (ACPI_FAILURE(status)) +		pr_err("error removing addr spc handler\n");  	input_unregister_device(input_dev); -	return status; +	return 0;  }  static const struct acpi_device_id atlas_device_ids[] = { @@ -145,6 +144,7 @@ MODULE_DEVICE_TABLE(acpi, atlas_device_ids);  static struct acpi_driver atlas_acpi_driver = {  	.name	= ACPI_ATLAS_NAME,  	.class	= ACPI_ATLAS_CLASS, +	.owner	= THIS_MODULE,  	.ids	= atlas_device_ids,  	.ops	= {  		.add	= atlas_acpi_button_add, @@ -154,18 +154,10 @@ static struct acpi_driver atlas_acpi_driver = {  static int __init atlas_acpi_init(void)  { -	int result; -  	if (acpi_disabled)  		return -ENODEV; -	result = acpi_bus_register_driver(&atlas_acpi_driver); -	if (result < 0) { -		printk(KERN_ERR "Atlas ACPI: Unable to register driver\n"); -		return -ENODEV; -	} - -	return 0; +	return acpi_bus_register_driver(&atlas_acpi_driver);  }  static void __exit atlas_acpi_exit(void) diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c new file mode 100644 index 00000000000..57c294f0719 --- /dev/null +++ b/drivers/input/misc/pwm-beeper.c @@ -0,0 +1,199 @@ +/* + *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> + *  PWM beeper driver + * + *  This program is free software; you can redistribute it and/or modify it + *  under  the terms of the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the License, or (at your + *  option) any later version. + * + *  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., + *  675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +struct pwm_beeper { +	struct input_dev *input; +	struct pwm_device *pwm; +	unsigned long period; +}; + +#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) + +static int pwm_beeper_event(struct input_dev *input, +			    unsigned int type, unsigned int code, int value) +{ +	int ret = 0; +	struct pwm_beeper *beeper = input_get_drvdata(input); +	unsigned long period; + +	if (type != EV_SND || value < 0) +		return -EINVAL; + +	switch (code) { +	case SND_BELL: +		value = value ? 1000 : 0; +		break; +	case SND_TONE: +		break; +	default: +		return -EINVAL; +	} + +	if (value == 0) { +		pwm_config(beeper->pwm, 0, 0); +		pwm_disable(beeper->pwm); +	} else { +		period = HZ_TO_NANOSECONDS(value); +		ret = pwm_config(beeper->pwm, period / 2, period); +		if (ret) +			return ret; +		ret = pwm_enable(beeper->pwm); +		if (ret) +			return ret; +		beeper->period = period; +	} + +	return 0; +} + +static int __devinit pwm_beeper_probe(struct platform_device *pdev) +{ +	unsigned long pwm_id = (unsigned long)pdev->dev.platform_data; +	struct pwm_beeper *beeper; +	int error; + +	beeper = kzalloc(sizeof(*beeper), GFP_KERNEL); +	if (!beeper) +		return -ENOMEM; + +	beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + +	if (IS_ERR(beeper->pwm)) { +		error = PTR_ERR(beeper->pwm); +		dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); +		goto err_free; +	} + +	beeper->input = input_allocate_device(); +	if (!beeper->input) { +		dev_err(&pdev->dev, "Failed to allocate input device\n"); +		error = -ENOMEM; +		goto err_pwm_free; +	} +	beeper->input->dev.parent = &pdev->dev; + +	beeper->input->name = "pwm-beeper"; +	beeper->input->phys = "pwm/input0"; +	beeper->input->id.bustype = BUS_HOST; +	beeper->input->id.vendor = 0x001f; +	beeper->input->id.product = 0x0001; +	beeper->input->id.version = 0x0100; + +	beeper->input->evbit[0] = BIT(EV_SND); +	beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); + +	beeper->input->event = pwm_beeper_event; + +	input_set_drvdata(beeper->input, beeper); + +	error = input_register_device(beeper->input); +	if (error) { +		dev_err(&pdev->dev, "Failed to register input device: %d\n", error); +		goto err_input_free; +	} + +	platform_set_drvdata(pdev, beeper); + +	return 0; + +err_input_free: +	input_free_device(beeper->input); +err_pwm_free: +	pwm_free(beeper->pwm); +err_free: +	kfree(beeper); + +	return error; +} + +static int __devexit pwm_beeper_remove(struct platform_device *pdev) +{ +	struct pwm_beeper *beeper = platform_get_drvdata(pdev); + +	platform_set_drvdata(pdev, NULL); +	input_unregister_device(beeper->input); + +	pwm_disable(beeper->pwm); +	pwm_free(beeper->pwm); + +	kfree(beeper); + +	return 0; +} + +#ifdef CONFIG_PM +static int pwm_beeper_suspend(struct device *dev) +{ +	struct pwm_beeper *beeper = dev_get_drvdata(dev); + +	if (beeper->period) +		pwm_disable(beeper->pwm); + +	return 0; +} + +static int pwm_beeper_resume(struct device *dev) +{ +	struct pwm_beeper *beeper = dev_get_drvdata(dev); + +	if (beeper->period) { +		pwm_config(beeper->pwm, beeper->period / 2, beeper->period); +		pwm_enable(beeper->pwm); +	} + +	return 0; +} + +static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, +			 pwm_beeper_suspend, pwm_beeper_resume); + +#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops) +#else +#define PWM_BEEPER_PM_OPS NULL +#endif + +static struct platform_driver pwm_beeper_driver = { +	.probe	= pwm_beeper_probe, +	.remove = __devexit_p(pwm_beeper_remove), +	.driver = { +		.name	= "pwm-beeper", +		.owner	= THIS_MODULE, +		.pm	= PWM_BEEPER_PM_OPS, +	}, +}; + +static int __init pwm_beeper_init(void) +{ +	return platform_driver_register(&pwm_beeper_driver); +} +module_init(pwm_beeper_init); + +static void __exit pwm_beeper_exit(void) +{ +	platform_driver_unregister(&pwm_beeper_driver); +} +module_exit(pwm_beeper_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("PWM beeper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pwm-beeper"); diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 1dacae4b43f..f3bb92e9755 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -353,14 +353,12 @@ static struct of_platform_driver grover_beep_driver = {  static int __init sparcspkr_init(void)  { -	int err = of_register_driver(&bbc_beep_driver, -				     &of_platform_bus_type); +	int err = of_register_platform_driver(&bbc_beep_driver);  	if (!err) { -		err = of_register_driver(&grover_beep_driver, -					 &of_platform_bus_type); +		err = of_register_platform_driver(&grover_beep_driver);  		if (err) -			of_unregister_driver(&bbc_beep_driver); +			of_unregister_platform_driver(&bbc_beep_driver);  	}  	return err; @@ -368,8 +366,8 @@ static int __init sparcspkr_init(void)  static void __exit sparcspkr_exit(void)  { -	of_unregister_driver(&bbc_beep_driver); -	of_unregister_driver(&grover_beep_driver); +	of_unregister_platform_driver(&bbc_beep_driver); +	of_unregister_platform_driver(&grover_beep_driver);  }  module_init(sparcspkr_init); diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index e9069b87fde..f16972bddca 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -52,7 +52,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr)  	return IRQ_HANDLED;  } -static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev) +static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)  {  	struct input_dev *pwr;  	int irq = platform_get_irq(pdev, 0); @@ -95,7 +95,7 @@ free_input_dev:  	return err;  } -static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev) +static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev)  {  	struct input_dev *pwr = platform_get_drvdata(pdev);  	int irq = platform_get_irq(pdev, 0); @@ -106,9 +106,8 @@ static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)  	return 0;  } -struct platform_driver twl4030_pwrbutton_driver = { -	.probe		= twl4030_pwrbutton_probe, -	.remove		= __devexit_p(twl4030_pwrbutton_remove), +static struct platform_driver twl4030_pwrbutton_driver = { +	.remove		= __exit_p(twl4030_pwrbutton_remove),  	.driver		= {  		.name	= "twl4030_pwrbutton",  		.owner	= THIS_MODULE, @@ -117,7 +116,8 @@ struct platform_driver twl4030_pwrbutton_driver = {  static int __init twl4030_pwrbutton_init(void)  { -	return platform_driver_register(&twl4030_pwrbutton_driver); +	return platform_driver_probe(&twl4030_pwrbutton_driver, +			twl4030_pwrbutton_probe);  }  module_init(twl4030_pwrbutton_init); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 4dac8b79fcd..12501de0c5c 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -1347,7 +1347,7 @@ static int __init wb_module_init(void)  	err = map_bios();  	if (err) -		return err; +		goto err_free_keymap;  	err = platform_driver_register(&wistron_driver);  	if (err) @@ -1371,6 +1371,8 @@ static int __init wb_module_init(void)  	platform_driver_unregister(&wistron_driver);   err_unmap_bios:  	unmap_bios(); + err_free_keymap: +	kfree(keymap);  	return err;  } diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 6dedded2722..ea67c49146a 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -312,6 +312,8 @@ static void setup_events_to_report(struct input_dev *input_dev,  	__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);  	__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);  	__set_bit(BTN_LEFT, input_dev->keybit); + +	input_set_events_per_packet(input_dev, 60);  }  /* report button data as logical button state */ @@ -580,23 +582,30 @@ exit:   */  static int bcm5974_start_traffic(struct bcm5974 *dev)  { -	if (bcm5974_wellspring_mode(dev, true)) { +	int error; + +	error = bcm5974_wellspring_mode(dev, true); +	if (error) {  		dprintk(1, "bcm5974: mode switch failed\n"); -		goto error; +		goto err_out;  	} -	if (usb_submit_urb(dev->bt_urb, GFP_KERNEL)) -		goto error; +	error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); +	if (error) +		goto err_reset_mode; -	if (usb_submit_urb(dev->tp_urb, GFP_KERNEL)) +	error = usb_submit_urb(dev->tp_urb, GFP_KERNEL); +	if (error)  		goto err_kill_bt;  	return 0;  err_kill_bt:  	usb_kill_urb(dev->bt_urb); -error: -	return -EIO; +err_reset_mode: +	bcm5974_wellspring_mode(dev, false); +err_out: +	return error;  }  static void bcm5974_pause_traffic(struct bcm5974 *dev) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 705589dc9ac..8c324403b9f 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -502,7 +502,9 @@ static void synaptics_process_packet(struct psmouse *psmouse)  	}  	input_report_abs(dev, ABS_PRESSURE, hw.z); -	input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); +	if (SYN_CAP_PALMDETECT(priv->capabilities)) +		input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); +  	input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);  	input_report_key(dev, BTN_LEFT, hw.left);  	input_report_key(dev, BTN_RIGHT, hw.right); @@ -602,7 +604,9 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)  	input_set_abs_params(dev, ABS_Y,  			     YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);  	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); -	__set_bit(ABS_TOOL_WIDTH, dev->absbit); + +	if (SYN_CAP_PALMDETECT(priv->capabilities)) +		input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);  	__set_bit(EV_KEY, dev->evbit);  	__set_bit(BTN_TOUCH, dev->keybit); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index f34b22bce4f..d8f68f77007 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -57,7 +57,6 @@ struct mousedev_hw_data {  };  struct mousedev { -	int exist;  	int open;  	int minor;  	struct input_handle handle; @@ -66,6 +65,7 @@ struct mousedev {  	spinlock_t client_lock; /* protects client_list */  	struct mutex mutex;  	struct device dev; +	bool exist;  	struct list_head mixdev_node;  	int mixdev_open; @@ -765,10 +765,15 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait)  {  	struct mousedev_client *client = file->private_data;  	struct mousedev *mousedev = client->mousedev; +	unsigned int mask;  	poll_wait(file, &mousedev->wait, wait); -	return ((client->ready || client->buffer) ? (POLLIN | POLLRDNORM) : 0) | -		(mousedev->exist ? 0 : (POLLHUP | POLLERR)); + +	mask = mousedev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; +	if (client->ready || client->buffer) +		mask |= POLLIN | POLLRDNORM; + +	return mask;  }  static const struct file_operations mousedev_fops = { @@ -802,7 +807,7 @@ static void mousedev_remove_chrdev(struct mousedev *mousedev)  static void mousedev_mark_dead(struct mousedev *mousedev)  {  	mutex_lock(&mousedev->mutex); -	mousedev->exist = 0; +	mousedev->exist = false;  	mutex_unlock(&mousedev->mutex);  } @@ -862,7 +867,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,  		dev_set_name(&mousedev->dev, "mouse%d", minor);  	mousedev->minor = minor; -	mousedev->exist = 1; +	mousedev->exist = true;  	mousedev->handle.dev = input_get_device(dev);  	mousedev->handle.name = dev_name(&mousedev->dev);  	mousedev->handle.handler = handler; diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index 847f4aad7ed..5d48bb66aa7 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -27,6 +27,11 @@  #include <asm/irq.h>  #elif defined(CONFIG_SH_CAYMAN)  #include <asm/irq.h> +#elif defined(CONFIG_PPC) +extern int of_i8042_kbd_irq; +extern int of_i8042_aux_irq; +# define I8042_KBD_IRQ  of_i8042_kbd_irq +# define I8042_AUX_IRQ  of_i8042_aux_irq  #else  # define I8042_KBD_IRQ	1  # define I8042_AUX_IRQ	12 diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h index 2906e1b60c0..f708c75d16f 100644 --- a/drivers/input/serio/i8042-ppcio.h +++ b/drivers/input/serio/i8042-ppcio.h @@ -52,81 +52,6 @@ static inline void i8042_platform_exit(void)  {  } -#elif defined(CONFIG_SPRUCE) - -#define I8042_KBD_IRQ 22 -#define I8042_AUX_IRQ 21 - -#define I8042_KBD_PHYS_DESC "spruceps2/serio0" -#define I8042_AUX_PHYS_DESC "spruceps2/serio1" -#define I8042_MUX_PHYS_DESC "spruceps2/serio%d" - -#define I8042_COMMAND_REG 0xff810000 -#define I8042_DATA_REG 0xff810001 - -static inline int i8042_read_data(void) -{ -	unsigned long kbd_data; - -	__raw_writel(0x00000088, 0xff500008); -	eieio(); - -	__raw_writel(0x03000000, 0xff50000c); -	eieio(); - -	asm volatile("lis     7,0xff88        \n\ -		      lswi    6,7,0x8         \n\ -		      mr      %0,6" -	              : "=r" (kbd_data) :: "6", "7"); - -	__raw_writel(0x00000000, 0xff50000c); -	eieio(); - -	return (unsigned char)(kbd_data >> 24); -} - -static inline int i8042_read_status(void) -{ -	unsigned long kbd_status; - -	__raw_writel(0x00000088, 0xff500008); -	eieio(); - -	__raw_writel(0x03000000, 0xff50000c); -	eieio(); - -	asm volatile("lis     7,0xff88        \n\ -		      ori     7,7,0x8         \n\ -		      lswi    6,7,0x8         \n\ -		      mr      %0,6" -		      : "=r" (kbd_status) :: "6", "7"); - -	__raw_writel(0x00000000, 0xff50000c); -	eieio(); - -	return (unsigned char)(kbd_status >> 24); -} - -static inline void i8042_write_data(int val) -{ -	*((unsigned char *)0xff810000) = (char)val; -} - -static inline void i8042_write_command(int val) -{ -	*((unsigned char *)0xff810001) = (char)val; -} - -static inline int i8042_platform_init(void) -{ -	i8042_reset = 1; -	return 0; -} - -static inline void i8042_platform_exit(void) -{ -} -  #else  #include "i8042-io.h" diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index 04e32f2d124..cb2a24b9474 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -58,9 +58,9 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev  		if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||  		    !strcmp(dp->name, OBP_PS2KBD_NAME2)) {  			struct of_device *kbd = of_find_device_by_node(dp); -			unsigned int irq = kbd->irqs[0]; +			unsigned int irq = kbd->archdata.irqs[0];  			if (irq == 0xffffffff) -				irq = op->irqs[0]; +				irq = op->archdata.irqs[0];  			i8042_kbd_irq = irq;  			kbd_iobase = of_ioremap(&kbd->resource[0],  						0, 8, "kbd"); @@ -68,9 +68,9 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev  		} else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||  			   !strcmp(dp->name, OBP_PS2MS_NAME2)) {  			struct of_device *ms = of_find_device_by_node(dp); -			unsigned int irq = ms->irqs[0]; +			unsigned int irq = ms->archdata.irqs[0];  			if (irq == 0xffffffff) -				irq = op->irqs[0]; +				irq = op->archdata.irqs[0];  			i8042_aux_irq = irq;  		} @@ -116,8 +116,7 @@ static int __init i8042_platform_init(void)  		if (!kbd_iobase)  			return -ENODEV;  	} else { -		int err = of_register_driver(&sparc_i8042_driver, -					     &of_bus_type); +		int err = of_register_platform_driver(&sparc_i8042_driver);  		if (err)  			return err; @@ -141,7 +140,7 @@ static inline void i8042_platform_exit(void)  	struct device_node *root = of_find_node_by_path("/");  	if (strcmp(root->name, "SUNW,JavaStation-1")) -		of_unregister_driver(&sparc_i8042_driver); +		of_unregister_platform_driver(&sparc_i8042_driver);  }  #else /* !CONFIG_PCI */ diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 6440a8f5568..258b98b9d7c 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -861,9 +861,6 @@ static int i8042_controller_selftest(void)  	unsigned char param;  	int i = 0; -	if (!i8042_reset) -		return 0; -  	/*  	 * We try this 5 times; on some really fragile systems this does not  	 * take the first time... @@ -1020,7 +1017,8 @@ static void i8042_controller_reset(void)   * Reset the controller if requested.   */ -	i8042_controller_selftest(); +	if (i8042_reset) +		i8042_controller_selftest();  /*   * Restore the original control register setting. @@ -1094,23 +1092,11 @@ static void i8042_dritek_enable(void)  #ifdef CONFIG_PM  /* - * Here we try to restore the original BIOS settings to avoid - * upsetting it. - */ - -static int i8042_pm_reset(struct device *dev) -{ -	i8042_controller_reset(); - -	return 0; -} - -/*   * Here we try to reset everything back to a state we had   * before suspending.   */ -static int i8042_pm_restore(struct device *dev) +static int i8042_controller_resume(bool force_reset)  {  	int error; @@ -1118,9 +1104,11 @@ static int i8042_pm_restore(struct device *dev)  	if (error)  		return error; -	error = i8042_controller_selftest(); -	if (error) -		return error; +	if (i8042_reset || force_reset) { +		error = i8042_controller_selftest(); +		if (error) +			return error; +	}  /*   * Restore original CTR value and disable all ports @@ -1162,6 +1150,28 @@ static int i8042_pm_restore(struct device *dev)  	return 0;  } +/* + * Here we try to restore the original BIOS settings to avoid + * upsetting it. + */ + +static int i8042_pm_reset(struct device *dev) +{ +	i8042_controller_reset(); + +	return 0; +} + +static int i8042_pm_resume(struct device *dev) +{ +	/* +	 * On resume from S2R we always try to reset the controller +	 * to bring it in a sane state. (In case of S2D we expect +	 * BIOS to reset the controller for us.) +	 */ +	return i8042_controller_resume(true); +} +  static int i8042_pm_thaw(struct device *dev)  {  	i8042_interrupt(0, NULL); @@ -1169,9 +1179,14 @@ static int i8042_pm_thaw(struct device *dev)  	return 0;  } +static int i8042_pm_restore(struct device *dev) +{ +	return i8042_controller_resume(false); +} +  static const struct dev_pm_ops i8042_pm_ops = {  	.suspend	= i8042_pm_reset, -	.resume		= i8042_pm_restore, +	.resume		= i8042_pm_resume,  	.thaw		= i8042_pm_thaw,  	.poweroff	= i8042_pm_reset,  	.restore	= i8042_pm_restore, @@ -1389,9 +1404,11 @@ static int __init i8042_probe(struct platform_device *dev)  	i8042_platform_device = dev; -	error = i8042_controller_selftest(); -	if (error) -		return error; +	if (i8042_reset) { +		error = i8042_controller_selftest(); +		if (error) +			return error; +	}  	error = i8042_controller_init();  	if (error) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 415f6306105..ce0b4608dad 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -158,6 +158,39 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)  	return 1;  } +static int wacom_dtu_irq(struct wacom_wac *wacom) +{ +	struct wacom_features *features = &wacom->features; +	char *data = wacom->data; +	struct input_dev *input = wacom->input; +	int prox = data[1] & 0x20, pressure; + +	dbg("wacom_dtu_irq: received report #%d", data[0]); + +	if (prox) { +		/* Going into proximity select tool */ +		wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; +		if (wacom->tool[0] == BTN_TOOL_PEN) +			wacom->id[0] = STYLUS_DEVICE_ID; +		else +			wacom->id[0] = ERASER_DEVICE_ID; +	} +	input_report_key(input, BTN_STYLUS, data[1] & 0x02); +	input_report_key(input, BTN_STYLUS2, data[1] & 0x10); +	input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); +	input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); +	pressure = ((data[7] & 0x01) << 8) | data[6]; +	if (pressure < 0) +		pressure = features->pressure_max + pressure + 1; +	input_report_abs(input, ABS_PRESSURE, pressure); +	input_report_key(input, BTN_TOUCH, data[1] & 0x05); +	if (!prox) /* out-prox */ +		wacom->id[0] = 0; +	input_report_key(input, wacom->tool[0], prox); +	input_report_abs(input, ABS_MISC, wacom->id[0]); +	return 1; +} +  static int wacom_graphire_irq(struct wacom_wac *wacom)  {  	struct wacom_features *features = &wacom->features; @@ -845,6 +878,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)  		sync = wacom_ptu_irq(wacom_wac);  		break; +	case DTU: +		sync = wacom_dtu_irq(wacom_wac); +		break; +  	case INTUOS:  	case INTUOS3S:  	case INTUOS3: @@ -1030,6 +1067,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,  	case PL:  	case PTU: +	case DTU:  		__set_bit(BTN_TOOL_PEN, input_dev->keybit);  		__set_bit(BTN_STYLUS, input_dev->keybit);  		__set_bit(BTN_STYLUS2, input_dev->keybit); @@ -1155,6 +1193,10 @@ static const struct wacom_features wacom_features_0xC6 =  	{ "Wacom Cintiq 12WX",    WACOM_PKGLEN_INTUOS,    53020, 33440, 1023, 63, WACOM_BEE };  static const struct wacom_features wacom_features_0xC7 =  	{ "Wacom DTU1931",        WACOM_PKGLEN_GRAPHIRE,  37832, 30305,  511,  0, PL }; +static const struct wacom_features wacom_features_0xCE = +	{ "Wacom DTU2231",        WACOM_PKGLEN_GRAPHIRE,  47864, 27011,  511,  0, DTU }; +static const struct wacom_features wacom_features_0xF0 = +	{ "Wacom DTU1631",        WACOM_PKGLEN_GRAPHIRE,  34623, 19553,  511,  0, DTU };  static const struct wacom_features wacom_features_0xCC =  	{ "Wacom Cintiq 21UX2",   WACOM_PKGLEN_INTUOS,    87200, 65600, 2047, 63, WACOM_21UX2 };  static const struct wacom_features wacom_features_0x90 = @@ -1234,6 +1276,8 @@ const struct usb_device_id wacom_ids[] = {  	{ USB_DEVICE_WACOM(0xC5) },  	{ USB_DEVICE_WACOM(0xC6) },  	{ USB_DEVICE_WACOM(0xC7) }, +	{ USB_DEVICE_WACOM(0xCE) }, +	{ USB_DEVICE_WACOM(0xF0) },  	{ USB_DEVICE_WACOM(0xCC) },  	{ USB_DEVICE_WACOM(0x90) },  	{ USB_DEVICE_WACOM(0x93) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 854b92092df..99e1a54cd30 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -43,6 +43,7 @@ enum {  	WACOM_G4,  	PTU,  	PL, +	DTU,  	INTUOS,  	INTUOS3S,  	INTUOS3, diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3b9d5e2105d..61f35184f76 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -55,37 +55,36 @@ config TOUCHSCREEN_AD7877  	  To compile this driver as a module, choose M here: the  	  module will be called ad7877. -config TOUCHSCREEN_AD7879_I2C -	tristate "AD7879 based touchscreens: AD7879-1 I2C Interface" -	depends on I2C -	select TOUCHSCREEN_AD7879 +config TOUCHSCREEN_AD7879 +	tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"  	help -	  Say Y here if you have a touchscreen interface using the -	  AD7879-1/AD7889-1 controller, and your board-specific -	  initialization code includes that in its table of I2C devices. +	  Say Y here if you want to support a touchscreen interface using +	  the AD7879-1/AD7889-1 controller. -	  If unsure, say N (but it's safe to say "Y"). +	  You should select a bus connection too.  	  To compile this driver as a module, choose M here: the  	  module will be called ad7879. +config TOUCHSCREEN_AD7879_I2C +	tristate "support I2C bus connection" +	depends on TOUCHSCREEN_AD7879 && I2C +	help +	  Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called ad7879-i2c. +  config TOUCHSCREEN_AD7879_SPI -	tristate "AD7879 based touchscreens: AD7879 SPI Interface" -	depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n -	select TOUCHSCREEN_AD7879 +	tristate "support SPI bus connection" +	depends on TOUCHSCREEN_AD7879 && SPI_MASTER  	help -	  Say Y here if you have a touchscreen interface using the -	  AD7879/AD7889 controller, and your board-specific initialization -	  code includes that in its table of SPI devices. +	  Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.  	  If unsure, say N (but it's safe to say "Y").  	  To compile this driver as a module, choose M here: the -	  module will be called ad7879. - -config TOUCHSCREEN_AD7879 -	tristate -	default n +	  module will be called ad7879-spi.  config TOUCHSCREEN_BITSY  	tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" @@ -99,6 +98,20 @@ config TOUCHSCREEN_BITSY  	  To compile this driver as a module, choose M here: the  	  module will be called h3600_ts_input. +config TOUCHSCREEN_CY8CTMG110 +	tristate "cy8ctmg110 touchscreen" +	depends on I2C +	depends on GPIOLIB + +	help +	  Say Y here if you have a cy8ctmg110 capacitive touchscreen on +	  an AAVA device. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called cy8ctmg110_ts. +  config TOUCHSCREEN_DA9034  	tristate "Touchscreen support for Dialog Semiconductor DA9034"  	depends on PMIC_DA903X @@ -292,6 +305,18 @@ config TOUCHSCREEN_PENMOUNT  	  To compile this driver as a module, choose M here: the  	  module will be called penmount. +config TOUCHSCREEN_QT602240 +	tristate "QT602240 I2C Touchscreen" +	depends on I2C +	help +	  Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen +	  connected to your system. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called qt602240_ts. +  config TOUCHSCREEN_MIGOR  	tristate "Renesas MIGO-R touchscreen"  	depends on SH_MIGOR && I2C @@ -540,9 +565,9 @@ config TOUCHSCREEN_USB_ZYTRONIC  	bool "Zytronic controller" if EMBEDDED  	depends on TOUCHSCREEN_USB_COMPOSITE -config TOUCHSCREEN_USB_ETT_TC5UH +config TOUCHSCREEN_USB_ETT_TC45USB  	default y -	bool "ET&T TC5UH touchscreen controler support" if EMBEDDED +	bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_NEXIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 497964a7a21..bd6f30b4ff7 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -9,9 +9,13 @@ wm97xx-ts-y := wm97xx-core.o  obj-$(CONFIG_TOUCHSCREEN_88PM860X)	+= 88pm860x-ts.o  obj-$(CONFIG_TOUCHSCREEN_AD7877)	+= ad7877.o  obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C)	+= ad7879-i2c.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o  obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o  obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o  obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_DA9034)	+= da9034-ts.o  obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)	+= dynapro.o  obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)	+= hampshire.o  obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o @@ -30,6 +34,7 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN)	+= htcpen.o  obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o  obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o  obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o +obj-$(CONFIG_TOUCHSCREEN_QT602240)	+= qt602240_ts.o  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o  obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o @@ -38,7 +43,6 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007)	+= tsc2007.o  obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o  obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)	+= wacom_w8001.o  obj-$(CONFIG_TOUCHSCREEN_WM97XX)	+= wm97xx-ts.o -obj-$(CONFIG_TOUCHSCREEN_DA9034)	+= da9034-ts.o  wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705)	+= wm9705.o  wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712)	+= wm9712.o  wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713)	+= wm9713.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c new file mode 100644 index 00000000000..d82a38ee9a3 --- /dev/null +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -0,0 +1,143 @@ +/* + * AD7879-1/AD7889-1 touchscreen (I2C bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h>	/* BUS_I2C */ +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> + +#include "ad7879.h" + +#define AD7879_DEVID		0x79	/* AD7879-1/AD7889-1 */ + +#ifdef CONFIG_PM +static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ +	struct ad7879 *ts = i2c_get_clientdata(client); + +	ad7879_suspend(ts); + +	return 0; +} + +static int ad7879_i2c_resume(struct i2c_client *client) +{ +	struct ad7879 *ts = i2c_get_clientdata(client); + +	ad7879_resume(ts); + +	return 0; +} +#else +# define ad7879_i2c_suspend NULL +# define ad7879_i2c_resume  NULL +#endif + +/* All registers are word-sized. + * AD7879 uses a high-byte first convention. + */ +static int ad7879_i2c_read(struct device *dev, u8 reg) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return swab16(i2c_smbus_read_word_data(client, reg)); +} + +static int ad7879_i2c_multi_read(struct device *dev, +				 u8 first_reg, u8 count, u16 *buf) +{ +	struct i2c_client *client = to_i2c_client(dev); +	u8 idx; + +	i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf); + +	for (idx = 0; idx < count; ++idx) +		buf[idx] = swab16(buf[idx]); + +	return 0; +} + +static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return i2c_smbus_write_word_data(client, reg, swab16(val)); +} + +static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { +	.bustype	= BUS_I2C, +	.read		= ad7879_i2c_read, +	.multi_read	= ad7879_i2c_multi_read, +	.write		= ad7879_i2c_write, +}; + +static int __devinit ad7879_i2c_probe(struct i2c_client *client, +				      const struct i2c_device_id *id) +{ +	struct ad7879 *ts; + +	if (!i2c_check_functionality(client->adapter, +				     I2C_FUNC_SMBUS_WORD_DATA)) { +		dev_err(&client->dev, "SMBUS Word Data not Supported\n"); +		return -EIO; +	} + +	ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq, +			  &ad7879_i2c_bus_ops); +	if (IS_ERR(ts)) +		return PTR_ERR(ts); + +	i2c_set_clientdata(client, ts); + +	return 0; +} + +static int __devexit ad7879_i2c_remove(struct i2c_client *client) +{ +	struct ad7879 *ts = i2c_get_clientdata(client); + +	ad7879_remove(ts); + +	return 0; +} + +static const struct i2c_device_id ad7879_id[] = { +	{ "ad7879", 0 }, +	{ "ad7889", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ad7879_id); + +static struct i2c_driver ad7879_i2c_driver = { +	.driver = { +		.name	= "ad7879", +		.owner	= THIS_MODULE, +	}, +	.probe		= ad7879_i2c_probe, +	.remove		= __devexit_p(ad7879_i2c_remove), +	.suspend	= ad7879_i2c_suspend, +	.resume		= ad7879_i2c_resume, +	.id_table	= ad7879_id, +}; + +static int __init ad7879_i2c_init(void) +{ +	return i2c_add_driver(&ad7879_i2c_driver); +} +module_init(ad7879_i2c_init); + +static void __exit ad7879_i2c_exit(void) +{ +	i2c_del_driver(&ad7879_i2c_driver); +} +module_exit(ad7879_i2c_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("i2c:ad7879"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c new file mode 100644 index 00000000000..59c6e68c432 --- /dev/null +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -0,0 +1,198 @@ +/* + * AD7879/AD7889 touchscreen (SPI bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h>	/* BUS_SPI */ +#include <linux/spi/spi.h> + +#include "ad7879.h" + +#define AD7879_DEVID		0x7A	/* AD7879/AD7889 */ + +#define MAX_SPI_FREQ_HZ      5000000 +#define AD7879_CMD_MAGIC     0xE000 +#define AD7879_CMD_READ      (1 << 10) +#define AD7879_CMD(reg)      (AD7879_CMD_MAGIC | ((reg) & 0xF)) +#define AD7879_WRITECMD(reg) (AD7879_CMD(reg)) +#define AD7879_READCMD(reg)  (AD7879_CMD(reg) | AD7879_CMD_READ) + +#ifdef CONFIG_PM +static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) +{ +	struct ad7879 *ts = spi_get_drvdata(spi); + +	ad7879_suspend(ts); + +	return 0; +} + +static int ad7879_spi_resume(struct spi_device *spi) +{ +	struct ad7879 *ts = spi_get_drvdata(spi); + +	ad7879_resume(ts); + +	return 0; +} +#else +# define ad7879_spi_suspend NULL +# define ad7879_spi_resume  NULL +#endif + +/* + * ad7879_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done in ad7879_collect(). + */ + +static int ad7879_spi_xfer(struct spi_device *spi, +			   u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf) +{ +	struct spi_message msg; +	struct spi_transfer *xfers; +	void *spi_data; +	u16 *command; +	u16 *_rx_buf = _rx_buf; /* shut gcc up */ +	u8 idx; +	int ret; + +	xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL); +	if (!spi_data) +		return -ENOMEM; + +	spi_message_init(&msg); + +	command = spi_data; +	command[0] = cmd; +	if (count == 1) { +		/* ad7879_spi_{read,write} gave us buf on stack */ +		command[1] = *tx_buf; +		tx_buf = &command[1]; +		_rx_buf = rx_buf; +		rx_buf = &command[2]; +	} + +	++xfers; +	xfers[0].tx_buf = command; +	xfers[0].len = 2; +	spi_message_add_tail(&xfers[0], &msg); +	++xfers; + +	for (idx = 0; idx < count; ++idx) { +		if (rx_buf) +			xfers[idx].rx_buf = &rx_buf[idx]; +		if (tx_buf) +			xfers[idx].tx_buf = &tx_buf[idx]; +		xfers[idx].len = 2; +		spi_message_add_tail(&xfers[idx], &msg); +	} + +	ret = spi_sync(spi, &msg); + +	if (count == 1) +		_rx_buf[0] = command[2]; + +	kfree(spi_data); + +	return ret; +} + +static int ad7879_spi_multi_read(struct device *dev, +				 u8 first_reg, u8 count, u16 *buf) +{ +	struct spi_device *spi = to_spi_device(dev); + +	return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf); +} + +static int ad7879_spi_read(struct device *dev, u8 reg) +{ +	struct spi_device *spi = to_spi_device(dev); +	u16 ret, dummy; + +	return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret; +} + +static int ad7879_spi_write(struct device *dev, u8 reg, u16 val) +{ +	struct spi_device *spi = to_spi_device(dev); +	u16 dummy; + +	return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy); +} + +static const struct ad7879_bus_ops ad7879_spi_bus_ops = { +	.bustype	= BUS_SPI, +	.read		= ad7879_spi_read, +	.multi_read	= ad7879_spi_multi_read, +	.write		= ad7879_spi_write, +}; + +static int __devinit ad7879_spi_probe(struct spi_device *spi) +{ +	struct ad7879 *ts; +	int err; + +	/* don't exceed max specified SPI CLK frequency */ +	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { +		dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); +		return -EINVAL; +	} + +	spi->bits_per_word = 16; +	err = spi_setup(spi); +	if (err) { +	        dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n"); +	        return err; +	} + +	ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops); +	if (IS_ERR(ts)) +		return PTR_ERR(ts); + +	spi_set_drvdata(spi, ts); + +	return 0; +} + +static int __devexit ad7879_spi_remove(struct spi_device *spi) +{ +	struct ad7879 *ts = spi_get_drvdata(spi); + +	ad7879_remove(ts); +	spi_set_drvdata(spi, NULL); + +	return 0; +} + +static struct spi_driver ad7879_spi_driver = { +	.driver = { +		.name	= "ad7879", +		.bus	= &spi_bus_type, +		.owner	= THIS_MODULE, +	}, +	.probe		= ad7879_spi_probe, +	.remove		= __devexit_p(ad7879_spi_remove), +	.suspend	= ad7879_spi_suspend, +	.resume		= ad7879_spi_resume, +}; + +static int __init ad7879_spi_init(void) +{ +	return spi_register_driver(&ad7879_spi_driver); +} +module_init(ad7879_spi_init); + +static void __exit ad7879_spi_exit(void) +{ +	spi_unregister_driver(&ad7879_spi_driver); +} +module_exit(ad7879_spi_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 4b32fb4704c..ba6f0bd1e76 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -1,25 +1,9 @@  /* - * Copyright (C) 2008-2009 Michael Hennerich, Analog Devices Inc. + * AD7879/AD7889 based touchscreen and GPIO driver   * - * Description:	AD7879/AD7889 based touchscreen, and GPIO driver - *		(I2C/SPI Interface) + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.   * - * Bugs:        Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * Licensed under the GPL-2 or later.   *   * History:   * Copyright (c) 2005 David Brownell @@ -44,12 +28,12 @@  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/slab.h> -#include <linux/workqueue.h>  #include <linux/spi/spi.h>  #include <linux/i2c.h>  #include <linux/gpio.h>  #include <linux/spi/ad7879.h> +#include "ad7879.h"  #define AD7879_REG_ZEROS		0  #define AD7879_REG_CTRL1		1 @@ -120,30 +104,19 @@ enum {  #define	MAX_12BIT			((1<<12)-1)  #define	TS_PEN_UP_TIMEOUT		msecs_to_jiffies(50) -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -#define AD7879_DEVID		0x7A -typedef struct spi_device	bus_device; -#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) -#define AD7879_DEVID		0x79 -typedef struct i2c_client	bus_device; -#endif -  struct ad7879 { -	bus_device		*bus; +	const struct ad7879_bus_ops *bops; + +	struct device		*dev;  	struct input_dev	*input; -	struct work_struct	work;  	struct timer_list	timer;  #ifdef CONFIG_GPIOLIB  	struct gpio_chip	gc; -#endif  	struct mutex		mutex; -	unsigned		disabled:1;	/* P: mutex */ - -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -	struct spi_message	msg; -	struct spi_transfer	xfer[AD7879_NR_SENSE + 1]; -	u16			cmd;  #endif +	unsigned int		irq; +	bool			disabled;	/* P: input->mutex */ +	bool			suspended;	/* P: input->mutex */  	u16			conversion_data[AD7879_NR_SENSE];  	char			phys[32];  	u8			first_conversion_delay; @@ -158,11 +131,22 @@ struct ad7879 {  	u16			cmd_crtl3;  }; -static int ad7879_read(bus_device *, u8); -static int ad7879_write(bus_device *, u8, u16); -static void ad7879_collect(struct ad7879 *); +static int ad7879_read(struct ad7879 *ts, u8 reg) +{ +	return ts->bops->read(ts->dev, reg); +} + +static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) +{ +	return ts->bops->multi_read(ts->dev, first_reg, count, buf); +} + +static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) +{ +	return ts->bops->write(ts->dev, reg, val); +} -static void ad7879_report(struct ad7879 *ts) +static int ad7879_report(struct ad7879 *ts)  {  	struct input_dev *input_dev = ts->input;  	unsigned Rt; @@ -175,12 +159,14 @@ static void ad7879_report(struct ad7879 *ts)  	/*  	 * The samples processed here are already preprocessed by the AD7879. -	 * The preprocessing function consists of a median and an averaging filter. -	 * The combination of these two techniques provides a robust solution, -	 * discarding the spurious noise in the signal and keeping only the data of interest. -	 * The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h) -	 * Other user-programmable conversion controls include variable acquisition time, -	 * and first conversion delay. Up to 16 averages can be taken per conversion. +	 * The preprocessing function consists of a median and an averaging +	 * filter.  The combination of these two techniques provides a robust +	 * solution, discarding the spurious noise in the signal and keeping +	 * only the data of interest.  The size of both filters is +	 * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other +	 * user-programmable conversion controls include variable acquisition +	 * time, and first conversion delay. Up to 16 averages can be taken +	 * per conversion.  	 */  	if (likely(x && z1)) { @@ -189,21 +175,17 @@ static void ad7879_report(struct ad7879 *ts)  		Rt /= z1;  		Rt = (Rt + 2047) >> 12; +		if (!timer_pending(&ts->timer)) +			input_report_key(input_dev, BTN_TOUCH, 1); +  		input_report_abs(input_dev, ABS_X, x);  		input_report_abs(input_dev, ABS_Y, y);  		input_report_abs(input_dev, ABS_PRESSURE, Rt);  		input_sync(input_dev); +		return 0;  	} -} - -static void ad7879_work(struct work_struct *work) -{ -	struct ad7879 *ts = container_of(work, struct ad7879, work); -	/* use keventd context to read the result registers */ -	ad7879_collect(ts); -	ad7879_report(ts); -	mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); +	return -EINVAL;  }  static void ad7879_ts_event_release(struct ad7879 *ts) @@ -211,6 +193,7 @@ static void ad7879_ts_event_release(struct ad7879 *ts)  	struct input_dev *input_dev = ts->input;  	input_report_abs(input_dev, ABS_PRESSURE, 0); +	input_report_key(input_dev, BTN_TOUCH, 0);  	input_sync(input_dev);  } @@ -225,56 +208,98 @@ static irqreturn_t ad7879_irq(int irq, void *handle)  {  	struct ad7879 *ts = handle; -	/* The repeated conversion sequencer controlled by TMR kicked off too fast. -	 * We ignore the last and process the sample sequence currently in the queue. -	 * It can't be older than 9.4ms -	 */ +	ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); -	if (!work_pending(&ts->work)) -		schedule_work(&ts->work); +	if (!ad7879_report(ts)) +		mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);  	return IRQ_HANDLED;  } -static void ad7879_setup(struct ad7879 *ts) +static void __ad7879_enable(struct ad7879 *ts)  { -	ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); -	ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3); -	ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1); +	ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); +	ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); +	ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); + +	enable_irq(ts->irq);  } -static void ad7879_disable(struct ad7879 *ts) +static void __ad7879_disable(struct ad7879 *ts)  { -	mutex_lock(&ts->mutex); +	disable_irq(ts->irq); -	if (!ts->disabled) { +	if (del_timer_sync(&ts->timer)) +		ad7879_ts_event_release(ts); -		ts->disabled = 1; -		disable_irq(ts->bus->irq); +	ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN)); +} -		cancel_work_sync(&ts->work); -		if (del_timer_sync(&ts->timer)) -			ad7879_ts_event_release(ts); +static int ad7879_open(struct input_dev *input) +{ +	struct ad7879 *ts = input_get_drvdata(input); -		ad7879_write(ts->bus, AD7879_REG_CTRL2, -			     AD7879_PM(AD7879_PM_SHUTDOWN)); -	} +	/* protected by input->mutex */ +	if (!ts->disabled && !ts->suspended) +		__ad7879_enable(ts); -	mutex_unlock(&ts->mutex); +	return 0;  } -static void ad7879_enable(struct ad7879 *ts) +static void ad7879_close(struct input_dev* input)  { -	mutex_lock(&ts->mutex); +	struct ad7879 *ts = input_get_drvdata(input); + +	/* protected by input->mutex */ +	if (!ts->disabled && !ts->suspended) +		__ad7879_disable(ts); +} + +void ad7879_suspend(struct ad7879 *ts) +{ +	mutex_lock(&ts->input->mutex); + +	if (!ts->suspended && !ts->disabled && ts->input->users) +		__ad7879_disable(ts); + +	ts->suspended = true; + +	mutex_unlock(&ts->input->mutex); +} +EXPORT_SYMBOL(ad7879_suspend); + +void ad7879_resume(struct ad7879 *ts) +{ +	mutex_lock(&ts->input->mutex); -	if (ts->disabled) { -		ad7879_setup(ts); -		ts->disabled = 0; -		enable_irq(ts->bus->irq); +	if (ts->suspended && !ts->disabled && ts->input->users) +		__ad7879_enable(ts); + +	ts->suspended = false; + +	mutex_unlock(&ts->input->mutex); +} +EXPORT_SYMBOL(ad7879_resume); + +static void ad7879_toggle(struct ad7879 *ts, bool disable) +{ +	mutex_lock(&ts->input->mutex); + +	if (!ts->suspended && ts->input->users != 0) { + +		if (disable) { +			if (ts->disabled) +				__ad7879_enable(ts); +		} else { +			if (!ts->disabled) +				__ad7879_disable(ts); +		}  	} -	mutex_unlock(&ts->mutex); +	ts->disabled = disable; + +	mutex_unlock(&ts->input->mutex);  }  static ssize_t ad7879_disable_show(struct device *dev, @@ -297,10 +322,7 @@ static ssize_t ad7879_disable_store(struct device *dev,  	if (error)  		return error; -	if (val) -		ad7879_disable(ts); -	else -		ad7879_enable(ts); +	ad7879_toggle(ts, val);  	return count;  } @@ -325,7 +347,7 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip,  	mutex_lock(&ts->mutex);  	ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; -	err = ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); +	err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);  	mutex_unlock(&ts->mutex);  	return err; @@ -345,7 +367,7 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip,  	else  		ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; -	err = ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); +	err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);  	mutex_unlock(&ts->mutex);  	return err; @@ -357,7 +379,7 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio)  	u16 val;  	mutex_lock(&ts->mutex); -	val = ad7879_read(ts->bus, AD7879_REG_CTRL2); +	val = ad7879_read(ts, AD7879_REG_CTRL2);  	mutex_unlock(&ts->mutex);  	return !!(val & AD7879_GPIO_DATA); @@ -374,16 +396,17 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip,  	else  		ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; -	ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); +	ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);  	mutex_unlock(&ts->mutex);  } -static int __devinit ad7879_gpio_add(struct device *dev) +static int ad7879_gpio_add(struct ad7879 *ts, +			   const struct ad7879_platform_data *pdata)  { -	struct ad7879 *ts = dev_get_drvdata(dev); -	struct ad7879_platform_data *pdata = dev->platform_data;  	int ret = 0; +	mutex_init(&ts->mutex); +  	if (pdata->gpio_export) {  		ts->gc.direction_input = ad7879_gpio_direction_input;  		ts->gc.direction_output = ad7879_gpio_direction_output; @@ -394,72 +417,75 @@ static int __devinit ad7879_gpio_add(struct device *dev)  		ts->gc.ngpio = 1;  		ts->gc.label = "AD7879-GPIO";  		ts->gc.owner = THIS_MODULE; -		ts->gc.dev = dev; +		ts->gc.dev = ts->dev;  		ret = gpiochip_add(&ts->gc);  		if (ret) -			dev_err(dev, "failed to register gpio %d\n", +			dev_err(ts->dev, "failed to register gpio %d\n",  				ts->gc.base);  	}  	return ret;  } -/* - * We mark ad7879_gpio_remove inline so there is a chance the code - * gets discarded when not needed. We can't do __devinit/__devexit - * markup since it is used in both probe and remove methods. - */ -static inline void ad7879_gpio_remove(struct device *dev) +static void ad7879_gpio_remove(struct ad7879 *ts)  { -	struct ad7879 *ts = dev_get_drvdata(dev); -	struct ad7879_platform_data *pdata = dev->platform_data; +	const struct ad7879_platform_data *pdata = ts->dev->platform_data;  	int ret;  	if (pdata->gpio_export) {  		ret = gpiochip_remove(&ts->gc);  		if (ret) -			dev_err(dev, "failed to remove gpio %d\n", +			dev_err(ts->dev, "failed to remove gpio %d\n",  				ts->gc.base);  	}  }  #else -static inline int ad7879_gpio_add(struct device *dev) +static inline int ad7879_gpio_add(struct ad7879 *ts, +				  const struct ad7879_platform_data *pdata)  {  	return 0;  } -static inline void ad7879_gpio_remove(struct device *dev) +static inline void ad7879_gpio_remove(struct ad7879 *ts)  {  }  #endif -static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, +			    const struct ad7879_bus_ops *bops)  { +	struct ad7879_platform_data *pdata = dev->platform_data; +	struct ad7879 *ts;  	struct input_dev *input_dev; -	struct ad7879_platform_data *pdata = bus->dev.platform_data;  	int err;  	u16 revid; -	if (!bus->irq) { -		dev_err(&bus->dev, "no IRQ?\n"); -		return -ENODEV; +	if (!irq) { +		dev_err(dev, "no IRQ?\n"); +		err = -EINVAL; +		goto err_out;  	}  	if (!pdata) { -		dev_err(&bus->dev, "no platform data?\n"); -		return -ENODEV; +		dev_err(dev, "no platform data?\n"); +		err = -EINVAL; +		goto err_out;  	} +	ts = kzalloc(sizeof(*ts), GFP_KERNEL);  	input_dev = input_allocate_device(); -	if (!input_dev) -		return -ENOMEM; +	if (!ts || !input_dev) { +		err = -ENOMEM; +		goto err_free_mem; +	} +	ts->bops = bops; +	ts->dev = dev;  	ts->input = input_dev; +	ts->irq = irq;  	setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); -	INIT_WORK(&ts->work, ad7879_work); -	mutex_init(&ts->mutex);  	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;  	ts->pressure_max = pdata->pressure_max ? : ~0; @@ -470,17 +496,26 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)  	ts->pen_down_acc_interval = pdata->pen_down_acc_interval;  	ts->median = pdata->median; -	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev)); +	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));  	input_dev->name = "AD7879 Touchscreen";  	input_dev->phys = ts->phys; -	input_dev->dev.parent = &bus->dev; +	input_dev->dev.parent = dev; +	input_dev->id.bustype = bops->bustype; + +	input_dev->open = ad7879_open; +	input_dev->close = ad7879_close; + +	input_set_drvdata(input_dev, ts);  	__set_bit(EV_ABS, input_dev->evbit);  	__set_bit(ABS_X, input_dev->absbit);  	__set_bit(ABS_Y, input_dev->absbit);  	__set_bit(ABS_PRESSURE, input_dev->absbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); +  	input_set_abs_params(input_dev, ABS_X,  			pdata->x_min ? : 0,  			pdata->x_max ? : MAX_12BIT, @@ -492,17 +527,18 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)  	input_set_abs_params(input_dev, ABS_PRESSURE,  			pdata->pressure_min, pdata->pressure_max, 0, 0); -	err = ad7879_write(bus, AD7879_REG_CTRL2, AD7879_RESET); - +	err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);  	if (err < 0) { -		dev_err(&bus->dev, "Failed to write %s\n", input_dev->name); +		dev_err(dev, "Failed to write %s\n", input_dev->name);  		goto err_free_mem;  	} -	revid = ad7879_read(bus, AD7879_REG_REVID); - -	if ((revid & 0xFF) != AD7879_DEVID) { -		dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name); +	revid = ad7879_read(ts, AD7879_REG_REVID); +	input_dev->id.product = (revid & 0xff); +	input_dev->id.version = revid >> 8; +	if (input_dev->id.product != devid) { +		dev_err(dev, "Failed to probe %s (%x vs %x)\n", +			input_dev->name, devid, revid);  		err = -ENODEV;  		goto err_free_mem;  	} @@ -524,21 +560,21 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)  			AD7879_ACQ(ts->acquisition_time) |  			AD7879_TMR(ts->pen_down_acc_interval); -	ad7879_setup(ts); - -	err = request_irq(bus->irq, ad7879_irq, -			  IRQF_TRIGGER_FALLING, bus->dev.driver->name, ts); - +	err = request_threaded_irq(ts->irq, NULL, ad7879_irq, +				   IRQF_TRIGGER_FALLING, +				   dev_name(dev), ts);  	if (err) { -		dev_err(&bus->dev, "irq %d busy?\n", bus->irq); +		dev_err(dev, "irq %d busy?\n", ts->irq);  		goto err_free_mem;  	} -	err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group); +	__ad7879_disable(ts); + +	err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);  	if (err)  		goto err_free_irq; -	err = ad7879_gpio_add(&bus->dev); +	err = ad7879_gpio_add(ts, pdata);  	if (err)  		goto err_remove_attr; @@ -546,321 +582,32 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)  	if (err)  		goto err_remove_gpio; -	dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n", -		 revid >> 8, bus->irq); - -	return 0; +	return ts;  err_remove_gpio: -	ad7879_gpio_remove(&bus->dev); +	ad7879_gpio_remove(ts);  err_remove_attr: -	sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group); +	sysfs_remove_group(&dev->kobj, &ad7879_attr_group);  err_free_irq: -	free_irq(bus->irq, ts); +	free_irq(ts->irq, ts);  err_free_mem:  	input_free_device(input_dev); - -	return err; -} - -static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts) -{ -	ad7879_gpio_remove(&bus->dev); -	ad7879_disable(ts); -	sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group); -	free_irq(ts->bus->irq, ts); -	input_unregister_device(ts->input); -	dev_dbg(&bus->dev, "unregistered touchscreen\n"); - -	return 0; -} - -#ifdef CONFIG_PM -static int ad7879_suspend(bus_device *bus, pm_message_t message) -{ -	struct ad7879 *ts = dev_get_drvdata(&bus->dev); - -	ad7879_disable(ts); - -	return 0; -} - -static int ad7879_resume(bus_device *bus) -{ -	struct ad7879 *ts = dev_get_drvdata(&bus->dev); - -	ad7879_enable(ts); - -	return 0; -} -#else -#define ad7879_suspend NULL -#define ad7879_resume  NULL -#endif - -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -#define MAX_SPI_FREQ_HZ		5000000 -#define AD7879_CMD_MAGIC	0xE000 -#define AD7879_CMD_READ		(1 << 10) -#define AD7879_WRITECMD(reg)	(AD7879_CMD_MAGIC | (reg & 0xF)) -#define AD7879_READCMD(reg)	(AD7879_CMD_MAGIC | AD7879_CMD_READ | (reg & 0xF)) - -struct ser_req { -	u16			command; -	u16			data; -	struct spi_message	msg; -	struct spi_transfer	xfer[2]; -}; - -/* - * ad7879_read/write are only used for initial setup and for sysfs controls. - * The main traffic is done in ad7879_collect(). - */ - -static int ad7879_read(struct spi_device *spi, u8 reg) -{ -	struct ser_req *req; -	int status, ret; - -	req = kzalloc(sizeof *req, GFP_KERNEL); -	if (!req) -		return -ENOMEM; - -	spi_message_init(&req->msg); - -	req->command = (u16) AD7879_READCMD(reg); -	req->xfer[0].tx_buf = &req->command; -	req->xfer[0].len = 2; - -	req->xfer[1].rx_buf = &req->data; -	req->xfer[1].len = 2; - -	spi_message_add_tail(&req->xfer[0], &req->msg); -	spi_message_add_tail(&req->xfer[1], &req->msg); - -	status = spi_sync(spi, &req->msg); -	ret = status ? : req->data; - -	kfree(req); - -	return ret; -} - -static int ad7879_write(struct spi_device *spi, u8 reg, u16 val) -{ -	struct ser_req *req; -	int status; - -	req = kzalloc(sizeof *req, GFP_KERNEL); -	if (!req) -		return -ENOMEM; - -	spi_message_init(&req->msg); - -	req->command = (u16) AD7879_WRITECMD(reg); -	req->xfer[0].tx_buf = &req->command; -	req->xfer[0].len = 2; - -	req->data = val; -	req->xfer[1].tx_buf = &req->data; -	req->xfer[1].len = 2; - -	spi_message_add_tail(&req->xfer[0], &req->msg); -	spi_message_add_tail(&req->xfer[1], &req->msg); - -	status = spi_sync(spi, &req->msg); - -	kfree(req); - -	return status; -} - -static void ad7879_collect(struct ad7879 *ts) -{ -	int status = spi_sync(ts->bus, &ts->msg); - -	if (status) -		dev_err(&ts->bus->dev, "spi_sync --> %d\n", status); -} - -static void ad7879_setup_ts_def_msg(struct ad7879 *ts) -{ -	struct spi_message *m; -	int i; - -	ts->cmd = (u16) AD7879_READCMD(AD7879_REG_XPLUS); - -	m = &ts->msg; -	spi_message_init(m); -	ts->xfer[0].tx_buf = &ts->cmd; -	ts->xfer[0].len = 2; - -	spi_message_add_tail(&ts->xfer[0], m); - -	for (i = 0; i < AD7879_NR_SENSE; i++) { -		ts->xfer[i + 1].rx_buf = &ts->conversion_data[i]; -		ts->xfer[i + 1].len = 2; -		spi_message_add_tail(&ts->xfer[i + 1], m); -	} -} - -static int __devinit ad7879_probe(struct spi_device *spi) -{ -	struct ad7879 *ts; -	int error; - -	/* don't exceed max specified SPI CLK frequency */ -	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { -		dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); -		return -EINVAL; -	} - -	ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); -	if (!ts) -		return -ENOMEM; - -	dev_set_drvdata(&spi->dev, ts); -	ts->bus = spi; - -	ad7879_setup_ts_def_msg(ts); - -	error = ad7879_construct(spi, ts); -	if (error) { -		dev_set_drvdata(&spi->dev, NULL); -		kfree(ts); -	} - -	return error; -} - -static int __devexit ad7879_remove(struct spi_device *spi) -{ -	struct ad7879 *ts = dev_get_drvdata(&spi->dev); - -	ad7879_destroy(spi, ts); -	dev_set_drvdata(&spi->dev, NULL);  	kfree(ts); - -	return 0; -} - -static struct spi_driver ad7879_driver = { -	.driver = { -		.name	= "ad7879", -		.bus	= &spi_bus_type, -		.owner	= THIS_MODULE, -	}, -	.probe		= ad7879_probe, -	.remove		= __devexit_p(ad7879_remove), -	.suspend	= ad7879_suspend, -	.resume		= ad7879_resume, -}; - -static int __init ad7879_init(void) -{ -	return spi_register_driver(&ad7879_driver); -} -module_init(ad7879_init); - -static void __exit ad7879_exit(void) -{ -	spi_unregister_driver(&ad7879_driver); -} -module_exit(ad7879_exit); - -#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) - -/* All registers are word-sized. - * AD7879 uses a high-byte first convention. - */ -static int ad7879_read(struct i2c_client *client, u8 reg) -{ -	return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static int ad7879_write(struct i2c_client *client, u8 reg, u16 val) -{ -	return i2c_smbus_write_word_data(client, reg, swab16(val)); -} - -static void ad7879_collect(struct ad7879 *ts) -{ -	int i; - -	for (i = 0; i < AD7879_NR_SENSE; i++) -		ts->conversion_data[i] = ad7879_read(ts->bus, -						     AD7879_REG_XPLUS + i); -} - -static int __devinit ad7879_probe(struct i2c_client *client, -					const struct i2c_device_id *id) -{ -	struct ad7879 *ts; -	int error; - -	if (!i2c_check_functionality(client->adapter, -					I2C_FUNC_SMBUS_WORD_DATA)) { -		dev_err(&client->dev, "SMBUS Word Data not Supported\n"); -		return -EIO; -	} - -	ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); -	if (!ts) -		return -ENOMEM; - -	i2c_set_clientdata(client, ts); -	ts->bus = client; - -	error = ad7879_construct(client, ts); -	if (error) -		kfree(ts); - -	return error; +err_out: +	return ERR_PTR(err);  } +EXPORT_SYMBOL(ad7879_probe); -static int __devexit ad7879_remove(struct i2c_client *client) +void ad7879_remove(struct ad7879 *ts)  { -	struct ad7879 *ts = dev_get_drvdata(&client->dev); - -	ad7879_destroy(client, ts); +	ad7879_gpio_remove(ts); +	sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); +	free_irq(ts->irq, ts); +	input_unregister_device(ts->input);  	kfree(ts); - -	return 0; -} - -static const struct i2c_device_id ad7879_id[] = { -	{ "ad7879", 0 }, -	{ "ad7889", 0 }, -	{ } -}; -MODULE_DEVICE_TABLE(i2c, ad7879_id); - -static struct i2c_driver ad7879_driver = { -	.driver = { -		.name	= "ad7879", -		.owner	= THIS_MODULE, -	}, -	.probe		= ad7879_probe, -	.remove		= __devexit_p(ad7879_remove), -	.suspend	= ad7879_suspend, -	.resume		= ad7879_resume, -	.id_table	= ad7879_id, -}; - -static int __init ad7879_init(void) -{ -	return i2c_add_driver(&ad7879_driver); -} -module_init(ad7879_init); - -static void __exit ad7879_exit(void) -{ -	i2c_del_driver(&ad7879_driver);  } -module_exit(ad7879_exit); -#endif +EXPORT_SYMBOL(ad7879_remove);  MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");  MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");  MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h new file mode 100644 index 00000000000..6b45a27236c --- /dev/null +++ b/drivers/input/touchscreen/ad7879.h @@ -0,0 +1,30 @@ +/* + * AD7879/AD7889 touchscreen (bus interfaces) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _AD7879_H_ +#define _AD7879_H_ + +#include <linux/types.h> + +struct ad7879; +struct device; + +struct ad7879_bus_ops { +	u16 bustype; +	int (*read)(struct device *dev, u8 reg); +	int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf); +	int (*write)(struct device *dev, u8 reg, u16 val); +}; + +void ad7879_suspend(struct ad7879 *); +void ad7879_resume(struct ad7879 *); +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq, +			    const struct ad7879_bus_ops *bops); +void ad7879_remove(struct ad7879 *); + +#endif diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index a9fdf55c023..16031933a8f 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -68,6 +68,8 @@ struct ts_event {  	u16	y;  	u16	z1, z2;  	int	ignore; +	u8	x_buf[3]; +	u8	y_buf[3];  };  /* @@ -79,6 +81,8 @@ struct ads7846_packet {  	u8			read_x, read_y, read_z1, read_z2, pwrdown;  	u16			dummy;		/* for the pwrdown read */  	struct ts_event		tc; +	/* for ads7845 with mpc5121 psc spi we use 3-byte buffers */ +	u8			read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3];  };  struct ads7846 { @@ -207,6 +211,14 @@ struct ser_req {  	struct spi_transfer	xfer[6];  }; +struct ads7845_ser_req { +	u8			command[3]; +	u8			pwrdown[3]; +	u8			sample[3]; +	struct spi_message	msg; +	struct spi_transfer	xfer[2]; +}; +  static void ads7846_enable(struct ads7846 *ts);  static void ads7846_disable(struct ads7846 *ts); @@ -287,6 +299,41 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  	return status;  } +static int ads7845_read12_ser(struct device *dev, unsigned command) +{ +	struct spi_device	*spi = to_spi_device(dev); +	struct ads7846		*ts = dev_get_drvdata(dev); +	struct ads7845_ser_req	*req = kzalloc(sizeof *req, GFP_KERNEL); +	int			status; + +	if (!req) +		return -ENOMEM; + +	spi_message_init(&req->msg); + +	req->command[0] = (u8) command; +	req->xfer[0].tx_buf = req->command; +	req->xfer[0].rx_buf = req->sample; +	req->xfer[0].len = 3; +	spi_message_add_tail(&req->xfer[0], &req->msg); + +	ts->irq_disabled = 1; +	disable_irq(spi->irq); +	status = spi_sync(spi, &req->msg); +	ts->irq_disabled = 0; +	enable_irq(spi->irq); + +	if (status == 0) { +		/* BE12 value, then padding */ +		status = be16_to_cpu(*((u16 *)&req->sample[1])); +		status = status >> 3; +		status &= 0x0fff; +	} + +	kfree(req); +	return status; +} +  #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)  #define SHOW(name, var, adjust) static ssize_t \ @@ -540,10 +587,17 @@ static void ads7846_rx(void *ads)  	/* ads7846_rx_val() did in-place conversion (including byteswap) from  	 * on-the-wire format as part of debouncing to get stable readings.  	 */ -	x = packet->tc.x; -	y = packet->tc.y; -	z1 = packet->tc.z1; -	z2 = packet->tc.z2; +	if (ts->model == 7845) { +		x = *(u16 *)packet->tc.x_buf; +		y = *(u16 *)packet->tc.y_buf; +		z1 = 0; +		z2 = 0; +	} else { +		x = packet->tc.x; +		y = packet->tc.y; +		z1 = packet->tc.z1; +		z2 = packet->tc.z2; +	}  	/* range filtering */  	if (x == MAX_12BIT) @@ -551,6 +605,12 @@ static void ads7846_rx(void *ads)  	if (ts->model == 7843) {  		Rt = ts->pressure_max / 2; +	} else if (ts->model == 7845) { +		if (get_pendown_state(ts)) +			Rt = ts->pressure_max / 2; +		else +			Rt = 0; +		dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt);  	} else if (likely(x && z1)) {  		/* compute touch pressure resistance using equation #2 */  		Rt = z2; @@ -671,10 +731,14 @@ static void ads7846_rx_val(void *ads)  	m = &ts->msg[ts->msg_idx];  	t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); -	/* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding; -	 * built from two 8 bit values written msb-first. -	 */ -	val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; +	if (ts->model == 7845) { +		val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; +	} else { +		/* adjust:  on-wire is a must-ignore bit, a BE12 value, then +		 * padding; built from two 8 bit values written msb-first. +		 */ +		val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; +	}  	action = ts->filter(ts->filter_data, ts->msg_idx, &val);  	switch (action) { @@ -878,14 +942,15 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)  static int __devinit ads7846_probe(struct spi_device *spi)  { -	struct ads7846			*ts; -	struct ads7846_packet		*packet; -	struct input_dev		*input_dev; -	struct ads7846_platform_data	*pdata = spi->dev.platform_data; -	struct spi_message		*m; -	struct spi_transfer		*x; -	int				vref; -	int				err; +	struct ads7846 *ts; +	struct ads7846_packet *packet; +	struct input_dev *input_dev; +	const struct ads7846_platform_data *pdata = spi->dev.platform_data; +	struct spi_message *m; +	struct spi_transfer *x; +	unsigned long irq_flags; +	int vref; +	int err;  	if (!spi->irq) {  		dev_dbg(&spi->dev, "no IRQ?\n"); @@ -1008,16 +1073,26 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	spi_message_init(m); -	/* y- still on; turn on only y+ (and ADC) */ -	packet->read_y = READ_Y(vref); -	x->tx_buf = &packet->read_y; -	x->len = 1; -	spi_message_add_tail(x, m); +	if (ts->model == 7845) { +		packet->read_y_cmd[0] = READ_Y(vref); +		packet->read_y_cmd[1] = 0; +		packet->read_y_cmd[2] = 0; +		x->tx_buf = &packet->read_y_cmd[0]; +		x->rx_buf = &packet->tc.y_buf[0]; +		x->len = 3; +		spi_message_add_tail(x, m); +	} else { +		/* y- still on; turn on only y+ (and ADC) */ +		packet->read_y = READ_Y(vref); +		x->tx_buf = &packet->read_y; +		x->len = 1; +		spi_message_add_tail(x, m); -	x++; -	x->rx_buf = &packet->tc.y; -	x->len = 2; -	spi_message_add_tail(x, m); +		x++; +		x->rx_buf = &packet->tc.y; +		x->len = 2; +		spi_message_add_tail(x, m); +	}  	/* the first sample after switching drivers can be low quality;  	 * optionally discard it, using a second one after the signals @@ -1043,17 +1118,28 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	m++;  	spi_message_init(m); -	/* turn y- off, x+ on, then leave in lowpower */ -	x++; -	packet->read_x = READ_X(vref); -	x->tx_buf = &packet->read_x; -	x->len = 1; -	spi_message_add_tail(x, m); +	if (ts->model == 7845) { +		x++; +		packet->read_x_cmd[0] = READ_X(vref); +		packet->read_x_cmd[1] = 0; +		packet->read_x_cmd[2] = 0; +		x->tx_buf = &packet->read_x_cmd[0]; +		x->rx_buf = &packet->tc.x_buf[0]; +		x->len = 3; +		spi_message_add_tail(x, m); +	} else { +		/* turn y- off, x+ on, then leave in lowpower */ +		x++; +		packet->read_x = READ_X(vref); +		x->tx_buf = &packet->read_x; +		x->len = 1; +		spi_message_add_tail(x, m); -	x++; -	x->rx_buf = &packet->tc.x; -	x->len = 2; -	spi_message_add_tail(x, m); +		x++; +		x->rx_buf = &packet->tc.x; +		x->len = 2; +		spi_message_add_tail(x, m); +	}  	/* ... maybe discard first sample ... */  	if (pdata->settle_delay_usecs) { @@ -1144,15 +1230,25 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	m++;  	spi_message_init(m); -	x++; -	packet->pwrdown = PWRDOWN; -	x->tx_buf = &packet->pwrdown; -	x->len = 1; -	spi_message_add_tail(x, m); +	if (ts->model == 7845) { +		x++; +		packet->pwrdown_cmd[0] = PWRDOWN; +		packet->pwrdown_cmd[1] = 0; +		packet->pwrdown_cmd[2] = 0; +		x->tx_buf = &packet->pwrdown_cmd[0]; +		x->len = 3; +	} else { +		x++; +		packet->pwrdown = PWRDOWN; +		x->tx_buf = &packet->pwrdown; +		x->len = 1; +		spi_message_add_tail(x, m); + +		x++; +		x->rx_buf = &packet->dummy; +		x->len = 2; +	} -	x++; -	x->rx_buf = &packet->dummy; -	x->len = 2;  	CS_CHANGE(*x);  	spi_message_add_tail(x, m); @@ -1174,17 +1270,22 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		goto err_put_regulator;  	} -	if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING, -			spi->dev.driver->name, ts)) { +	irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; + +	err = request_irq(spi->irq, ads7846_irq, irq_flags, +			  spi->dev.driver->name, ts); + +	if (err && !pdata->irq_flags) {  		dev_info(&spi->dev,  			"trying pin change workaround on irq %d\n", spi->irq);  		err = request_irq(spi->irq, ads7846_irq,  				  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,  				  spi->dev.driver->name, ts); -		if (err) { -			dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); -			goto err_disable_regulator; -		} +	} + +	if (err) { +		dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); +		goto err_disable_regulator;  	}  	err = ads784x_hwmon_register(spi, ts); @@ -1196,8 +1297,11 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	/* take a first sample, leaving nPENIRQ active and vREF off; avoid  	 * the touchscreen, in case it's not connected.  	 */ -	(void) ads7846_read12_ser(&spi->dev, -			  READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); +	if (ts->model == 7845) +		ads7845_read12_ser(&spi->dev, PWRDOWN); +	else +		(void) ads7846_read12_ser(&spi->dev, +				READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);  	err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);  	if (err) diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c new file mode 100644 index 00000000000..4eb7df0b7f8 --- /dev/null +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -0,0 +1,363 @@ +/* + * Driver for cypress touch screen controller + * + * Copyright (c) 2009 Aava Mobile + * + * Some cleanups by Alan Cox <alan@linux.intel.com> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/input/cy8ctmg110_pdata.h> + +#define CY8CTMG110_DRIVER_NAME      "cy8ctmg110" + +/* Touch coordinates */ +#define CY8CTMG110_X_MIN		0 +#define CY8CTMG110_Y_MIN		0 +#define CY8CTMG110_X_MAX		759 +#define CY8CTMG110_Y_MAX		465 + + +/* cy8ctmg110 register definitions */ +#define CY8CTMG110_TOUCH_WAKEUP_TIME	0 +#define CY8CTMG110_TOUCH_SLEEP_TIME	2 +#define CY8CTMG110_TOUCH_X1		3 +#define CY8CTMG110_TOUCH_Y1		5 +#define CY8CTMG110_TOUCH_X2		7 +#define CY8CTMG110_TOUCH_Y2		9 +#define CY8CTMG110_FINGERS		11 +#define CY8CTMG110_GESTURE		12 +#define CY8CTMG110_REG_MAX		13 + + +/* + * The touch driver structure. + */ +struct cy8ctmg110 { +	struct input_dev *input; +	char phys[32]; +	struct i2c_client *client; +	int reset_pin; +	int irq_pin; +}; + +/* + * cy8ctmg110_power is the routine that is called when touch hardware + * will powered off or on. + */ +static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron) +{ +	if (ts->reset_pin) +		gpio_direction_output(ts->reset_pin, 1 - poweron); +} + +static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg, +		unsigned char len, unsigned char *value) +{ +	struct i2c_client *client = tsc->client; +	unsigned int ret; +	unsigned char i2c_data[6]; + +	BUG_ON(len > 5); + +	i2c_data[0] = reg; +	memcpy(i2c_data + 1, value, len); + +	ret = i2c_master_send(client, i2c_data, len + 1); +	if (ret != 1) { +		dev_err(&client->dev, "i2c write data cmd failed\n"); +		return ret; +	} + +	return 0; +} + +static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc, +		unsigned char *data, unsigned char len, unsigned char cmd) +{ +	struct i2c_client *client = tsc->client; +	unsigned int ret; +	struct i2c_msg msg[2] = { +		/* first write slave position to i2c devices */ +		{ client->addr, 0, 1, &cmd }, +		/* Second read data from position */ +		{ client->addr, I2C_M_RD, len, data } +	}; + +	ret = i2c_transfer(client->adapter, msg, 2); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) +{ +	struct input_dev *input = tsc->input; +	unsigned char reg_p[CY8CTMG110_REG_MAX]; +	int x, y; + +	memset(reg_p, 0, CY8CTMG110_REG_MAX); + +	/* Reading coordinates */ +	if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0) +		return -EIO; + +	y = reg_p[2] << 8 | reg_p[3]; +	x = reg_p[0] << 8 | reg_p[1]; + +	/* Number of touch */ +	if (reg_p[8] == 0) { +		input_report_key(input, BTN_TOUCH, 0); +	} else  { +		input_report_key(input, BTN_TOUCH, 1); +		input_report_abs(input, ABS_X, x); +		input_report_abs(input, ABS_Y, y); +	} + +	input_sync(input); + +	return 0; +} + +static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep) +{ +	unsigned char reg_p[3]; + +	if (sleep) { +		reg_p[0] = 0x00; +		reg_p[1] = 0xff; +		reg_p[2] = 5; +	} else { +		reg_p[0] = 0x10; +		reg_p[1] = 0xff; +		reg_p[2] = 0; +	} + +	return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p); +} + +static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id) +{ +	struct cy8ctmg110 *tsc = dev_id; + +	cy8ctmg110_touch_pos(tsc); + +	return IRQ_HANDLED; +} + +static int __devinit cy8ctmg110_probe(struct i2c_client *client, +					const struct i2c_device_id *id) +{ +	const struct cy8ctmg110_pdata *pdata = client->dev.platform_data; +	struct cy8ctmg110 *ts; +	struct input_dev *input_dev; +	int err; + +	/* No pdata no way forward */ +	if (pdata == NULL) { +		dev_err(&client->dev, "no pdata\n"); +		return -ENODEV; +	} + +	if (!i2c_check_functionality(client->adapter, +					I2C_FUNC_SMBUS_READ_WORD_DATA)) +		return -EIO; + +	ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!ts || !input_dev) { +		err = -ENOMEM; +		goto err_free_mem; +	} + +	ts->client = client; +	ts->input = input_dev; + +	snprintf(ts->phys, sizeof(ts->phys), +		 "%s/input0", dev_name(&client->dev)); + +	input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen"; +	input_dev->phys = ts->phys; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; + +	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + +	input_set_abs_params(input_dev, ABS_X, +			CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, +			CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + +	if (ts->reset_pin) { +		err = gpio_request(ts->reset_pin, NULL); +		if (err) { +			dev_err(&client->dev, +				"Unable to request GPIO pin %d.\n", +				ts->reset_pin); +			goto err_free_mem; +		} +	} + +	cy8ctmg110_power(ts, true); +	cy8ctmg110_set_sleepmode(ts, false); + +	err = gpio_request(ts->irq_pin, "touch_irq_key"); +	if (err < 0) { +		dev_err(&client->dev, +			"Failed to request GPIO %d, error %d\n", +			ts->irq_pin, err); +		goto err_shutoff_device; +	} + +	err = gpio_direction_input(ts->irq_pin); +	if (err < 0) { +		dev_err(&client->dev, +			"Failed to configure input direction for GPIO %d, error %d\n", +			ts->irq_pin, err); +		goto err_free_irq_gpio; +	} + +	client->irq = gpio_to_irq(ts->irq_pin); +	if (client->irq < 0) { +		err = client->irq; +		dev_err(&client->dev, +			"Unable to get irq number for GPIO %d, error %d\n", +			ts->irq_pin, err); +		goto err_free_irq_gpio; +	} + +	err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, +				   IRQF_TRIGGER_RISING, "touch_reset_key", ts); +	if (err < 0) { +		dev_err(&client->dev, +			"irq %d busy? error %d\n", client->irq, err); +		goto err_free_irq_gpio; +	} + +	err = input_register_device(input_dev); +	if (err) +		goto err_free_irq; + +	i2c_set_clientdata(client, ts); +	device_init_wakeup(&client->dev, 1); +	return 0; + +err_free_irq: +	free_irq(client->irq, ts); +err_free_irq_gpio: +	gpio_free(ts->irq_pin); +err_shutoff_device: +	cy8ctmg110_set_sleepmode(ts, true); +	cy8ctmg110_power(ts, false); +	if (ts->reset_pin) +		gpio_free(ts->reset_pin); +err_free_mem: +	input_free_device(input_dev); +	kfree(ts); +	return err; +} + +#ifdef CONFIG_PM +static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +{ +	struct cy8ctmg110 *ts = i2c_get_clientdata(client); + +	if (device_may_wakeup(&client->dev)) +		enable_irq_wake(client->irq); +	else { +		cy8ctmg110_set_sleepmode(ts, true); +		cy8ctmg110_power(ts, false); +	} +	return 0; +} + +static int cy8ctmg110_resume(struct i2c_client *client) +{ +	struct cy8ctmg110 *ts = i2c_get_clientdata(client); + +	if (device_may_wakeup(&client->dev)) +		disable_irq_wake(client->irq); +	else { +		cy8ctmg110_power(ts, true); +		cy8ctmg110_set_sleepmode(ts, false); +	} +	return 0; +} +#endif + +static int __devexit cy8ctmg110_remove(struct i2c_client *client) +{ +	struct cy8ctmg110 *ts = i2c_get_clientdata(client); + +	cy8ctmg110_set_sleepmode(ts, true); +	cy8ctmg110_power(ts, false); + +	free_irq(client->irq, ts); +	input_unregister_device(ts->input); +	gpio_free(ts->irq_pin); +	if (ts->reset_pin) +		gpio_free(ts->reset_pin); +	kfree(ts); + +	return 0; +} + +static struct i2c_device_id cy8ctmg110_idtable[] = { +	{ CY8CTMG110_DRIVER_NAME, 1 }, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); + +static struct i2c_driver cy8ctmg110_driver = { +	.driver		= { +		.owner	= THIS_MODULE, +		.name	= CY8CTMG110_DRIVER_NAME, +	}, +	.id_table	= cy8ctmg110_idtable, +	.probe		= cy8ctmg110_probe, +	.remove		= __devexit_p(cy8ctmg110_remove), +#ifdef CONFIG_PM +	.suspend	= cy8ctmg110_suspend, +	.resume		= cy8ctmg110_resume, +#endif +}; + +static int __init cy8ctmg110_init(void) +{ +	return i2c_add_driver(&cy8ctmg110_driver); +} + +static void __exit cy8ctmg110_exit(void) +{ +	i2c_del_driver(&cy8ctmg110_driver); +} + +module_init(cy8ctmg110_init); +module_exit(cy8ctmg110_exit); + +MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>"); +MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 1fb0c2f06a4..6ee9940aaf5 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -16,7 +16,7 @@  #include <linux/module.h>  #include <linux/init.h>  #include <linux/i2c.h> -#include <linux/i2c/mcs5000_ts.h> +#include <linux/i2c/mcs.h>  #include <linux/interrupt.h>  #include <linux/input.h>  #include <linux/irq.h> @@ -105,7 +105,7 @@ enum mcs5000_ts_read_offset {  struct mcs5000_ts_data {  	struct i2c_client *client;  	struct input_dev *input_dev; -	const struct mcs5000_ts_platform_data *platform_data; +	const struct mcs_platform_data *platform_data;  };  static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) @@ -164,7 +164,7 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)  static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data)  { -	const struct mcs5000_ts_platform_data *platform_data = +	const struct mcs_platform_data *platform_data =  		data->platform_data;  	struct i2c_client *client = data->client; diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c new file mode 100644 index 00000000000..66b26ad3032 --- /dev/null +++ b/drivers/input/touchscreen/qt602240_ts.c @@ -0,0 +1,1401 @@ +/* + * AT42QT602240/ATMXT224 Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c/qt602240_ts.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +/* Version */ +#define QT602240_VER_20			20 +#define QT602240_VER_21			21 +#define QT602240_VER_22			22 + +/* Slave addresses */ +#define QT602240_APP_LOW		0x4a +#define QT602240_APP_HIGH		0x4b +#define QT602240_BOOT_LOW		0x24 +#define QT602240_BOOT_HIGH		0x25 + +/* Firmware */ +#define QT602240_FW_NAME		"qt602240.fw" + +/* Registers */ +#define QT602240_FAMILY_ID		0x00 +#define QT602240_VARIANT_ID		0x01 +#define QT602240_VERSION		0x02 +#define QT602240_BUILD			0x03 +#define QT602240_MATRIX_X_SIZE		0x04 +#define QT602240_MATRIX_Y_SIZE		0x05 +#define QT602240_OBJECT_NUM		0x06 +#define QT602240_OBJECT_START		0x07 + +#define QT602240_OBJECT_SIZE		6 + +/* Object types */ +#define QT602240_DEBUG_DIAGNOSTIC	37 +#define QT602240_GEN_MESSAGE		5 +#define QT602240_GEN_COMMAND		6 +#define QT602240_GEN_POWER		7 +#define QT602240_GEN_ACQUIRE		8 +#define QT602240_TOUCH_MULTI		9 +#define QT602240_TOUCH_KEYARRAY		15 +#define QT602240_TOUCH_PROXIMITY	23 +#define QT602240_PROCI_GRIPFACE		20 +#define QT602240_PROCG_NOISE		22 +#define QT602240_PROCI_ONETOUCH		24 +#define QT602240_PROCI_TWOTOUCH		27 +#define QT602240_SPT_COMMSCONFIG	18	/* firmware ver 21 over */ +#define QT602240_SPT_GPIOPWM		19 +#define QT602240_SPT_SELFTEST		25 +#define QT602240_SPT_CTECONFIG		28 +#define QT602240_SPT_USERDATA		38	/* firmware ver 21 over */ + +/* QT602240_GEN_COMMAND field */ +#define QT602240_COMMAND_RESET		0 +#define QT602240_COMMAND_BACKUPNV	1 +#define QT602240_COMMAND_CALIBRATE	2 +#define QT602240_COMMAND_REPORTALL	3 +#define QT602240_COMMAND_DIAGNOSTIC	5 + +/* QT602240_GEN_POWER field */ +#define QT602240_POWER_IDLEACQINT	0 +#define QT602240_POWER_ACTVACQINT	1 +#define QT602240_POWER_ACTV2IDLETO	2 + +/* QT602240_GEN_ACQUIRE field */ +#define QT602240_ACQUIRE_CHRGTIME	0 +#define QT602240_ACQUIRE_TCHDRIFT	2 +#define QT602240_ACQUIRE_DRIFTST	3 +#define QT602240_ACQUIRE_TCHAUTOCAL	4 +#define QT602240_ACQUIRE_SYNC		5 +#define QT602240_ACQUIRE_ATCHCALST	6 +#define QT602240_ACQUIRE_ATCHCALSTHR	7 + +/* QT602240_TOUCH_MULTI field */ +#define QT602240_TOUCH_CTRL		0 +#define QT602240_TOUCH_XORIGIN		1 +#define QT602240_TOUCH_YORIGIN		2 +#define QT602240_TOUCH_XSIZE		3 +#define QT602240_TOUCH_YSIZE		4 +#define QT602240_TOUCH_BLEN		6 +#define QT602240_TOUCH_TCHTHR		7 +#define QT602240_TOUCH_TCHDI		8 +#define QT602240_TOUCH_ORIENT		9 +#define QT602240_TOUCH_MOVHYSTI		11 +#define QT602240_TOUCH_MOVHYSTN		12 +#define QT602240_TOUCH_NUMTOUCH		14 +#define QT602240_TOUCH_MRGHYST		15 +#define QT602240_TOUCH_MRGTHR		16 +#define QT602240_TOUCH_AMPHYST		17 +#define QT602240_TOUCH_XRANGE_LSB	18 +#define QT602240_TOUCH_XRANGE_MSB	19 +#define QT602240_TOUCH_YRANGE_LSB	20 +#define QT602240_TOUCH_YRANGE_MSB	21 +#define QT602240_TOUCH_XLOCLIP		22 +#define QT602240_TOUCH_XHICLIP		23 +#define QT602240_TOUCH_YLOCLIP		24 +#define QT602240_TOUCH_YHICLIP		25 +#define QT602240_TOUCH_XEDGECTRL	26 +#define QT602240_TOUCH_XEDGEDIST	27 +#define QT602240_TOUCH_YEDGECTRL	28 +#define QT602240_TOUCH_YEDGEDIST	29 +#define QT602240_TOUCH_JUMPLIMIT	30	/* firmware ver 22 over */ + +/* QT602240_PROCI_GRIPFACE field */ +#define QT602240_GRIPFACE_CTRL		0 +#define QT602240_GRIPFACE_XLOGRIP	1 +#define QT602240_GRIPFACE_XHIGRIP	2 +#define QT602240_GRIPFACE_YLOGRIP	3 +#define QT602240_GRIPFACE_YHIGRIP	4 +#define QT602240_GRIPFACE_MAXTCHS	5 +#define QT602240_GRIPFACE_SZTHR1	7 +#define QT602240_GRIPFACE_SZTHR2	8 +#define QT602240_GRIPFACE_SHPTHR1	9 +#define QT602240_GRIPFACE_SHPTHR2	10 +#define QT602240_GRIPFACE_SUPEXTTO	11 + +/* QT602240_PROCI_NOISE field */ +#define QT602240_NOISE_CTRL		0 +#define QT602240_NOISE_OUTFLEN		1 +#define QT602240_NOISE_GCAFUL_LSB	3 +#define QT602240_NOISE_GCAFUL_MSB	4 +#define QT602240_NOISE_GCAFLL_LSB	5 +#define QT602240_NOISE_GCAFLL_MSB	6 +#define QT602240_NOISE_ACTVGCAFVALID	7 +#define QT602240_NOISE_NOISETHR		8 +#define QT602240_NOISE_FREQHOPSCALE	10 +#define QT602240_NOISE_FREQ0		11 +#define QT602240_NOISE_FREQ1		12 +#define QT602240_NOISE_FREQ2		13 +#define QT602240_NOISE_FREQ3		14 +#define QT602240_NOISE_FREQ4		15 +#define QT602240_NOISE_IDLEGCAFVALID	16 + +/* QT602240_SPT_COMMSCONFIG */ +#define QT602240_COMMS_CTRL		0 +#define QT602240_COMMS_CMD		1 + +/* QT602240_SPT_CTECONFIG field */ +#define QT602240_CTE_CTRL		0 +#define QT602240_CTE_CMD		1 +#define QT602240_CTE_MODE		2 +#define QT602240_CTE_IDLEGCAFDEPTH	3 +#define QT602240_CTE_ACTVGCAFDEPTH	4 +#define QT602240_CTE_VOLTAGE		5	/* firmware ver 21 over */ + +#define QT602240_VOLTAGE_DEFAULT	2700000 +#define QT602240_VOLTAGE_STEP		10000 + +/* Define for QT602240_GEN_COMMAND */ +#define QT602240_BOOT_VALUE		0xa5 +#define QT602240_BACKUP_VALUE		0x55 +#define QT602240_BACKUP_TIME		25	/* msec */ +#define QT602240_RESET_TIME		65	/* msec */ + +#define QT602240_FWRESET_TIME		175	/* msec */ + +/* Command to unlock bootloader */ +#define QT602240_UNLOCK_CMD_MSB		0xaa +#define QT602240_UNLOCK_CMD_LSB		0xdc + +/* Bootloader mode status */ +#define QT602240_WAITING_BOOTLOAD_CMD	0xc0	/* valid 7 6 bit only */ +#define QT602240_WAITING_FRAME_DATA	0x80	/* valid 7 6 bit only */ +#define QT602240_FRAME_CRC_CHECK	0x02 +#define QT602240_FRAME_CRC_FAIL		0x03 +#define QT602240_FRAME_CRC_PASS		0x04 +#define QT602240_APP_CRC_FAIL		0x40	/* valid 7 8 bit only */ +#define QT602240_BOOT_STATUS_MASK	0x3f + +/* Touch status */ +#define QT602240_SUPPRESS		(1 << 1) +#define QT602240_AMP			(1 << 2) +#define QT602240_VECTOR			(1 << 3) +#define QT602240_MOVE			(1 << 4) +#define QT602240_RELEASE		(1 << 5) +#define QT602240_PRESS			(1 << 6) +#define QT602240_DETECT			(1 << 7) + +/* Touchscreen absolute values */ +#define QT602240_MAX_XC			0x3ff +#define QT602240_MAX_YC			0x3ff +#define QT602240_MAX_AREA		0xff + +#define QT602240_MAX_FINGER		10 + +/* Initial register values recommended from chip vendor */ +static const u8 init_vals_ver_20[] = { +	/* QT602240_GEN_COMMAND(6) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_GEN_POWER(7) */ +	0x20, 0xff, 0x32, +	/* QT602240_GEN_ACQUIRE(8) */ +	0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, +	/* QT602240_TOUCH_MULTI(9) */ +	0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, +	0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, +	/* QT602240_TOUCH_KEYARRAY(15) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, +	/* QT602240_SPT_GPIOPWM(19) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, +	/* QT602240_PROCI_GRIPFACE(20) */ +	0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, +	0x1e, 0x00, +	/* QT602240_PROCG_NOISE(22) */ +	0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, +	0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, +	/* QT602240_TOUCH_PROXIMITY(23) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, +	/* QT602240_PROCI_ONETOUCH(24) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_SPT_SELFTEST(25) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, +	/* QT602240_PROCI_TWOTOUCH(27) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_SPT_CTECONFIG(28) */ +	0x00, 0x00, 0x00, 0x04, 0x08, +}; + +static const u8 init_vals_ver_21[] = { +	/* QT602240_GEN_COMMAND(6) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_GEN_POWER(7) */ +	0x20, 0xff, 0x32, +	/* QT602240_GEN_ACQUIRE(8) */ +	0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, +	/* QT602240_TOUCH_MULTI(9) */ +	0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, +	0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_TOUCH_KEYARRAY(15) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, +	/* QT602240_SPT_GPIOPWM(19) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_PROCI_GRIPFACE(20) */ +	0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, +	0x0f, 0x0a, +	/* QT602240_PROCG_NOISE(22) */ +	0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, +	0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, +	/* QT602240_TOUCH_PROXIMITY(23) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, +	/* QT602240_PROCI_ONETOUCH(24) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_SPT_SELFTEST(25) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, +	/* QT602240_PROCI_TWOTOUCH(27) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_SPT_CTECONFIG(28) */ +	0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +static const u8 init_vals_ver_22[] = { +	/* QT602240_GEN_COMMAND(6) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_GEN_POWER(7) */ +	0x20, 0xff, 0x32, +	/* QT602240_GEN_ACQUIRE(8) */ +	0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, +	/* QT602240_TOUCH_MULTI(9) */ +	0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, +	0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, +	/* QT602240_TOUCH_KEYARRAY(15) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, +	/* QT602240_SPT_GPIOPWM(19) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_PROCI_GRIPFACE(20) */ +	0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, +	0x0f, 0x0a, +	/* QT602240_PROCG_NOISE(22) */ +	0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, +	0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, +	/* QT602240_TOUCH_PROXIMITY(23) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_PROCI_ONETOUCH(24) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_SPT_SELFTEST(25) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, +	/* QT602240_PROCI_TWOTOUCH(27) */ +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	/* QT602240_SPT_CTECONFIG(28) */ +	0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +struct qt602240_info { +	u8 family_id; +	u8 variant_id; +	u8 version; +	u8 build; +	u8 matrix_xsize; +	u8 matrix_ysize; +	u8 object_num; +}; + +struct qt602240_object { +	u8 type; +	u16 start_address; +	u8 size; +	u8 instances; +	u8 num_report_ids; + +	/* to map object and message */ +	u8 max_reportid; +}; + +struct qt602240_message { +	u8 reportid; +	u8 message[7]; +	u8 checksum; +}; + +struct qt602240_finger { +	int status; +	int x; +	int y; +	int area; +}; + +/* Each client has this additional data */ +struct qt602240_data { +	struct i2c_client *client; +	struct input_dev *input_dev; +	const struct qt602240_platform_data *pdata; +	struct qt602240_object *object_table; +	struct qt602240_info info; +	struct qt602240_finger finger[QT602240_MAX_FINGER]; +	unsigned int irq; +}; + +static bool qt602240_object_readable(unsigned int type) +{ +	switch (type) { +	case QT602240_GEN_MESSAGE: +	case QT602240_GEN_COMMAND: +	case QT602240_GEN_POWER: +	case QT602240_GEN_ACQUIRE: +	case QT602240_TOUCH_MULTI: +	case QT602240_TOUCH_KEYARRAY: +	case QT602240_TOUCH_PROXIMITY: +	case QT602240_PROCI_GRIPFACE: +	case QT602240_PROCG_NOISE: +	case QT602240_PROCI_ONETOUCH: +	case QT602240_PROCI_TWOTOUCH: +	case QT602240_SPT_COMMSCONFIG: +	case QT602240_SPT_GPIOPWM: +	case QT602240_SPT_SELFTEST: +	case QT602240_SPT_CTECONFIG: +	case QT602240_SPT_USERDATA: +		return true; +	default: +		return false; +	} +} + +static bool qt602240_object_writable(unsigned int type) +{ +	switch (type) { +	case QT602240_GEN_COMMAND: +	case QT602240_GEN_POWER: +	case QT602240_GEN_ACQUIRE: +	case QT602240_TOUCH_MULTI: +	case QT602240_TOUCH_KEYARRAY: +	case QT602240_TOUCH_PROXIMITY: +	case QT602240_PROCI_GRIPFACE: +	case QT602240_PROCG_NOISE: +	case QT602240_PROCI_ONETOUCH: +	case QT602240_PROCI_TWOTOUCH: +	case QT602240_SPT_GPIOPWM: +	case QT602240_SPT_SELFTEST: +	case QT602240_SPT_CTECONFIG: +		return true; +	default: +		return false; +	} +} + +static void qt602240_dump_message(struct device *dev, +				  struct qt602240_message *message) +{ +	dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); +	dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); +	dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); +	dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); +	dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); +	dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); +	dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); +	dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); +	dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); +} + +static int qt602240_check_bootloader(struct i2c_client *client, +				     unsigned int state) +{ +	u8 val; + +recheck: +	if (i2c_master_recv(client, &val, 1) != 1) { +		dev_err(&client->dev, "%s: i2c recv failed\n", __func__); +		return -EIO; +	} + +	switch (state) { +	case QT602240_WAITING_BOOTLOAD_CMD: +	case QT602240_WAITING_FRAME_DATA: +		val &= ~QT602240_BOOT_STATUS_MASK; +		break; +	case QT602240_FRAME_CRC_PASS: +		if (val == QT602240_FRAME_CRC_CHECK) +			goto recheck; +		break; +	default: +		return -EINVAL; +	} + +	if (val != state) { +		dev_err(&client->dev, "Unvalid bootloader mode state\n"); +		return -EINVAL; +	} + +	return 0; +} + +static int qt602240_unlock_bootloader(struct i2c_client *client) +{ +	u8 buf[2]; + +	buf[0] = QT602240_UNLOCK_CMD_LSB; +	buf[1] = QT602240_UNLOCK_CMD_MSB; + +	if (i2c_master_send(client, buf, 2) != 2) { +		dev_err(&client->dev, "%s: i2c send failed\n", __func__); +		return -EIO; +	} + +	return 0; +} + +static int qt602240_fw_write(struct i2c_client *client, +			     const u8 *data, unsigned int frame_size) +{ +	if (i2c_master_send(client, data, frame_size) != frame_size) { +		dev_err(&client->dev, "%s: i2c send failed\n", __func__); +		return -EIO; +	} + +	return 0; +} + +static int __qt602240_read_reg(struct i2c_client *client, +			       u16 reg, u16 len, void *val) +{ +	struct i2c_msg xfer[2]; +	u8 buf[2]; + +	buf[0] = reg & 0xff; +	buf[1] = (reg >> 8) & 0xff; + +	/* Write register */ +	xfer[0].addr = client->addr; +	xfer[0].flags = 0; +	xfer[0].len = 2; +	xfer[0].buf = buf; + +	/* Read data */ +	xfer[1].addr = client->addr; +	xfer[1].flags = I2C_M_RD; +	xfer[1].len = len; +	xfer[1].buf = val; + +	if (i2c_transfer(client->adapter, xfer, 2) != 2) { +		dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); +		return -EIO; +	} + +	return 0; +} + +static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ +	return __qt602240_read_reg(client, reg, 1, val); +} + +static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ +	u8 buf[3]; + +	buf[0] = reg & 0xff; +	buf[1] = (reg >> 8) & 0xff; +	buf[2] = val; + +	if (i2c_master_send(client, buf, 3) != 3) { +		dev_err(&client->dev, "%s: i2c send failed\n", __func__); +		return -EIO; +	} + +	return 0; +} + +static int qt602240_read_object_table(struct i2c_client *client, +				      u16 reg, u8 *object_buf) +{ +	return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, +				   object_buf); +} + +static struct qt602240_object * +qt602240_get_object(struct qt602240_data *data, u8 type) +{ +	struct qt602240_object *object; +	int i; + +	for (i = 0; i < data->info.object_num; i++) { +		object = data->object_table + i; +		if (object->type == type) +			return object; +	} + +	dev_err(&data->client->dev, "Invalid object type\n"); +	return NULL; +} + +static int qt602240_read_message(struct qt602240_data *data, +				 struct qt602240_message *message) +{ +	struct qt602240_object *object; +	u16 reg; + +	object = qt602240_get_object(data, QT602240_GEN_MESSAGE); +	if (!object) +		return -EINVAL; + +	reg = object->start_address; +	return __qt602240_read_reg(data->client, reg, +			sizeof(struct qt602240_message), message); +} + +static int qt602240_read_object(struct qt602240_data *data, +				u8 type, u8 offset, u8 *val) +{ +	struct qt602240_object *object; +	u16 reg; + +	object = qt602240_get_object(data, type); +	if (!object) +		return -EINVAL; + +	reg = object->start_address; +	return __qt602240_read_reg(data->client, reg + offset, 1, val); +} + +static int qt602240_write_object(struct qt602240_data *data, +				 u8 type, u8 offset, u8 val) +{ +	struct qt602240_object *object; +	u16 reg; + +	object = qt602240_get_object(data, type); +	if (!object) +		return -EINVAL; + +	reg = object->start_address; +	return qt602240_write_reg(data->client, reg + offset, val); +} + +static void qt602240_input_report(struct qt602240_data *data, int single_id) +{ +	struct qt602240_finger *finger = data->finger; +	struct input_dev *input_dev = data->input_dev; +	int status = finger[single_id].status; +	int finger_num = 0; +	int id; + +	for (id = 0; id < QT602240_MAX_FINGER; id++) { +		if (!finger[id].status) +			continue; + +		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, +				finger[id].status != QT602240_RELEASE ? +				finger[id].area : 0); +		input_report_abs(input_dev, ABS_MT_POSITION_X, +				finger[id].x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, +				finger[id].y); +		input_mt_sync(input_dev); + +		if (finger[id].status == QT602240_RELEASE) +			finger[id].status = 0; +		else +			finger_num++; +	} + +	input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + +	if (status != QT602240_RELEASE) { +		input_report_abs(input_dev, ABS_X, finger[single_id].x); +		input_report_abs(input_dev, ABS_Y, finger[single_id].y); +	} + +	input_sync(input_dev); +} + +static void qt602240_input_touchevent(struct qt602240_data *data, +				      struct qt602240_message *message, int id) +{ +	struct qt602240_finger *finger = data->finger; +	struct device *dev = &data->client->dev; +	u8 status = message->message[0]; +	int x; +	int y; +	int area; + +	/* Check the touch is present on the screen */ +	if (!(status & QT602240_DETECT)) { +		if (status & QT602240_RELEASE) { +			dev_dbg(dev, "[%d] released\n", id); + +			finger[id].status = QT602240_RELEASE; +			qt602240_input_report(data, id); +		} +		return; +	} + +	/* Check only AMP detection */ +	if (!(status & (QT602240_PRESS | QT602240_MOVE))) +		return; + +	x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); +	y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); +	area = message->message[4]; + +	dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, +		status & QT602240_MOVE ? "moved" : "pressed", +		x, y, area); + +	finger[id].status = status & QT602240_MOVE ? +				QT602240_MOVE : QT602240_PRESS; +	finger[id].x = x; +	finger[id].y = y; +	finger[id].area = area; + +	qt602240_input_report(data, id); +} + +static irqreturn_t qt602240_interrupt(int irq, void *dev_id) +{ +	struct qt602240_data *data = dev_id; +	struct qt602240_message message; +	struct qt602240_object *object; +	struct device *dev = &data->client->dev; +	int id; +	u8 reportid; +	u8 max_reportid; +	u8 min_reportid; + +	do { +		if (qt602240_read_message(data, &message)) { +			dev_err(dev, "Failed to read message\n"); +			goto end; +		} + +		reportid = message.reportid; + +		/* whether reportid is thing of QT602240_TOUCH_MULTI */ +		object = qt602240_get_object(data, QT602240_TOUCH_MULTI); +		if (!object) +			goto end; + +		max_reportid = object->max_reportid; +		min_reportid = max_reportid - object->num_report_ids + 1; +		id = reportid - min_reportid; + +		if (reportid >= min_reportid && reportid <= max_reportid) +			qt602240_input_touchevent(data, &message, id); +		else +			qt602240_dump_message(dev, &message); +	} while (reportid != 0xff); + +end: +	return IRQ_HANDLED; +} + +static int qt602240_check_reg_init(struct qt602240_data *data) +{ +	struct qt602240_object *object; +	struct device *dev = &data->client->dev; +	int index = 0; +	int i, j; +	u8 version = data->info.version; +	u8 *init_vals; + +	switch (version) { +	case QT602240_VER_20: +		init_vals = (u8 *)init_vals_ver_20; +		break; +	case QT602240_VER_21: +		init_vals = (u8 *)init_vals_ver_21; +		break; +	case QT602240_VER_22: +		init_vals = (u8 *)init_vals_ver_22; +		break; +	default: +		dev_err(dev, "Firmware version %d doesn't support\n", version); +		return -EINVAL; +	} + +	for (i = 0; i < data->info.object_num; i++) { +		object = data->object_table + i; + +		if (!qt602240_object_writable(object->type)) +			continue; + +		for (j = 0; j < object->size + 1; j++) +			qt602240_write_object(data, object->type, j, +					init_vals[index + j]); + +		index += object->size + 1; +	} + +	return 0; +} + +static int qt602240_check_matrix_size(struct qt602240_data *data) +{ +	const struct qt602240_platform_data *pdata = data->pdata; +	struct device *dev = &data->client->dev; +	int mode = -1; +	int error; +	u8 val; + +	dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); +	dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); + +	switch (pdata->x_line) { +	case 0 ... 15: +		if (pdata->y_line <= 14) +			mode = 0; +		break; +	case 16: +		if (pdata->y_line <= 12) +			mode = 1; +		if (pdata->y_line == 13 || pdata->y_line == 14) +			mode = 0; +		break; +	case 17: +		if (pdata->y_line <= 11) +			mode = 2; +		if (pdata->y_line == 12 || pdata->y_line == 13) +			mode = 1; +		break; +	case 18: +		if (pdata->y_line <= 10) +			mode = 3; +		if (pdata->y_line == 11 || pdata->y_line == 12) +			mode = 2; +		break; +	case 19: +		if (pdata->y_line <= 9) +			mode = 4; +		if (pdata->y_line == 10 || pdata->y_line == 11) +			mode = 3; +		break; +	case 20: +		mode = 4; +	} + +	if (mode < 0) { +		dev_err(dev, "Invalid X/Y lines\n"); +		return -EINVAL; +	} + +	error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, +				QT602240_CTE_MODE, &val); +	if (error) +		return error; + +	if (mode == val) +		return 0; + +	/* Change the CTE configuration */ +	qt602240_write_object(data, QT602240_SPT_CTECONFIG, +			QT602240_CTE_CTRL, 1); +	qt602240_write_object(data, QT602240_SPT_CTECONFIG, +			QT602240_CTE_MODE, mode); +	qt602240_write_object(data, QT602240_SPT_CTECONFIG, +			QT602240_CTE_CTRL, 0); + +	return 0; +} + +static int qt602240_make_highchg(struct qt602240_data *data) +{ +	struct device *dev = &data->client->dev; +	int count = 10; +	int error; +	u8 val; + +	/* Read dummy message to make high CHG pin */ +	do { +		error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); +		if (error) +			return error; +	} while ((val != 0xff) && --count); + +	if (!count) { +		dev_err(dev, "CHG pin isn't cleared\n"); +		return -EBUSY; +	} + +	return 0; +} + +static void qt602240_handle_pdata(struct qt602240_data *data) +{ +	const struct qt602240_platform_data *pdata = data->pdata; +	u8 voltage; + +	/* Set touchscreen lines */ +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, +			pdata->x_line); +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, +			pdata->y_line); + +	/* Set touchscreen orient */ +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, +			pdata->orient); + +	/* Set touchscreen burst length */ +	qt602240_write_object(data, QT602240_TOUCH_MULTI, +			QT602240_TOUCH_BLEN, pdata->blen); + +	/* Set touchscreen threshold */ +	qt602240_write_object(data, QT602240_TOUCH_MULTI, +			QT602240_TOUCH_TCHTHR, pdata->threshold); + +	/* Set touchscreen resolution */ +	qt602240_write_object(data, QT602240_TOUCH_MULTI, +			QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); +	qt602240_write_object(data, QT602240_TOUCH_MULTI, +			QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); +	qt602240_write_object(data, QT602240_TOUCH_MULTI, +			QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); +	qt602240_write_object(data, QT602240_TOUCH_MULTI, +			QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + +	/* Set touchscreen voltage */ +	if (data->info.version >= QT602240_VER_21 && pdata->voltage) { +		if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { +			voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / +				QT602240_VOLTAGE_STEP; +			voltage = 0xff - voltage + 1; +		} else +			voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / +				QT602240_VOLTAGE_STEP; + +		qt602240_write_object(data, QT602240_SPT_CTECONFIG, +				QT602240_CTE_VOLTAGE, voltage); +	} +} + +static int qt602240_get_info(struct qt602240_data *data) +{ +	struct i2c_client *client = data->client; +	struct qt602240_info *info = &data->info; +	int error; +	u8 val; + +	error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); +	if (error) +		return error; +	info->family_id = val; + +	error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); +	if (error) +		return error; +	info->variant_id = val; + +	error = qt602240_read_reg(client, QT602240_VERSION, &val); +	if (error) +		return error; +	info->version = val; + +	error = qt602240_read_reg(client, QT602240_BUILD, &val); +	if (error) +		return error; +	info->build = val; + +	error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); +	if (error) +		return error; +	info->object_num = val; + +	return 0; +} + +static int qt602240_get_object_table(struct qt602240_data *data) +{ +	int error; +	int i; +	u16 reg; +	u8 reportid = 0; +	u8 buf[QT602240_OBJECT_SIZE]; + +	for (i = 0; i < data->info.object_num; i++) { +		struct qt602240_object *object = data->object_table + i; + +		reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; +		error = qt602240_read_object_table(data->client, reg, buf); +		if (error) +			return error; + +		object->type = buf[0]; +		object->start_address = (buf[2] << 8) | buf[1]; +		object->size = buf[3]; +		object->instances = buf[4]; +		object->num_report_ids = buf[5]; + +		if (object->num_report_ids) { +			reportid += object->num_report_ids * +					(object->instances + 1); +			object->max_reportid = reportid; +		} +	} + +	return 0; +} + +static int qt602240_initialize(struct qt602240_data *data) +{ +	struct i2c_client *client = data->client; +	struct qt602240_info *info = &data->info; +	int error; +	u8 val; + +	error = qt602240_get_info(data); +	if (error) +		return error; + +	data->object_table = kcalloc(info->object_num, +				     sizeof(struct qt602240_data), +				     GFP_KERNEL); +	if (!data->object_table) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	/* Get object table information */ +	error = qt602240_get_object_table(data); +	if (error) +		return error; + +	/* Check register init values */ +	error = qt602240_check_reg_init(data); +	if (error) +		return error; + +	/* Check X/Y matrix size */ +	error = qt602240_check_matrix_size(data); +	if (error) +		return error; + +	error = qt602240_make_highchg(data); +	if (error) +		return error; + +	qt602240_handle_pdata(data); + +	/* Backup to memory */ +	qt602240_write_object(data, QT602240_GEN_COMMAND, +			QT602240_COMMAND_BACKUPNV, +			QT602240_BACKUP_VALUE); +	msleep(QT602240_BACKUP_TIME); + +	/* Soft reset */ +	qt602240_write_object(data, QT602240_GEN_COMMAND, +			QT602240_COMMAND_RESET, 1); +	msleep(QT602240_RESET_TIME); + +	/* Update matrix size at info struct */ +	error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); +	if (error) +		return error; +	info->matrix_xsize = val; + +	error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); +	if (error) +		return error; +	info->matrix_ysize = val; + +	dev_info(&client->dev, +			"Family ID: %d Variant ID: %d Version: %d Build: %d\n", +			info->family_id, info->variant_id, info->version, +			info->build); + +	dev_info(&client->dev, +			"Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", +			info->matrix_xsize, info->matrix_ysize, +			info->object_num); + +	return 0; +} + +static ssize_t qt602240_object_show(struct device *dev, +				    struct device_attribute *attr, char *buf) +{ +	struct qt602240_data *data = dev_get_drvdata(dev); +	struct qt602240_object *object; +	int count = 0; +	int i, j; +	int error; +	u8 val; + +	for (i = 0; i < data->info.object_num; i++) { +		object = data->object_table + i; + +		count += sprintf(buf + count, +				"Object Table Element %d(Type %d)\n", +				i + 1, object->type); + +		if (!qt602240_object_readable(object->type)) { +			count += sprintf(buf + count, "\n"); +			continue; +		} + +		for (j = 0; j < object->size + 1; j++) { +			error = qt602240_read_object(data, +						object->type, j, &val); +			if (error) +				return error; + +			count += sprintf(buf + count, +					"  Byte %d: 0x%x (%d)\n", j, val, val); +		} + +		count += sprintf(buf + count, "\n"); +	} + +	return count; +} + +static int qt602240_load_fw(struct device *dev, const char *fn) +{ +	struct qt602240_data *data = dev_get_drvdata(dev); +	struct i2c_client *client = data->client; +	const struct firmware *fw = NULL; +	unsigned int frame_size; +	unsigned int pos = 0; +	int ret; + +	ret = request_firmware(&fw, fn, dev); +	if (ret) { +		dev_err(dev, "Unable to open firmware %s\n", fn); +		return ret; +	} + +	/* Change to the bootloader mode */ +	qt602240_write_object(data, QT602240_GEN_COMMAND, +			QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); +	msleep(QT602240_RESET_TIME); + +	/* Change to slave address of bootloader */ +	if (client->addr == QT602240_APP_LOW) +		client->addr = QT602240_BOOT_LOW; +	else +		client->addr = QT602240_BOOT_HIGH; + +	ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); +	if (ret) +		goto out; + +	/* Unlock bootloader */ +	qt602240_unlock_bootloader(client); + +	while (pos < fw->size) { +		ret = qt602240_check_bootloader(client, +						QT602240_WAITING_FRAME_DATA); +		if (ret) +			goto out; + +		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + +		/* We should add 2 at frame size as the the firmware data is not +		 * included the CRC bytes. +		 */ +		frame_size += 2; + +		/* Write one frame to device */ +		qt602240_fw_write(client, fw->data + pos, frame_size); + +		ret = qt602240_check_bootloader(client, +						QT602240_FRAME_CRC_PASS); +		if (ret) +			goto out; + +		pos += frame_size; + +		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); +	} + +out: +	release_firmware(fw); + +	/* Change to slave address of application */ +	if (client->addr == QT602240_BOOT_LOW) +		client->addr = QT602240_APP_LOW; +	else +		client->addr = QT602240_APP_HIGH; + +	return ret; +} + +static ssize_t qt602240_update_fw_store(struct device *dev, +					struct device_attribute *attr, +					const char *buf, size_t count) +{ +	struct qt602240_data *data = dev_get_drvdata(dev); +	unsigned int version; +	int error; + +	if (sscanf(buf, "%u", &version) != 1) { +		dev_err(dev, "Invalid values\n"); +		return -EINVAL; +	} + +	if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { +		dev_err(dev, "FW update supported starting with version 21\n"); +		return -EINVAL; +	} + +	disable_irq(data->irq); + +	error = qt602240_load_fw(dev, QT602240_FW_NAME); +	if (error) { +		dev_err(dev, "The firmware update failed(%d)\n", error); +		count = error; +	} else { +		dev_dbg(dev, "The firmware update succeeded\n"); + +		/* Wait for reset */ +		msleep(QT602240_FWRESET_TIME); + +		kfree(data->object_table); +		data->object_table = NULL; + +		qt602240_initialize(data); +	} + +	enable_irq(data->irq); + +	return count; +} + +static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); + +static struct attribute *qt602240_attrs[] = { +	&dev_attr_object.attr, +	&dev_attr_update_fw.attr, +	NULL +}; + +static const struct attribute_group qt602240_attr_group = { +	.attrs = qt602240_attrs, +}; + +static void qt602240_start(struct qt602240_data *data) +{ +	/* Touch enable */ +	qt602240_write_object(data, +			QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); +} + +static void qt602240_stop(struct qt602240_data *data) +{ +	/* Touch disable */ +	qt602240_write_object(data, +			QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); +} + +static int qt602240_input_open(struct input_dev *dev) +{ +	struct qt602240_data *data = input_get_drvdata(dev); + +	qt602240_start(data); + +	return 0; +} + +static void qt602240_input_close(struct input_dev *dev) +{ +	struct qt602240_data *data = input_get_drvdata(dev); + +	qt602240_stop(data); +} + +static int __devinit qt602240_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct qt602240_data *data; +	struct input_dev *input_dev; +	int error; + +	if (!client->dev.platform_data) +		return -EINVAL; + +	data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!data || !input_dev) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		error = -ENOMEM; +		goto err_free_mem; +	} + +	input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; +	input_dev->open = qt602240_input_open; +	input_dev->close = qt602240_input_close; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	/* For single touch */ +	input_set_abs_params(input_dev, ABS_X, +			     0, QT602240_MAX_XC, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, +			     0, QT602240_MAX_YC, 0, 0); + +	/* For multi touch */ +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +			     0, QT602240_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, QT602240_MAX_XC, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, QT602240_MAX_YC, 0, 0); + +	input_set_drvdata(input_dev, data); + +	data->client = client; +	data->input_dev = input_dev; +	data->pdata = client->dev.platform_data; +	data->irq = client->irq; + +	i2c_set_clientdata(client, data); + +	error = qt602240_initialize(data); +	if (error) +		goto err_free_object; + +	error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, +			IRQF_TRIGGER_FALLING, client->dev.driver->name, data); +	if (error) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		goto err_free_object; +	} + +	error = input_register_device(input_dev); +	if (error) +		goto err_free_irq; + +	error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); +	if (error) +		goto err_unregister_device; + +	return 0; + +err_unregister_device: +	input_unregister_device(input_dev); +	input_dev = NULL; +err_free_irq: +	free_irq(client->irq, data); +err_free_object: +	kfree(data->object_table); +err_free_mem: +	input_free_device(input_dev); +	kfree(data); +	return error; +} + +static int __devexit qt602240_remove(struct i2c_client *client) +{ +	struct qt602240_data *data = i2c_get_clientdata(client); + +	sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); +	free_irq(data->irq, data); +	input_unregister_device(data->input_dev); +	kfree(data->object_table); +	kfree(data); + +	return 0; +} + +#ifdef CONFIG_PM +static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) +{ +	struct qt602240_data *data = i2c_get_clientdata(client); +	struct input_dev *input_dev = data->input_dev; + +	mutex_lock(&input_dev->mutex); + +	if (input_dev->users) +		qt602240_stop(data); + +	mutex_unlock(&input_dev->mutex); + +	return 0; +} + +static int qt602240_resume(struct i2c_client *client) +{ +	struct qt602240_data *data = i2c_get_clientdata(client); +	struct input_dev *input_dev = data->input_dev; + +	/* Soft reset */ +	qt602240_write_object(data, QT602240_GEN_COMMAND, +			QT602240_COMMAND_RESET, 1); + +	msleep(QT602240_RESET_TIME); + +	mutex_lock(&input_dev->mutex); + +	if (input_dev->users) +		qt602240_start(data); + +	mutex_unlock(&input_dev->mutex); + +	return 0; +} +#else +#define qt602240_suspend	NULL +#define qt602240_resume		NULL +#endif + +static const struct i2c_device_id qt602240_id[] = { +	{ "qt602240_ts", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, qt602240_id); + +static struct i2c_driver qt602240_driver = { +	.driver = { +		.name	= "qt602240_ts", +		.owner	= THIS_MODULE, +	}, +	.probe		= qt602240_probe, +	.remove		= __devexit_p(qt602240_remove), +	.suspend	= qt602240_suspend, +	.resume		= qt602240_resume, +	.id_table	= qt602240_id, +}; + +static int __init qt602240_init(void) +{ +	return i2c_add_driver(&qt602240_driver); +} + +static void __exit qt602240_exit(void) +{ +	i2c_del_driver(&qt602240_driver); +} + +module_init(qt602240_init); +module_exit(qt602240_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 5b70a1419b4..a644d18c04d 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -355,9 +355,6 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev)  	struct tps6507x_ts *tsc = tps6507x_dev->ts;  	struct input_dev *input_dev = tsc->input_dev; -	if (!tsc) -		return 0; -  	cancel_delayed_work_sync(&tsc->work);  	destroy_workqueue(tsc->wq); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 567d57215c2..f45f80f6d33 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -95,6 +95,7 @@ struct usbtouch_device_info {  	int  (*get_pkt_len) (unsigned char *pkt, int len);  	int  (*read_data)   (struct usbtouch_usb *usbtouch, unsigned char *pkt); +	int  (*alloc)       (struct usbtouch_usb *usbtouch);  	int  (*init)        (struct usbtouch_usb *usbtouch);  	void (*exit)	    (struct usbtouch_usb *usbtouch);  }; @@ -135,7 +136,7 @@ enum {  	DEVTYPE_JASTEC,  	DEVTYPE_E2I,  	DEVTYPE_ZYTRONIC, -	DEVTYPE_TC5UH, +	DEVTYPE_TC45USB,  	DEVTYPE_NEXIO,  }; @@ -222,8 +223,11 @@ static const struct usb_device_id usbtouch_devices[] = {  	{USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},  #endif -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH -	{USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH}, +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB +	/* TC5UH */ +	{USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB}, +	/* TC4UM */ +	{USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB},  #endif  #ifdef CONFIG_TOUCHSCREEN_USB_NEXIO @@ -507,7 +511,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)  	int ret = -ENOMEM;  	unsigned char *buf; -	buf = kmalloc(2, GFP_KERNEL); +	buf = kmalloc(2, GFP_NOIO);  	if (!buf)  		goto err_nobuf;  	/* reset */ @@ -574,10 +578,10 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)  #endif  /***************************************************************************** - * ET&T TC5UH part + * ET&T TC5UH/TC4UM part   */ -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH -static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB +static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt)  {  	dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];  	dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; @@ -732,11 +736,43 @@ static void nexio_ack_complete(struct urb *urb)  {  } +static int nexio_alloc(struct usbtouch_usb *usbtouch) +{ +	struct nexio_priv *priv; +	int ret = -ENOMEM; + +	usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); +	if (!usbtouch->priv) +		goto out_buf; + +	priv = usbtouch->priv; + +	priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), +				GFP_KERNEL); +	if (!priv->ack_buf) +		goto err_priv; + +	priv->ack = usb_alloc_urb(0, GFP_KERNEL); +	if (!priv->ack) { +		dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); +		goto err_ack_buf; +	} + +	return 0; + +err_ack_buf: +	kfree(priv->ack_buf); +err_priv: +	kfree(priv); +out_buf: +	return ret; +} +  static int nexio_init(struct usbtouch_usb *usbtouch)  {  	struct usb_device *dev = interface_to_usbdev(usbtouch->interface);  	struct usb_host_interface *interface = usbtouch->interface->cur_altsetting; -	struct nexio_priv *priv; +	struct nexio_priv *priv = usbtouch->priv;  	int ret = -ENOMEM;  	int actual_len, i;  	unsigned char *buf; @@ -755,7 +791,7 @@ static int nexio_init(struct usbtouch_usb *usbtouch)  	if (!input_ep || !output_ep)  		return -ENXIO; -	buf = kmalloc(NEXIO_BUFSIZE, GFP_KERNEL); +	buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO);  	if (!buf)  		goto out_buf; @@ -787,11 +823,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch)  		switch (buf[0]) {  		case 0x83:	/* firmware version */  			if (!firmware_ver) -				firmware_ver = kstrdup(&buf[2], GFP_KERNEL); +				firmware_ver = kstrdup(&buf[2], GFP_NOIO);  			break;  		case 0x84:	/* device name */  			if (!device_name) -				device_name = kstrdup(&buf[2], GFP_KERNEL); +				device_name = kstrdup(&buf[2], GFP_NOIO);  			break;  		}  	} @@ -802,36 +838,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch)  	kfree(firmware_ver);  	kfree(device_name); -	/* prepare ACK URB */ -	ret = -ENOMEM; - -	usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); -	if (!usbtouch->priv) -		goto out_buf; - -	priv = usbtouch->priv; - -	priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), -				GFP_KERNEL); -	if (!priv->ack_buf) -		goto err_priv; - -	priv->ack = usb_alloc_urb(0, GFP_KERNEL); -	if (!priv->ack) { -		dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); -		goto err_ack_buf; -	} -  	usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep),  			  priv->ack_buf, sizeof(nexio_ack_pkt),  			  nexio_ack_complete, usbtouch);  	ret = 0; -	goto out_buf; -err_ack_buf: -	kfree(priv->ack_buf); -err_priv: -	kfree(priv);  out_buf:  	kfree(buf);  	return ret; @@ -849,29 +860,32 @@ static void nexio_exit(struct usbtouch_usb *usbtouch)  static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)  { -	int x, y, begin_x, begin_y, end_x, end_y, w, h, ret;  	struct nexio_touch_packet *packet = (void *) pkt;  	struct nexio_priv *priv = usbtouch->priv; +	unsigned int data_len = be16_to_cpu(packet->data_len); +	unsigned int x_len = be16_to_cpu(packet->x_len); +	unsigned int y_len = be16_to_cpu(packet->y_len); +	int x, y, begin_x, begin_y, end_x, end_y, w, h, ret;  	/* got touch data? */  	if ((pkt[0] & 0xe0) != 0xe0)  		return 0; -	if (be16_to_cpu(packet->data_len) > 0xff) -		packet->data_len = cpu_to_be16(be16_to_cpu(packet->data_len) - 0x100); -	if (be16_to_cpu(packet->x_len) > 0xff) -		packet->x_len = cpu_to_be16(be16_to_cpu(packet->x_len) - 0x80); +	if (data_len > 0xff) +		data_len -= 0x100; +	if (x_len > 0xff) +		x_len -= 0x80;  	/* send ACK */  	ret = usb_submit_urb(priv->ack, GFP_ATOMIC);  	if (!usbtouch->type->max_xc) { -		usbtouch->type->max_xc = 2 * be16_to_cpu(packet->x_len); -		input_set_abs_params(usbtouch->input, ABS_X, 0, -				     2 * be16_to_cpu(packet->x_len), 0, 0); -		usbtouch->type->max_yc = 2 * be16_to_cpu(packet->y_len); -		input_set_abs_params(usbtouch->input, ABS_Y, 0, -				     2 * be16_to_cpu(packet->y_len), 0, 0); +		usbtouch->type->max_xc = 2 * x_len; +		input_set_abs_params(usbtouch->input, ABS_X, +				     0, usbtouch->type->max_xc, 0, 0); +		usbtouch->type->max_yc = 2 * y_len; +		input_set_abs_params(usbtouch->input, ABS_Y, +				     0, usbtouch->type->max_yc, 0, 0);  	}  	/*  	 * The device reports state of IR sensors on X and Y axes. @@ -881,22 +895,21 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)  	 * it's disabled (and untested) here as there's no X driver for that.  	 */  	begin_x = end_x = begin_y = end_y = -1; -	for (x = 0; x < be16_to_cpu(packet->x_len); x++) { +	for (x = 0; x < x_len; x++) {  		if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) {  			begin_x = x;  			continue;  		}  		if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) {  			end_x = x - 1; -			for (y = be16_to_cpu(packet->x_len); -			     y < be16_to_cpu(packet->data_len); y++) { +			for (y = x_len; y < data_len; y++) {  				if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) { -					begin_y = y - be16_to_cpu(packet->x_len); +					begin_y = y - x_len;  					continue;  				}  				if (end_y == -1 &&  				    begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) { -					end_y = y - 1 - be16_to_cpu(packet->x_len); +					end_y = y - 1 - x_len;  					w = end_x - begin_x;  					h = end_y - begin_y;  #if 0 @@ -1104,14 +1117,14 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {  	},  #endif -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH -	[DEVTYPE_TC5UH] = { +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB +	[DEVTYPE_TC45USB] = {  		.min_xc		= 0x0,  		.max_xc		= 0x0fff,  		.min_yc		= 0x0,  		.max_yc		= 0x0fff,  		.rept_size	= 5, -		.read_data	= tc5uh_read_data, +		.read_data	= tc45usb_read_data,  	},  #endif @@ -1120,6 +1133,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {  		.rept_size	= 1024,  		.irq_always	= true,  		.read_data	= nexio_read_data, +		.alloc		= nexio_alloc,  		.init		= nexio_init,  		.exit		= nexio_exit,  	}, @@ -1263,6 +1277,7 @@ static void usbtouch_irq(struct urb *urb)  	usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);  exit: +	usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval)  		err("%s - usb_submit_urb failed with result: %d", @@ -1272,25 +1287,89 @@ exit:  static int usbtouch_open(struct input_dev *input)  {  	struct usbtouch_usb *usbtouch = input_get_drvdata(input); +	int r;  	usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface); +	r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0; +	if (r < 0) +		goto out; +  	if (!usbtouch->type->irq_always) { -		if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) -		  return -EIO; +		if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) { +			r = -EIO; +			goto out_put; +		}  	} -	return 0; +	usbtouch->interface->needs_remote_wakeup = 1; +out_put: +	usb_autopm_put_interface(usbtouch->interface); +out: +	return r;  }  static void usbtouch_close(struct input_dev *input)  {  	struct usbtouch_usb *usbtouch = input_get_drvdata(input); +	int r;  	if (!usbtouch->type->irq_always)  		usb_kill_urb(usbtouch->irq); +	r = usb_autopm_get_interface(usbtouch->interface); +	usbtouch->interface->needs_remote_wakeup = 0; +	if (!r) +		usb_autopm_put_interface(usbtouch->interface);  } +static int usbtouch_suspend +(struct usb_interface *intf, pm_message_t message) +{ +	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + +	usb_kill_urb(usbtouch->irq); + +	return 0; +} + +static int usbtouch_resume(struct usb_interface *intf) +{ +	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); +	struct input_dev *input = usbtouch->input; +	int result = 0; + +	mutex_lock(&input->mutex); +	if (input->users || usbtouch->type->irq_always) +		result = usb_submit_urb(usbtouch->irq, GFP_NOIO); +	mutex_unlock(&input->mutex); + +	return result; +} + +static int usbtouch_reset_resume(struct usb_interface *intf) +{ +	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); +	struct input_dev *input = usbtouch->input; +	int err = 0; + +	/* reinit the device */ +	if (usbtouch->type->init) { +		err = usbtouch->type->init(usbtouch); +		if (err) { +			dbg("%s - type->init() failed, err: %d", +			    __func__, err); +			return err; +		} +	} + +	/* restart IO if needed */ +	mutex_lock(&input->mutex); +	if (input->users) +		err = usb_submit_urb(usbtouch->irq, GFP_NOIO); +	mutex_unlock(&input->mutex); + +	return err; +}  static void usbtouch_free_buffers(struct usb_device *udev,  				  struct usbtouch_usb *usbtouch) @@ -1411,12 +1490,21 @@ static int usbtouch_probe(struct usb_interface *intf,  	usbtouch->irq->transfer_dma = usbtouch->data_dma;  	usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -	/* device specific init */ +	/* device specific allocations */ +	if (type->alloc) { +		err = type->alloc(usbtouch); +		if (err) { +			dbg("%s - type->alloc() failed, err: %d", __func__, err); +			goto out_free_urb; +		} +	} + +	/* device specific initialisation*/  	if (type->init) {  		err = type->init(usbtouch);  		if (err) {  			dbg("%s - type->init() failed, err: %d", __func__, err); -			goto out_free_urb; +			goto out_do_exit;  		}  	} @@ -1429,8 +1517,11 @@ static int usbtouch_probe(struct usb_interface *intf,  	usb_set_intfdata(intf, usbtouch);  	if (usbtouch->type->irq_always) { +		/* this can't fail */ +		usb_autopm_get_interface(intf);  		err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);  		if (err) { +			usb_autopm_put_interface(intf);  			err("%s - usb_submit_urb failed with result: %d",  			    __func__, err);  			goto out_unregister_input; @@ -1481,7 +1572,11 @@ static struct usb_driver usbtouch_driver = {  	.name		= "usbtouchscreen",  	.probe		= usbtouch_probe,  	.disconnect	= usbtouch_disconnect, +	.suspend	= usbtouch_suspend, +	.resume		= usbtouch_resume, +	.reset_resume	= usbtouch_reset_resume,  	.id_table	= usbtouch_devices, +	.supports_autosuspend = 1,  };  static int __init usbtouch_init(void) diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index e14081675bb..ebb11907d40 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -339,7 +339,7 @@ static struct xenbus_driver xenkbd_driver = {  static int __init xenkbd_init(void)  { -	if (!xen_domain()) +	if (!xen_pv_domain())  		return -ENODEV;  	/* Nothing to do if running in dom0. */  |