diff options
Diffstat (limited to 'drivers/input/touchscreen/wacom_w8001.c')
| -rw-r--r-- | drivers/input/touchscreen/wacom_w8001.c | 299 | 
1 files changed, 221 insertions, 78 deletions
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 9ae4c7b16ba..2792ca397dd 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -3,6 +3,7 @@   *   * Copyright (c) 2008 Jaya Kumar   * Copyright (c) 2010 Red Hat, Inc. + * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>   *   * This file is subject to the terms and conditions of the GNU General Public   * License. See the file COPYING in the main directory of this archive for @@ -15,10 +16,10 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/slab.h> -#include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/serio.h> -#include <linux/init.h>  #include <linux/ctype.h> +#include <linux/delay.h>  #define DRIVER_DESC	"Wacom W8001 serial touchscreen driver" @@ -37,6 +38,7 @@ MODULE_LICENSE("GPL");  #define W8001_QUERY_PACKET	0x20 +#define W8001_CMD_STOP		'0'  #define W8001_CMD_START		'1'  #define W8001_CMD_QUERY		'*'  #define W8001_CMD_TOUCHQUERY	'%' @@ -48,7 +50,9 @@ MODULE_LICENSE("GPL");  #define W8001_PKTLEN_TPCCTL	11	/* control packet */  #define W8001_PKTLEN_TOUCH2FG	13 -#define MAX_TRACKING_ID		0xFF	/* arbitrarily chosen */ +/* resolution in points/mm */ +#define W8001_PEN_RESOLUTION    100 +#define W8001_TOUCH_RESOLUTION  10  struct w8001_coord {  	u8 rdy; @@ -64,11 +68,11 @@ struct w8001_coord {  /* touch query reply packet */  struct w8001_touch_query { +	u16 x; +	u16 y;  	u8 panel_res;  	u8 capacity_res;  	u8 sensor_id; -	u16 x; -	u16 y;  };  /* @@ -87,10 +91,14 @@ struct w8001 {  	char phys[32];  	int type;  	unsigned int pktlen; -	int trkid[2]; +	u16 max_touch_x; +	u16 max_touch_y; +	u16 max_pen_x; +	u16 max_pen_y; +	char name[64];  }; -static void parse_data(u8 *data, struct w8001_coord *coord) +static void parse_pen_data(u8 *data, struct w8001_coord *coord)  {  	memset(coord, 0, sizeof(*coord)); @@ -114,30 +122,58 @@ static void parse_data(u8 *data, struct w8001_coord *coord)  	coord->tilt_y = data[8] & 0x7F;  } -static void parse_touch(struct w8001 *w8001) +static void parse_single_touch(u8 *data, struct w8001_coord *coord) +{ +	coord->x = (data[1] << 7) | data[2]; +	coord->y = (data[3] << 7) | data[4]; +	coord->tsw = data[0] & 0x01; +} + +static void scale_touch_coordinates(struct w8001 *w8001, +				    unsigned int *x, unsigned int *y) +{ +	if (w8001->max_pen_x && w8001->max_touch_x) +		*x = *x * w8001->max_pen_x / w8001->max_touch_x; + +	if (w8001->max_pen_y && w8001->max_touch_y) +		*y = *y * w8001->max_pen_y / w8001->max_touch_y; +} + +static void parse_multi_touch(struct w8001 *w8001)  { -	static int trkid;  	struct input_dev *dev = w8001->dev;  	unsigned char *data = w8001->data; +	unsigned int x, y;  	int i; +	int count = 0;  	for (i = 0; i < 2; i++) { -		input_mt_slot(dev, i); +		bool touch = data[0] & (1 << i); -		if (data[0] & (1 << i)) { -			int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); -			int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); +		input_mt_slot(dev, i); +		input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); +		if (touch) { +			x = (data[6 * i + 1] << 7) | data[6 * i + 2]; +			y = (data[6 * i + 3] << 7) | data[6 * i + 4];  			/* data[5,6] and [11,12] is finger capacity */ +			/* scale to pen maximum */ +			scale_touch_coordinates(w8001, &x, &y); +  			input_report_abs(dev, ABS_MT_POSITION_X, x);  			input_report_abs(dev, ABS_MT_POSITION_Y, y); -			input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); -			if (w8001->trkid[i] < 0) -				w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; -		} else { -			w8001->trkid[i] = -1; +			count++;  		} -		input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); +	} + +	/* emulate single touch events when stylus is out of proximity. +	 * This is to make single touch backward support consistent +	 * across all Wacom single touch devices. +	 */ +	if (w8001->type != BTN_TOOL_PEN && +			    w8001->type != BTN_TOOL_RUBBER) { +		w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED; +		input_mt_report_pointer_emulation(dev, true);  	}  	input_sync(dev); @@ -158,6 +194,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)  	query->y = data[5] << 9;  	query->y |= data[6] << 2;  	query->y |= (data[2] >> 3) & 0x3; + +	/* Early days' single-finger touch models need the following defaults */ +	if (!query->x && !query->y) { +		query->x = 1024; +		query->y = 1024; +		if (query->panel_res) +			query->x = query->y = (1 << query->panel_res); +		query->panel_res = W8001_TOUCH_RESOLUTION; +	}  }  static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) @@ -167,16 +212,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)  	/*  	 * We have 1 bit for proximity (rdy) and 3 bits for tip, side,  	 * side2/eraser. If rdy && f2 are set, this can be either pen + side2, -	 * or eraser. assume +	 * or eraser. Assume:  	 * - if dev is already in proximity and f2 is toggled → pen + side2  	 * - if dev comes into proximity with f2 set → eraser  	 * If f2 disappears after assuming eraser, fake proximity out for  	 * eraser and in for pen.  	 */ -	if (!w8001->type) { -		w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; -	} else if (w8001->type == BTN_TOOL_RUBBER) { +	switch (w8001->type) { +	case BTN_TOOL_RUBBER:  		if (!coord->f2) {  			input_report_abs(dev, ABS_PRESSURE, 0);  			input_report_key(dev, BTN_TOUCH, 0); @@ -186,8 +230,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)  			input_sync(dev);  			w8001->type = BTN_TOOL_PEN;  		} -	} else { +		break; + +	case BTN_TOOL_FINGER: +		input_report_key(dev, BTN_TOUCH, 0); +		input_report_key(dev, BTN_TOOL_FINGER, 0); +		input_sync(dev); +		/* fall through */ + +	case KEY_RESERVED: +		w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; +		break; + +	default:  		input_report_key(dev, BTN_STYLUS2, coord->f2); +		break;  	}  	input_report_abs(dev, ABS_X, coord->x); @@ -199,7 +256,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)  	input_sync(dev);  	if (!coord->rdy) -		w8001->type = 0; +		w8001->type = KEY_RESERVED; +} + +static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) +{ +	struct input_dev *dev = w8001->dev; +	unsigned int x = coord->x; +	unsigned int y = coord->y; + +	/* scale to pen maximum */ +	scale_touch_coordinates(w8001, &x, &y); + +	input_report_abs(dev, ABS_X, x); +	input_report_abs(dev, ABS_Y, y); +	input_report_key(dev, BTN_TOUCH, coord->tsw); +	input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); + +	input_sync(dev); + +	w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;  }  static irqreturn_t w8001_interrupt(struct serio *serio, @@ -220,9 +296,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,  	case W8001_PKTLEN_TOUCH93 - 1:  	case W8001_PKTLEN_TOUCH9A - 1: -		/* ignore one-finger touch packet. */ -		if (w8001->pktlen == w8001->idx) +		tmp = w8001->data[0] & W8001_TOUCH_BYTE; +		if (tmp != W8001_TOUCH_BYTE) +			break; + +		if (w8001->pktlen == w8001->idx) {  			w8001->idx = 0; +			if (w8001->type != BTN_TOOL_PEN && +			    w8001->type != BTN_TOOL_RUBBER) { +				parse_single_touch(w8001->data, &coord); +				report_single_touch(w8001, &coord); +			} +		}  		break;  	/* Pen coordinates packet */ @@ -231,18 +316,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,  		if (unlikely(tmp == W8001_TAB_BYTE))  			break; -		tmp = (w8001->data[0] & W8001_TOUCH_BYTE); +		tmp = w8001->data[0] & W8001_TOUCH_BYTE;  		if (tmp == W8001_TOUCH_BYTE)  			break;  		w8001->idx = 0; -		parse_data(w8001->data, &coord); +		parse_pen_data(w8001->data, &coord);  		report_pen_events(w8001, &coord);  		break;  	/* control packet */  	case W8001_PKTLEN_TPCCTL - 1: -		tmp = (w8001->data[0] & W8001_TOUCH_MASK); +		tmp = w8001->data[0] & W8001_TOUCH_MASK;  		if (tmp == W8001_TOUCH_BYTE)  			break; @@ -255,7 +340,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio,  	/* 2 finger touch packet */  	case W8001_PKTLEN_TOUCH2FG - 1:  		w8001->idx = 0; -		parse_touch(w8001); +		parse_multi_touch(w8001);  		break;  	} @@ -281,57 +366,130 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,  	return rc;  } +static int w8001_open(struct input_dev *dev) +{ +	struct w8001 *w8001 = input_get_drvdata(dev); + +	return w8001_command(w8001, W8001_CMD_START, false); +} + +static void w8001_close(struct input_dev *dev) +{ +	struct w8001 *w8001 = input_get_drvdata(dev); + +	w8001_command(w8001, W8001_CMD_STOP, false); +} +  static int w8001_setup(struct w8001 *w8001)  {  	struct input_dev *dev = w8001->dev;  	struct w8001_coord coord; +	struct w8001_touch_query touch;  	int error; -	error = w8001_command(w8001, W8001_CMD_QUERY, true); +	error = w8001_command(w8001, W8001_CMD_STOP, false);  	if (error)  		return error; -	parse_data(w8001->response, &coord); +	msleep(250);	/* wait 250ms before querying the device */ -	input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); -	input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); -	input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); -	input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); -	input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); +	dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); -	error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); +	__set_bit(INPUT_PROP_DIRECT, dev->propbit); + +	/* penabled? */ +	error = w8001_command(w8001, W8001_CMD_QUERY, true);  	if (!error) { -		struct w8001_touch_query touch; +		__set_bit(BTN_TOUCH, dev->keybit); +		__set_bit(BTN_TOOL_PEN, dev->keybit); +		__set_bit(BTN_TOOL_RUBBER, dev->keybit); +		__set_bit(BTN_STYLUS, dev->keybit); +		__set_bit(BTN_STYLUS2, dev->keybit); + +		parse_pen_data(w8001->response, &coord); +		w8001->max_pen_x = coord.x; +		w8001->max_pen_y = coord.y; + +		input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); +		input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); +		input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); +		input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); +		input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); +		if (coord.tilt_x && coord.tilt_y) { +			input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); +			input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); +		} +		w8001->id = 0x90; +		strlcat(w8001->name, " Penabled", sizeof(w8001->name)); +	} + +	/* Touch enabled? */ +	error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + +	/* +	 * Some non-touch devices may reply to the touch query. But their +	 * second byte is empty, which indicates touch is not supported. +	 */ +	if (!error && w8001->response[1]) { +		__set_bit(BTN_TOUCH, dev->keybit); +		__set_bit(BTN_TOOL_FINGER, dev->keybit);  		parse_touchquery(w8001->response, &touch); +		w8001->max_touch_x = touch.x; +		w8001->max_touch_y = touch.y; + +		if (w8001->max_pen_x && w8001->max_pen_y) { +			/* if pen is supported scale to pen maximum */ +			touch.x = w8001->max_pen_x; +			touch.y = w8001->max_pen_y; +			touch.panel_res = W8001_PEN_RESOLUTION; +		} + +		input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); +		input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); +		input_abs_set_res(dev, ABS_X, touch.panel_res); +		input_abs_set_res(dev, ABS_Y, touch.panel_res);  		switch (touch.sensor_id) {  		case 0:  		case 2:  			w8001->pktlen = W8001_PKTLEN_TOUCH93; +			w8001->id = 0x93; +			strlcat(w8001->name, " 1FG", sizeof(w8001->name));  			break; +  		case 1:  		case 3:  		case 4:  			w8001->pktlen = W8001_PKTLEN_TOUCH9A; +			strlcat(w8001->name, " 1FG", sizeof(w8001->name)); +			w8001->id = 0x9a;  			break; +  		case 5:  			w8001->pktlen = W8001_PKTLEN_TOUCH2FG; -			input_mt_create_slots(dev, 2); -			input_set_abs_params(dev, ABS_MT_TRACKING_ID, -						0, MAX_TRACKING_ID, 0, 0); +			input_mt_init_slots(dev, 2, 0);  			input_set_abs_params(dev, ABS_MT_POSITION_X,  						0, touch.x, 0, 0);  			input_set_abs_params(dev, ABS_MT_POSITION_Y,  						0, touch.y, 0, 0);  			input_set_abs_params(dev, ABS_MT_TOOL_TYPE, -						0, 0, 0, 0); +						0, MT_TOOL_MAX, 0, 0); + +			strlcat(w8001->name, " 2FG", sizeof(w8001->name)); +			if (w8001->max_pen_x && w8001->max_pen_y) +				w8001->id = 0xE3; +			else +				w8001->id = 0xE2;  			break;  		}  	} -	return w8001_command(w8001, W8001_CMD_START, false); +	strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + +	return 0;  }  /* @@ -342,12 +500,12 @@ static void w8001_disconnect(struct serio *serio)  {  	struct w8001 *w8001 = serio_get_drvdata(serio); -	input_get_device(w8001->dev); -	input_unregister_device(w8001->dev);  	serio_close(serio); -	serio_set_drvdata(serio, NULL); -	input_put_device(w8001->dev); + +	input_unregister_device(w8001->dev);  	kfree(w8001); + +	serio_set_drvdata(serio, NULL);  }  /* @@ -370,27 +528,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)  	}  	w8001->serio = serio; -	w8001->id = serio->id.id;  	w8001->dev = input_dev; -	w8001->trkid[0] = w8001->trkid[1] = -1;  	init_completion(&w8001->cmd_done);  	snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); -	input_dev->name = "Wacom W8001 Penabled Serial TouchScreen"; -	input_dev->phys = w8001->phys; -	input_dev->id.bustype = BUS_RS232; -	input_dev->id.vendor = SERIO_W8001; -	input_dev->id.product = w8001->id; -	input_dev->id.version = 0x0100; -	input_dev->dev.parent = &serio->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_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); -	input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); -	input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); -	input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); -  	serio_set_drvdata(serio, w8001);  	err = serio_open(serio, drv);  	if (err) @@ -400,6 +541,19 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)  	if (err)  		goto fail3; +	input_dev->name = w8001->name; +	input_dev->phys = w8001->phys; +	input_dev->id.product = w8001->id; +	input_dev->id.bustype = BUS_RS232; +	input_dev->id.vendor = 0x056a; +	input_dev->id.version = 0x0100; +	input_dev->dev.parent = &serio->dev; + +	input_dev->open = w8001_open; +	input_dev->close = w8001_close; + +	input_set_drvdata(input_dev, w8001); +  	err = input_register_device(w8001->dev);  	if (err)  		goto fail3; @@ -439,15 +593,4 @@ static struct serio_driver w8001_drv = {  	.disconnect	= w8001_disconnect,  }; -static int __init w8001_init(void) -{ -	return serio_register_driver(&w8001_drv); -} - -static void __exit w8001_exit(void) -{ -	serio_unregister_driver(&w8001_drv); -} - -module_init(w8001_init); -module_exit(w8001_exit); +module_serio_driver(w8001_drv);  | 
