From d352c0e1f32e168e20d14d666a73f3712fbdc1f5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 09:28:03 -0800 Subject: Input: sparcspkr - use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sparcspkr.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 6f997aa49183..4a5afc7fe96e 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = { .shutdown = sparcspkr_shutdown, }; +static struct platform_driver * const drivers[] = { + &bbc_beep_driver, + &grover_beep_driver, +}; + static int __init sparcspkr_init(void) { - int err = platform_driver_register(&bbc_beep_driver); - - if (!err) { - err = platform_driver_register(&grover_beep_driver); - if (err) - platform_driver_unregister(&bbc_beep_driver); - } - - return err; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } static void __exit sparcspkr_exit(void) { - platform_driver_unregister(&bbc_beep_driver); - platform_driver_unregister(&grover_beep_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_init(sparcspkr_init); -- cgit v1.2.3 From 86c03f46b9a40b251e4d97f461a13f8b1f18ae17 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 1 Dec 2015 13:31:06 -0800 Subject: Input: wacom_w8001 - use __set_bit for evbits Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Reviewed-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 2792ca397dd0..d194d57c21b6 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -393,7 +393,8 @@ static int w8001_setup(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ - dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); __set_bit(INPUT_PROP_DIRECT, dev->propbit); -- cgit v1.2.3 From ec9acda7360de7ee70cc0db36f41c1fd819a3310 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 1 Dec 2015 13:31:28 -0800 Subject: Input: wacom_w8001 - set BTN_TOOL_DOUBLETAP if we have 2fg support Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Reviewed-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index d194d57c21b6..222006ee23fa 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -471,6 +471,7 @@ static int w8001_setup(struct w8001 *w8001) case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); input_mt_init_slots(dev, 2, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch.x, 0, 0); -- cgit v1.2.3 From e171735410ae5a0ebf90d6bc6a8a97fc28bfc041 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Dec 2015 12:24:26 -0800 Subject: Input: wacom_w8001 - handle touch error case correctly If a device failed at the pen setup and gets a zero reply from the touch device, we need to return an error. Otherwise we have a device with nothing but a name and the EV_KEY and EV_ABS bits. Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 222006ee23fa..9ea5f8226a31 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -385,7 +385,7 @@ static int w8001_setup(struct w8001 *w8001) struct input_dev *dev = w8001->dev; struct w8001_coord coord; struct w8001_touch_query touch; - int error; + int error, err_pen, err_touch; error = w8001_command(w8001, W8001_CMD_STOP, false); if (error) @@ -400,8 +400,8 @@ static int w8001_setup(struct w8001 *w8001) __set_bit(INPUT_PROP_DIRECT, dev->propbit); /* penabled? */ - error = w8001_command(w8001, W8001_CMD_QUERY, true); - if (!error) { + err_pen = w8001_command(w8001, W8001_CMD_QUERY, true); + if (!err_pen) { __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_PEN, dev->keybit); __set_bit(BTN_TOOL_RUBBER, dev->keybit); @@ -426,13 +426,12 @@ static int w8001_setup(struct w8001 *w8001) } /* Touch enabled? */ - error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + err_touch = 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]) { + if (!err_touch && !w8001->response[1]) + err_touch = -ENXIO; + + if (!err_touch) { __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_FINGER, dev->keybit); @@ -491,7 +490,7 @@ static int w8001_setup(struct w8001 *w8001) strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); - return 0; + return !err_pen || !err_touch ? 0 : -ENXIO; } /* -- cgit v1.2.3 From 5d0a4fe2a9347b05bc5db2963cb7cd6cf014a9ed Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Dec 2015 12:25:09 -0800 Subject: Input: wacom_w8001 - split pen and touch initialization up This is preparation work for splitting it up for two event nodes. Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 194 ++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 9ea5f8226a31..de8f0e47fc3d 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -380,12 +380,10 @@ static void w8001_close(struct input_dev *dev) w8001_command(w8001, W8001_CMD_STOP, false); } -static int w8001_setup(struct w8001 *w8001) +static int w8001_detect(struct w8001 *w8001) { struct input_dev *dev = w8001->dev; - struct w8001_coord coord; - struct w8001_touch_query touch; - int error, err_pen, err_touch; + int error; error = w8001_command(w8001, W8001_CMD_STOP, false); if (error) @@ -399,98 +397,121 @@ static int w8001_setup(struct w8001 *w8001) __set_bit(INPUT_PROP_DIRECT, dev->propbit); + return 0; +} + +static int w8001_setup_pen(struct w8001 *w8001) +{ + struct input_dev *dev = w8001->dev; + struct w8001_coord coord; + int error; + /* penabled? */ - err_pen = w8001_command(w8001, W8001_CMD_QUERY, true); - if (!err_pen) { - __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)); + error = w8001_command(w8001, W8001_CMD_QUERY, true); + if (error) + return error; + + __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)); + + return 0; +} + +static int w8001_setup_touch(struct w8001 *w8001) +{ + struct input_dev *dev = w8001->dev; + struct w8001_touch_query touch; + int error; + /* Touch enabled? */ - err_touch = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + if (error) + return error; + /* + * Some non-touch devices may reply to the touch query. But their + * second byte is empty, which indicates touch is not supported. + */ + if (!w8001->response[1]) + return -ENXIO; - if (!err_touch && !w8001->response[1]) - err_touch = -ENXIO; + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); - if (!err_touch) { - __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; - 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; + } - 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); - 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; + 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 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; - - __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); - 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, 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; - } + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + 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, 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; } strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); - return !err_pen || !err_touch ? 0 : -ENXIO; + return 0; } /* @@ -519,7 +540,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) { struct w8001 *w8001; struct input_dev *input_dev; - int err; + int err, err_pen, err_touch; w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); input_dev = input_allocate_device(); @@ -538,10 +559,17 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail2; - err = w8001_setup(w8001); + err = w8001_detect(w8001); if (err) goto fail3; + err_pen = w8001_setup_pen(w8001); + err_touch = w8001_setup_touch(w8001); + if (err_pen && err_touch) { + err = -ENXIO; + goto fail3; + } + input_dev->name = w8001->name; input_dev->phys = w8001->phys; input_dev->id.product = w8001->id; -- cgit v1.2.3 From e0361b70175f0cd6199dd9ed6679632de73973d4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Dec 2015 12:26:19 -0800 Subject: Input: wacom_w8001 - split the touch and pen devices into two devices These devices have a pen device and a touch device through the same serial protocol, split it up into two separate devices like we do for USB Wacom tablets too. Userspace already matches on the device name so we can't drop it completely. Compose the same basename based on capabilities and append the tool type, leading to a name like "Wacom Serial Penabled 2FG Touchscreen Pen". Note that this drops BTN_TOOL_FINGER, it is not needed once the tools are split out (and a touch device with BTN_TOOL_FINGER is interpreted as touchpad by most of userspace). Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 167 ++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index de8f0e47fc3d..fe983e781bca 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -80,7 +80,8 @@ struct w8001_touch_query { */ struct w8001 { - struct input_dev *dev; + struct input_dev *pen_dev; + struct input_dev *touch_dev; struct serio *serio; struct completion cmd_done; int id; @@ -95,7 +96,10 @@ struct w8001 { u16 max_touch_y; u16 max_pen_x; u16 max_pen_y; - char name[64]; + char pen_name[64]; + char touch_name[64]; + int open_count; + struct mutex mutex; }; static void parse_pen_data(u8 *data, struct w8001_coord *coord) @@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001, static void parse_multi_touch(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned char *data = w8001->data; unsigned int x, y; int i; @@ -207,7 +211,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query) static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->pen_dev; /* * We have 1 bit for proximity (rdy) and 3 bits for tip, side, @@ -233,11 +237,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) 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; @@ -261,7 +260,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned int x = coord->x; unsigned int y = coord->y; @@ -271,7 +270,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) 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); @@ -369,20 +367,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command, static int w8001_open(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); + int err; + + err = mutex_lock_interruptible(&w8001->mutex); + if (err) + return err; + + if (w8001->open_count++ == 0) { + err = w8001_command(w8001, W8001_CMD_START, false); + if (err) + w8001->open_count--; + } - return w8001_command(w8001, W8001_CMD_START, false); + mutex_unlock(&w8001->mutex); + return err; } static void w8001_close(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); - w8001_command(w8001, W8001_CMD_STOP, false); + mutex_lock(&w8001->mutex); + + if (--w8001->open_count == 0) + w8001_command(w8001, W8001_CMD_STOP, false); + + mutex_unlock(&w8001->mutex); } static int w8001_detect(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; int error; error = w8001_command(w8001, W8001_CMD_STOP, false); @@ -391,18 +405,13 @@ static int w8001_detect(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ - __set_bit(EV_KEY, dev->evbit); - __set_bit(EV_ABS, dev->evbit); - strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); - - __set_bit(INPUT_PROP_DIRECT, dev->propbit); - return 0; } -static int w8001_setup_pen(struct w8001 *w8001) +static int w8001_setup_pen(struct w8001 *w8001, char *basename, + size_t basename_sz) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->pen_dev; struct w8001_coord coord; int error; @@ -411,11 +420,14 @@ static int w8001_setup_pen(struct w8001 *w8001) if (error) return error; + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); __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); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); parse_pen_data(w8001->response, &coord); w8001->max_pen_x = coord.x; @@ -432,17 +444,19 @@ static int w8001_setup_pen(struct w8001 *w8001) } w8001->id = 0x90; - strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + strlcat(basename, " Penabled", basename_sz); return 0; } -static int w8001_setup_touch(struct w8001 *w8001) +static int w8001_setup_touch(struct w8001 *w8001, char *basename, + size_t basename_sz) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; struct w8001_touch_query touch; int error; + /* Touch enabled? */ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); if (error) @@ -454,8 +468,10 @@ static int w8001_setup_touch(struct w8001 *w8001) if (!w8001->response[1]) return -ENXIO; + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); parse_touchquery(w8001->response, &touch); w8001->max_touch_x = touch.x; @@ -478,14 +494,14 @@ static int w8001_setup_touch(struct w8001 *w8001) case 2: w8001->pktlen = W8001_PKTLEN_TOUCH93; w8001->id = 0x93; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + strlcat(basename, " 1FG", basename_sz); break; case 1: case 3: case 4: w8001->pktlen = W8001_PKTLEN_TOUCH9A; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + strlcat(basename, " 1FG", basename_sz); w8001->id = 0x9a; break; @@ -501,7 +517,7 @@ static int w8001_setup_touch(struct w8001 *w8001) input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); - strlcat(w8001->name, " 2FG", sizeof(w8001->name)); + strlcat(basename, " 2FG", basename_sz); if (w8001->max_pen_x && w8001->max_pen_y) w8001->id = 0xE3; else @@ -509,11 +525,27 @@ static int w8001_setup_touch(struct w8001 *w8001) break; } - strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + strlcat(basename, " Touchscreen", basename_sz); return 0; } +static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, + struct serio *serio) +{ + dev->phys = w8001->phys; + dev->id.bustype = BUS_RS232; + dev->id.product = w8001->id; + dev->id.vendor = 0x056a; + dev->id.version = 0x0100; + dev->open = w8001_open; + dev->close = w8001_close; + + dev->dev.parent = &serio->dev; + + input_set_drvdata(dev, w8001); +} + /* * w8001_disconnect() is the opposite of w8001_connect() */ @@ -524,7 +556,10 @@ static void w8001_disconnect(struct serio *serio) serio_close(serio); - input_unregister_device(w8001->dev); + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); + if (w8001->touch_dev) + input_unregister_device(w8001->touch_dev); kfree(w8001); serio_set_drvdata(serio, NULL); @@ -539,18 +574,23 @@ static void w8001_disconnect(struct serio *serio) static int w8001_connect(struct serio *serio, struct serio_driver *drv) { struct w8001 *w8001; - struct input_dev *input_dev; + struct input_dev *input_dev_pen; + struct input_dev *input_dev_touch; + char basename[64]; int err, err_pen, err_touch; w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!w8001 || !input_dev) { + input_dev_pen = input_allocate_device(); + input_dev_touch = input_allocate_device(); + if (!w8001 || !input_dev_pen || !input_dev_touch) { err = -ENOMEM; goto fail1; } w8001->serio = serio; - w8001->dev = input_dev; + w8001->pen_dev = input_dev_pen; + w8001->touch_dev = input_dev_touch; + mutex_init(&w8001->mutex); init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -563,38 +603,63 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail3; - err_pen = w8001_setup_pen(w8001); - err_touch = w8001_setup_touch(w8001); + /* For backwards-compatibility we compose the basename based on + * capabilities and then just append the tool type + */ + strlcpy(basename, "Wacom Serial", sizeof(basename)); + + err_pen = w8001_setup_pen(w8001, basename, sizeof(basename)); + err_touch = w8001_setup_touch(w8001, basename, sizeof(basename)); if (err_pen && err_touch) { err = -ENXIO; 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; + if (!err_pen) { + strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name)); + strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name)); + input_dev_pen->name = w8001->pen_name; - input_dev->open = w8001_open; - input_dev->close = w8001_close; + w8001_set_devdata(input_dev_pen, w8001, serio); - input_set_drvdata(input_dev, w8001); + err = input_register_device(w8001->pen_dev); + if (err) + goto fail3; + } else { + input_free_device(input_dev_pen); + input_dev_pen = NULL; + w8001->pen_dev = NULL; + } - err = input_register_device(w8001->dev); - if (err) - goto fail3; + if (!err_touch) { + strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name)); + strlcat(w8001->touch_name, " Finger", + sizeof(w8001->touch_name)); + input_dev_touch->name = w8001->touch_name; + + w8001_set_devdata(input_dev_touch, w8001, serio); + + err = input_register_device(w8001->touch_dev); + if (err) + goto fail4; + } else { + input_free_device(input_dev_touch); + input_dev_touch = NULL; + w8001->touch_dev = NULL; + } return 0; +fail4: + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); fail3: serio_close(serio); fail2: serio_set_drvdata(serio, NULL); fail1: - input_free_device(input_dev); + input_free_device(input_dev_pen); + input_free_device(input_dev_touch); kfree(w8001); return err; } -- cgit v1.2.3 From bcf5b3deb0d269a3a28ecf4c73e1c89121ba61d4 Mon Sep 17 00:00:00 2001 From: Sander Vermin Date: Tue, 1 Dec 2015 13:25:05 -0800 Subject: Input: pixcir_i2c - add support for wake and enable gpios On some devices the wake and enable pins of the pixcir touchscreen controller are connected to gpios and these must be controlled by the driver for the device to operate properly. Signed-off-by: Sander Vermin Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/pixcir_i2c_ts.txt | 4 ++- drivers/input/touchscreen/pixcir_i2c_ts.c | 41 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt index 8eb240a287c8..697a3e7831e7 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt @@ -9,7 +9,9 @@ Required properties: - touchscreen-size-y: vertical resolution of touchscreen (in pixels) Optional properties: -- reset-gpio: GPIO connected to the RESET line of the chip +- reset-gpios: GPIO connected to the RESET line of the chip +- enable-gpios: GPIO connected to the ENABLE line of the chip +- wake-gpios: GPIO connected to the WAKE line of the chip Example: diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 4b961ad9f0b5..09523a3d3f23 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data { struct input_dev *input; struct gpio_desc *gpio_attb; struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_enable; + struct gpio_desc *gpio_wake; const struct pixcir_i2c_chip_data *chip; int max_fingers; /* Max fingers supported in this instance */ bool running; @@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, struct device *dev = &ts->client->dev; int ret; + if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 1); + } + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE); if (ret < 0) { dev_err(dev, "%s: can't read reg 0x%x : %d\n", @@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, return ret; } + if (mode == PIXCIR_POWER_HALT) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 0); + } + return 0; } @@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts) struct device *dev = &ts->client->dev; int error; + if (ts->gpio_enable) { + gpiod_set_value_cansleep(ts->gpio_enable, 1); + msleep(100); + } + /* LEVEL_TOUCH interrupt with active low polarity */ error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0); if (error) { @@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts) /* Wait till running ISR is complete */ synchronize_irq(ts->client->irq); + if (ts->gpio_enable) + gpiod_set_value_cansleep(ts->gpio_enable, 0); + return 0; } @@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; } + tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_wake)) { + error = PTR_ERR(tsdata->gpio_wake); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get wake gpio: %d\n", error); + return error; + } + + tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_enable)) { + error = PTR_ERR(tsdata->gpio_enable); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get enable gpio: %d\n", error); + return error; + } + + if (tsdata->gpio_enable) + msleep(100); + error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, tsdata); -- cgit v1.2.3 From f889beaaab1ce2ff9d018302359abb345f49be29 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Sat, 12 Dec 2015 20:43:35 -0800 Subject: Input: da9063 - report KEY_POWER instead of KEY_SLEEP during power key-press Stop reporting KEY_SLEEP for a short key-press and report KEY_POWER instead This change applies to both DA9063 and DA9062 ONKEY drivers. A previous application used for testing by the developer required a KEY_SLEEP and KEY_POWER input_report_key event to distinguish between a short and long key-press of the power key. This is not the general convention and the typical solution is for KEY_POWER to be used in both cases: suspend and S/W power off. Signed-off-by: Steve Twiss Signed-off-by: Dmitry Torokhov --- drivers/input/misc/da9063_onkey.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index 8eb697db82d0..bb863e062b03 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); schedule_delayed_work(&onkey->work, 0); - dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); + dev_dbg(onkey->dev, "KEY_POWER long press.\n"); } else { - input_report_key(onkey->input, KEY_SLEEP, 1); + input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); - input_report_key(onkey->input, KEY_SLEEP, 0); + input_report_key(onkey->input, KEY_POWER, 0); input_sync(onkey->input); - dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); + dev_dbg(onkey->dev, "KEY_POWER short press.\n"); } return IRQ_HANDLED; -- cgit v1.2.3 From 10cf4899f8affa468b9140f377857bfe3f563012 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 11 Nov 2015 11:45:30 -0800 Subject: gpiolib: tighten up ACPI legacy gpio lookups We should not fall back to the legacy unnamed gpio lookup style if the driver requests gpios with different names, because we'll give out the same gpio twice. Let's keep track of the names that were used for the device and only do the fallback for the first name used. Signed-off-by: Dmitry Torokhov Acked-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 43 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 3 +++ drivers/gpio/gpiolib.h | 8 ++++++++ 3 files changed, 54 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 16a7b6816744..7ab7bc3c9300 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -922,3 +922,46 @@ int acpi_gpio_count(struct device *dev, const char *con_id) } return count; } + +struct acpi_crs_lookup { + struct list_head node; + struct acpi_device *adev; + const char *con_id; +}; + +static DEFINE_MUTEX(acpi_crs_lookup_lock); +static LIST_HEAD(acpi_crs_lookup_list); + +bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) +{ + struct acpi_crs_lookup *l, *lookup = NULL; + + /* Never allow fallback if the device has properties */ + if (adev->data.properties || adev->driver_gpios) + return false; + + mutex_lock(&acpi_crs_lookup_lock); + + list_for_each_entry(l, &acpi_crs_lookup_list, node) { + if (l->adev == adev) { + lookup = l; + break; + } + } + + if (!lookup) { + lookup = kmalloc(sizeof(*lookup), GFP_KERNEL); + if (lookup) { + lookup->adev = adev; + lookup->con_id = con_id; + list_add_tail(&lookup->node, &acpi_crs_lookup_list); + } + } + + mutex_unlock(&acpi_crs_lookup_lock); + + return lookup && + ((!lookup->con_id && !con_id) || + (lookup->con_id && con_id && + strcmp(lookup->con_id, con_id) == 0)); +} diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a18f00fc1bb8..3ca6c9f4391e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1868,6 +1868,9 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, /* Then from plain _CRS GPIOs */ if (IS_ERR(desc)) { + if (!acpi_can_fallback_to_crs(adev, con_id)) + return ERR_PTR(-ENOENT); + desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info); if (IS_ERR(desc)) return desc; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 98ab08c0aa2d..2b87cbd68478 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -47,6 +47,8 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, struct acpi_gpio_info *info); int acpi_gpio_count(struct device *dev, const char *con_id); + +bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id); #else static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { } @@ -73,6 +75,12 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id) { return -ENODEV; } + +static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev, + const char *con_id) +{ + return false; +} #endif struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, -- cgit v1.2.3 From 6b0f8f9c52efe24d6dac06ab963b7bd91c723751 Mon Sep 17 00:00:00 2001 From: Böszörményi Zoltán Date: Wed, 16 Dec 2015 11:11:50 -0800 Subject: Input: add eGalaxTouch serial touchscreen driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two EETI touchscreen drivers in the kernel (eeti_ts and egalax_ts) but both are for I2C-connected panels. This is for a different, serial and not multi-touch touchscreen panel. The protocol documentation is at http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf Signed-off-by: Böszörményi Zoltán Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 10 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/egalax_ts_serial.c | 194 +++++++++++++++++++++++++++ include/uapi/linux/serio.h | 1 + 4 files changed, 206 insertions(+) create mode 100644 drivers/input/touchscreen/egalax_ts_serial.c (limited to 'drivers') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ae33da7ab51f..9bcb718668b2 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_EGALAX_SERIAL + tristate "EETI eGalax serial touchscreen" + select SERIO + help + Say Y here to enable support for serial connected EETI + eGalax touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts_serial. + config TOUCHSCREEN_FT6236 tristate "FT6236 I2C touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index cbaa6abb08da..001357c3f73f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c new file mode 100644 index 000000000000..a078c1c2c3f9 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts_serial.c @@ -0,0 +1,194 @@ +/* + * EETI Egalax serial touchscreen driver + * + * Copyright (c) 2015 Zoltán Böszörményi + * + * based on the + * + * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett) + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "EETI Egalax serial touchscreen driver" + +/* + * Definitions & global arrays. + */ + +#define EGALAX_FORMAT_MAX_LENGTH 6 +#define EGALAX_FORMAT_START_BIT BIT(7) +#define EGALAX_FORMAT_PRESSURE_BIT BIT(6) +#define EGALAX_FORMAT_TOUCH_BIT BIT(0) +#define EGALAX_FORMAT_RESOLUTION_MASK 0x06 + +#define EGALAX_MIN_XC 0 +#define EGALAX_MAX_XC 0x4000 +#define EGALAX_MIN_YC 0 +#define EGALAX_MAX_YC 0x4000 + +/* + * Per-touchscreen data. + */ +struct egalax { + struct input_dev *input; + struct serio *serio; + int idx; + u8 data[EGALAX_FORMAT_MAX_LENGTH]; + char phys[32]; +}; + +static void egalax_process_data(struct egalax *egalax) +{ + struct input_dev *dev = egalax->input; + u8 *data = egalax->data; + u16 x, y; + u8 shift; + u8 mask; + + shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1); + mask = 0xff >> (shift + 1); + + x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift; + y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift; + + input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); +} + +static irqreturn_t egalax_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct egalax *egalax = serio_get_drvdata(serio); + int pkt_len; + + egalax->data[egalax->idx++] = data; + + if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) { + pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5; + if (pkt_len == egalax->idx) { + egalax_process_data(egalax); + egalax->idx = 0; + } + } else { + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + egalax->data[0]); + egalax->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * egalax_connect() is the routine that is called when someone adds a + * new serio device that supports egalax protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ +static int egalax_connect(struct serio *serio, struct serio_driver *drv) +{ + struct egalax *egalax; + struct input_dev *input_dev; + int error; + + egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!egalax) { + error = -ENOMEM; + goto err_free_mem; + } + + egalax->serio = serio; + egalax->input = input_dev; + snprintf(egalax->phys, sizeof(egalax->phys), + "%s/input0", serio->phys); + + input_dev->name = "EETI eGalaxTouch Serial TouchScreen"; + input_dev->phys = egalax->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_EGALAX; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0); + + serio_set_drvdata(serio, egalax); + + error = serio_open(serio, drv); + if (error) + goto err_reset_drvdata; + + error = input_register_device(input_dev); + if (error) + goto err_close_serio; + + return 0; + +err_close_serio: + serio_close(serio); +err_reset_drvdata: + serio_set_drvdata(serio, NULL); +err_free_mem: + input_free_device(input_dev); + kfree(egalax); + return error; +} + +static void egalax_disconnect(struct serio *serio) +{ + struct egalax *egalax = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(egalax->input); + kfree(egalax); +} + +/* + * The serio driver structure. + */ + +static const struct serio_device_id egalax_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_EGALAX, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, egalax_serio_ids); + +static struct serio_driver egalax_drv = { + .driver = { + .name = "egalax", + }, + .description = DRIVER_DESC, + .id_table = egalax_serio_ids, + .interrupt = egalax_interrupt, + .connect = egalax_connect, + .disconnect = egalax_disconnect, +}; +module_serio_driver(egalax_drv); + +MODULE_AUTHOR("Zoltán Böszörményi "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h index becdd78295cc..c2ea1698257f 100644 --- a/include/uapi/linux/serio.h +++ b/include/uapi/linux/serio.h @@ -77,5 +77,6 @@ #define SERIO_PS2MULT 0x3c #define SERIO_TSC40 0x3d #define SERIO_WACOM_IV 0x3e +#define SERIO_EGALAX 0x3f #endif /* _UAPI_SERIO_H */ -- cgit v1.2.3 From 40e3be3933aee185fd6ab1ec87dfaf3502d9f5b3 Mon Sep 17 00:00:00 2001 From: Damien Riegel Date: Wed, 16 Dec 2015 11:49:14 -0800 Subject: Input: add touchscreen support for TS-4800 On this board, the touchscreen, an ads7843, is not handled directly by Linux but by a companion FPGA. This FPGA is memory-mapped and the IP design is very similar to the mk712. This commit adds the support for this IP. Signed-off-by: Damien Riegel Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/ts4800-ts.txt | 11 ++ drivers/input/touchscreen/Kconfig | 16 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ts4800-ts.c | 216 +++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt create mode 100644 drivers/input/touchscreen/ts4800-ts.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt new file mode 100644 index 000000000000..4c1c092c276b --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt @@ -0,0 +1,11 @@ +* TS-4800 Touchscreen bindings + +Required properties: +- compatible: must be "technologic,ts4800-ts" +- reg: physical base address of the controller and length of memory mapped + region. +- syscon: phandle / integers array that points to the syscon node which + describes the FPGA's syscon registers. + - phandle to FPGA's syscon + - offset to the touchscreen register + - offset to the touchscreen enable bit diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 9bcb718668b2..f986e8b44b5f 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -937,6 +937,22 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TS4800 + tristate "TS-4800 touchscreen" + depends on HAS_IOMEM && OF + select MFD_SYSCON + select INPUT_POLLDEV + help + Say Y here if you have a touchscreen on a TS-4800 board. + + On TS-4800, the touchscreen is not handled directly by Linux but by + a companion FPGA. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ts4800_ts. + config TOUCHSCREEN_TSC_SERIO tristate "TSC-10/25/40 serial touchscreen support" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 001357c3f73f..968ff12e3132 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c new file mode 100644 index 000000000000..3c3dd78303be --- /dev/null +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -0,0 +1,216 @@ +/* + * Touchscreen driver for the TS-4800 board + * + * Copyright (c) 2015 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* polling interval in ms */ +#define POLL_INTERVAL 3 + +#define DEBOUNCE_COUNT 1 + +/* sensor values are 12-bit wide */ +#define MAX_12BIT ((1 << 12) - 1) + +#define PENDOWN_MASK 0x1 + +#define X_OFFSET 0x0 +#define Y_OFFSET 0x2 + +struct ts4800_ts { + struct input_polled_dev *poll_dev; + struct device *dev; + char phys[32]; + + void __iomem *base; + struct regmap *regmap; + unsigned int reg; + unsigned int bit; + + bool pendown; + int debounce; +}; + +static void ts4800_ts_open(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); + if (ret) + dev_warn(ts->dev, "Failed to enable touchscreen\n"); +} + +static void ts4800_ts_close(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); + if (ret) + dev_warn(ts->dev, "Failed to disable touchscreen\n"); + +} + +static void ts4800_ts_poll(struct input_polled_dev *dev) +{ + struct input_dev *input_dev = dev->input; + struct ts4800_ts *ts = dev->private; + u16 last_x = readw(ts->base + X_OFFSET); + u16 last_y = readw(ts->base + Y_OFFSET); + bool pendown = last_x & PENDOWN_MASK; + + if (pendown) { + if (ts->debounce) { + ts->debounce--; + return; + } + + if (!ts->pendown) { + input_report_key(input_dev, BTN_TOUCH, 1); + ts->pendown = true; + } + + last_x = ((~last_x) >> 4) & MAX_12BIT; + last_y = ((~last_y) >> 4) & MAX_12BIT; + + input_report_abs(input_dev, ABS_X, last_x); + input_report_abs(input_dev, ABS_Y, last_y); + input_sync(input_dev); + } else if (ts->pendown) { + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static int ts4800_parse_dt(struct platform_device *pdev, + struct ts4800_ts *ts) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *syscon_np; + u32 reg, bit; + int error; + + syscon_np = of_parse_phandle(np, "syscon", 0); + if (!syscon_np) { + dev_err(dev, "no syscon property\n"); + return -ENODEV; + } + + error = of_property_read_u32_index(np, "syscon", 1, ®); + if (error < 0) { + dev_err(dev, "no offset in syscon\n"); + return error; + } + + ts->reg = reg; + + error = of_property_read_u32_index(np, "syscon", 2, &bit); + if (error < 0) { + dev_err(dev, "no bit in syscon\n"); + return error; + } + + ts->bit = BIT(bit); + + ts->regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(ts->regmap)) { + dev_err(dev, "cannot get parent's regmap\n"); + return PTR_ERR(ts->regmap); + } + + return 0; +} + +static int ts4800_ts_probe(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev; + struct ts4800_ts *ts; + struct resource *res; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + error = ts4800_parse_dt(pdev, ts); + if (error) + return error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); + + poll_dev = devm_input_allocate_polled_device(&pdev->dev); + if (!poll_dev) + return -ENOMEM; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); + ts->poll_dev = poll_dev; + ts->dev = &pdev->dev; + + poll_dev->private = ts; + poll_dev->poll_interval = POLL_INTERVAL; + poll_dev->open = ts4800_ts_open; + poll_dev->close = ts4800_ts_close; + poll_dev->poll = ts4800_ts_poll; + + poll_dev->input->name = "TS-4800 Touchscreen"; + poll_dev->input->phys = ts->phys; + + input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH); + input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(&pdev->dev, + "Unabled to register polled input device (%d)\n", + error); + return error; + } + + return 0; +} + +static const struct of_device_id ts4800_ts_of_match[] = { + { .compatible = "technologic,ts4800-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ts4800_ts_of_match); + +static struct platform_driver ts4800_ts_driver = { + .driver = { + .name = "ts4800-ts", + .of_match_table = ts4800_ts_of_match, + }, + .probe = ts4800_ts_probe, +}; +module_platform_driver(ts4800_ts_driver); + +MODULE_AUTHOR("Damien Riegel "); +MODULE_DESCRIPTION("TS-4800 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ts4800_ts"); -- cgit v1.2.3 From 0a88d60784440da1ef8f72156cb9547aeebf56cf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 20:29:22 -0800 Subject: Input: psmouse - use switch statement in psmouse_process_byte() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of a series mostly exclusive "if" statements testing protocol type of the mouse let's use "switch" statement. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 65 ++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ad18dab0ac47..4e43e7e70edb 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -138,22 +138,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; -/* - * Full packet accumulated, process it - */ - -/* - * Scroll wheel on IntelliMice, scroll buttons on NetMice - */ + /* Full packet accumulated, process it */ - if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) + switch (psmouse->type) { + case PSMOUSE_IMPS: + /* IntelliMouse has scroll wheel */ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); + break; -/* - * Scroll wheel and buttons on IntelliMouse Explorer - */ - - if (psmouse->type == PSMOUSE_IMEX) { + case PSMOUSE_IMEX: + /* Scroll wheel and buttons on IntelliMouse Explorer */ switch (packet[3] & 0xC0) { case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); @@ -168,39 +162,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); break; } - } + break; -/* - * Extra buttons on Genius NewNet 3D - */ + case PSMOUSE_GENPS: + /* Report scroll buttons on NetMice */ + input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); - if (psmouse->type == PSMOUSE_GENPS) { + /* Extra buttons on Genius NewNet 3D */ input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); - } + break; -/* - * Extra button on ThinkingMouse - */ - if (psmouse->type == PSMOUSE_THINKPS) { + case PSMOUSE_THINKPS: + /* Extra button on ThinkingMouse */ input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); - /* Without this bit of weirdness moving up gives wildly high Y changes. */ + + /* + * Without this bit of weirdness moving up gives wildly + * high Y changes. + */ packet[1] |= (packet[0] & 0x40) << 1; - } + break; -/* - * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first - * byte. - */ - if (psmouse->type == PSMOUSE_CORTRON) { + case PSMOUSE_CORTRON: + /* + * Cortron PS2 Trackball reports SIDE button in the + * 4th bit of the first byte. + */ input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); packet[0] |= 0x08; - } + break; -/* - * Generic PS/2 Mouse - */ + default: + break; + } + /* Generic PS/2 Mouse */ input_report_key(dev, BTN_LEFT, packet[0] & 1); input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); -- cgit v1.2.3 From ad5307715b2d0afed511b2cad9aed530bbbb236b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 20:47:08 -0800 Subject: Input: psmouse - fix comment style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module was using non-standard comment style with comment blocks often starting at the very beginning of a line instead of being aligned with the code. Let's switch to standard formatting. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 279 +++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 155 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4e43e7e70edb..525de2e7c08b 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -129,7 +129,6 @@ struct psmouse_protocol { * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived. */ - psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; @@ -219,7 +218,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, /* * __psmouse_set_state() sets new psmouse state and resets all flags. */ - static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { psmouse->state = new_state; @@ -228,13 +226,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta psmouse->last = jiffies; } - /* * psmouse_set_state() sets new psmouse state and resets all flags and * counters while holding serio lock so fighting with interrupt handler * is not a concern. */ - void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { serio_pause_rx(psmouse->ps2dev.serio); @@ -246,7 +242,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) * psmouse_handle_byte() processes one byte of the input data stream * by calling corresponding protocol handler. */ - static int psmouse_handle_byte(struct psmouse *psmouse) { psmouse_ret_t rc = psmouse->protocol_handler(psmouse); @@ -289,7 +284,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse) * psmouse_interrupt() handles incoming characters, either passing them * for normal processing or gathering them as command response. */ - static irqreturn_t psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { @@ -332,9 +326,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, } psmouse->packet[psmouse->pktcnt++] = data; -/* - * Check if this is a new device announcement (0xAA 0x00) - */ + + /* Check if this is a new device announcement (0xAA 0x00) */ if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { if (psmouse->pktcnt == 1) { psmouse->last = jiffies; @@ -348,9 +341,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, serio_reconnect(serio); goto out; } -/* - * Not a new device, try processing first byte normally - */ + + /* Not a new device, try processing first byte normally */ psmouse->pktcnt = 1; if (psmouse_handle_byte(psmouse)) goto out; @@ -358,9 +350,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, psmouse->packet[psmouse->pktcnt++] = data; } -/* - * See if we need to force resync because mouse was idle for too long - */ + /* + * See if we need to force resync because mouse was idle for + * too long. + */ if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt == 1 && psmouse->resync_time && time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { @@ -377,7 +370,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, return IRQ_HANDLED; } - /* * psmouse_sliced_command() sends an extended PS/2 command to the mouse * using sliced syntax, understood by advanced devices, such as Logitech @@ -401,7 +393,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) return 0; } - /* * psmouse_reset() resets the mouse into power-on state. */ @@ -421,7 +412,6 @@ int psmouse_reset(struct psmouse *psmouse) /* * Here we set the mouse resolution. */ - void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) { static const unsigned char params[] = { 0, 1, 2, 2, 3 }; @@ -438,7 +428,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) /* * Here we set the mouse report rate. */ - static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) { static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; @@ -454,7 +443,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) /* * Here we set the mouse scaling. */ - static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) { ps2_command(&psmouse->ps2dev, NULL, @@ -465,7 +453,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) /* * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. */ - static int psmouse_poll(struct psmouse *psmouse) { return ps2_command(&psmouse->ps2dev, psmouse->packet, @@ -599,7 +586,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) if (param[0] != 4) return -1; -/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ + /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 80; @@ -669,10 +656,10 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) if (!psmouse->name) psmouse->name = "Mouse"; -/* - * We have no way of figuring true number of buttons so let's - * assume that the device has 3. - */ + /* + * We have no way of figuring true number of buttons so let's + * assume that the device has 3. + */ __set_bit(BTN_MIDDLE, psmouse->dev->keybit); } @@ -700,7 +687,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) * Apply default settings to the psmouse structure. Most of them will * be overridden by individual protocol initialization routines. */ - static void psmouse_apply_defaults(struct psmouse *psmouse) { struct input_dev *input_dev = psmouse->dev; @@ -753,13 +739,15 @@ static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have. */ - static int psmouse_extensions(struct psmouse *psmouse, unsigned int max_proto, bool set_properties) { bool synaptics_hardware = false; -/* Always check for focaltech, this is safe as it uses pnp-id matching */ + /* + * Always check for focaltech, this is safe as it uses pnp-id + * matching. + */ if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { if (!set_properties || focaltech_init(psmouse) == 0) { @@ -777,10 +765,10 @@ static int psmouse_extensions(struct psmouse *psmouse, } } -/* - * We always check for lifebook because it does not disturb mouse - * (it only checks DMI information). - */ + /* + * We always check for LifeBook because it does not disturb mouse + * (it only checks DMI information). + */ if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { if (!set_properties || lifebook_init(psmouse) == 0) @@ -795,53 +783,57 @@ static int psmouse_extensions(struct psmouse *psmouse, } } -/* - * Try Kensington ThinkingMouse (we try first, because synaptics probe - * upsets the thinkingmouse). - */ - + /* + * Try Kensington ThinkingMouse (we try first, because Synaptics + * probe upsets the ThinkingMouse). + */ if (max_proto > PSMOUSE_IMEX && psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { return PSMOUSE_THINKPS; } -/* - * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol - * support is disabled in config - we need to know if it is synaptics so we - * can reset it properly after probing for intellimouse. - */ + /* + * Try Synaptics TouchPad. Note that probing is done even if + * Synaptics protocol support is disabled in config - we need to + * know if it is Synaptics so we can reset it properly after + * probing for IntelliMouse. + */ if (max_proto > PSMOUSE_PS2 && psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { -/* - * Try activating protocol, but check if support is enabled first, since - * we try detecting Synaptics even when protocol is disabled. - */ + /* + * Try activating protocol, but check if support is + * enabled first, since we try detecting Synaptics + * even when protocol is disabled. + */ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) && (!set_properties || synaptics_init(psmouse) == 0)) { return PSMOUSE_SYNAPTICS; } -/* - * Some Synaptics touchpads can emulate extended protocols (like IMPS/2). - * Unfortunately Logitech/Genius probes confuse some firmware versions so - * we'll have to skip them. - */ + /* + * Some Synaptics touchpads can emulate extended + * protocols (like IMPS/2). Unfortunately + * Logitech/Genius probes confuse some firmware + * versions so we'll have to skip them. + */ max_proto = PSMOUSE_IMEX; } -/* - * Make sure that touchpad is in relative mode, gestures (taps) are enabled - */ + + /* + * Make sure that touchpad is in relative mode, gestures + * (taps) are enabled. + */ synaptics_reset(psmouse); } -/* - * Try Cypress Trackpad. - * Must try it before Finger Sensing Pad because Finger Sensing Pad probe - * upsets some modules of Cypress Trackpads. - */ + /* + * Try Cypress Trackpad. We must try it before Finger Sensing Pad + * because Finger Sensing Pad probe upsets some modules of Cypress + * Trackpads. + */ if (max_proto > PSMOUSE_IMEX && cypress_detect(psmouse, set_properties) == 0) { if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) { @@ -859,45 +851,34 @@ static int psmouse_extensions(struct psmouse *psmouse, max_proto = PSMOUSE_IMEX; } -/* - * Try ALPS TouchPad - */ + /* Try ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); if (psmouse_do_detect(alps_detect, psmouse, set_properties) == 0) { if (!set_properties || alps_init(psmouse) == 0) return PSMOUSE_ALPS; -/* - * Init failed, try basic relative protocols - */ + + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } } -/* - * Try OLPC HGPK touchpad. - */ + /* Try OLPC HGPK touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { if (!set_properties || hgpk_init(psmouse) == 0) return PSMOUSE_HGPK; -/* - * Init failed, try basic relative protocols - */ + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } -/* - * Try Elantech touchpad. - */ + /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { if (!set_properties || elantech_init(psmouse) == 0) return PSMOUSE_ELANTECH; -/* - * Init failed, try basic relative protocols - */ + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } @@ -919,27 +900,25 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_TOUCHKIT_PS2; } -/* - * Try Finger Sensing Pad. We do it here because its probe upsets - * Trackpoint devices (causing TP_READ_ID command to time out). - */ + /* + * Try Finger Sensing Pad. We do it here because its probe upsets + * Trackpoint devices (causing TP_READ_ID command to time out). + */ if (max_proto > PSMOUSE_IMEX) { if (psmouse_do_detect(fsp_detect, psmouse, set_properties) == 0) { if (!set_properties || fsp_init(psmouse) == 0) return PSMOUSE_FSP; -/* - * Init failed, try basic relative protocols - */ + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } } -/* - * Reset to defaults in case the device got confused by extended - * protocol probes. Note that we follow up with full reset because - * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. - */ + /* + * Reset to defaults in case the device got confused by extended + * protocol probes. Note that we follow up with full reset because + * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); psmouse_reset(psmouse); @@ -955,19 +934,19 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_IMPS; } -/* - * Okay, all failed, we have a standard mouse here. The number of the buttons - * is still a question, though. We assume 3. - */ + /* + * Okay, all failed, we have a standard mouse here. The number of + * the buttons is still a question, though. We assume 3. + */ psmouse_do_detect(ps2bare_detect, psmouse, set_properties); if (synaptics_hardware) { -/* - * We detected Synaptics hardware but it did not respond to IMPS/2 probes. - * We need to reset the touchpad because if there is a track point on the - * pass through port it could get disabled while probing for protocol - * extensions. - */ + /* + * We detected Synaptics hardware but it did not respond to + * IMPS/2 probes. We need to reset the touchpad because if + * there is a track point on the pass through port it could + * get disabled while probing for protocol extensions. + */ psmouse_reset(psmouse); } @@ -1167,19 +1146,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, /* * psmouse_probe() probes for a PS/2 mouse. */ - static int psmouse_probe(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; -/* - * First, we check if it's a mouse. It should send 0x00 or 0x03 - * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. - * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent - * ID queries, probably due to a firmware bug. - */ - + /* + * First, we check if it's a mouse. It should send 0x00 or 0x03 in + * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. + * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and + * subsequent ID queries, probably due to a firmware bug. + */ param[0] = 0xa5; if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) return -1; @@ -1188,10 +1165,10 @@ static int psmouse_probe(struct psmouse *psmouse) param[0] != 0x04 && param[0] != 0xff) return -1; -/* - * Then we reset and disable the mouse so that it doesn't generate events. - */ - + /* + * Then we reset and disable the mouse so that it doesn't generate + * events. + */ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) psmouse_warn(psmouse, "Failed to reset mouse on %s\n", ps2dev->serio->phys); @@ -1202,13 +1179,11 @@ static int psmouse_probe(struct psmouse *psmouse) /* * psmouse_initialize() initializes the mouse to a sane state. */ - static void psmouse_initialize(struct psmouse *psmouse) { -/* - * We set the mouse report rate, resolution and scaling. - */ - + /* + * We set the mouse report rate, resolution and scaling. + */ if (psmouse_max_proto != PSMOUSE_PS2) { psmouse->set_rate(psmouse, psmouse->rate); psmouse->set_resolution(psmouse, psmouse->resolution); @@ -1219,7 +1194,6 @@ static void psmouse_initialize(struct psmouse *psmouse) /* * psmouse_activate() enables the mouse so that we get motion reports from it. */ - int psmouse_activate(struct psmouse *psmouse) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { @@ -1233,10 +1207,9 @@ int psmouse_activate(struct psmouse *psmouse) } /* - * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion - * reports from it unless we explicitly request it. + * psmouse_deactivate() puts the mouse into poll mode so that we don't get + * motion reports from it unless we explicitly request it. */ - int psmouse_deactivate(struct psmouse *psmouse) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { @@ -1249,11 +1222,9 @@ int psmouse_deactivate(struct psmouse *psmouse) return 0; } - /* * psmouse_resync() attempts to re-validate current protocol. */ - static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = @@ -1273,16 +1244,16 @@ static void psmouse_resync(struct work_struct *work) psmouse_deactivate(parent); } -/* - * Some mice don't ACK commands sent while they are in the middle of - * transmitting motion packet. To avoid delay we use ps2_sendbyte() - * instead of ps2_command() which would wait for 200ms for an ACK - * that may never come. - * As an additional quirk ALPS touchpads may not only forget to ACK - * disable command but will stop reporting taps, so if we see that - * mouse at least once ACKs disable we will do full reconnect if ACK - * is missing. - */ + /* + * Some mice don't ACK commands sent while they are in the middle of + * transmitting motion packet. To avoid delay we use ps2_sendbyte() + * instead of ps2_command() which would wait for 200ms for an ACK + * that may never come. + * As an additional quirk ALPS touchpads may not only forget to ACK + * disable command but will stop reporting taps, so if we see that + * mouse at least once ACKs disable we will do full reconnect if ACK + * is missing. + */ psmouse->num_resyncs++; if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { @@ -1291,13 +1262,13 @@ static void psmouse_resync(struct work_struct *work) } else psmouse->acks_disable_command = true; -/* - * Poll the mouse. If it was reset the packet will be shorter than - * psmouse->pktsize and ps2_command will fail. We do not expect and - * do not handle scenario when mouse "upgrades" its protocol while - * disconnected since it would require additional delay. If we ever - * see a mouse that does it we'll adjust the code. - */ + /* + * Poll the mouse. If it was reset the packet will be shorter than + * psmouse->pktsize and ps2_command will fail. We do not expect and + * do not handle scenario when mouse "upgrades" its protocol while + * disconnected since it would require additional delay. If we ever + * see a mouse that does it we'll adjust the code. + */ if (!failed) { if (psmouse->poll(psmouse)) failed = true; @@ -1314,11 +1285,12 @@ static void psmouse_resync(struct work_struct *work) psmouse_set_state(psmouse, PSMOUSE_RESYNCING); } } -/* - * Now try to enable mouse. We try to do that even if poll failed and also - * repeat our attempts 5 times, otherwise we may be left out with disabled - * mouse. - */ + + /* + * Now try to enable mouse. We try to do that even if poll failed + * and also repeat our attempts 5 times, otherwise we may be left + * out with disabled mouse. + */ for (i = 0; i < 5; i++) { if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { enabled = true; @@ -1350,7 +1322,6 @@ static void psmouse_resync(struct work_struct *work) /* * psmouse_cleanup() resets the mouse into power-on state. */ - static void psmouse_cleanup(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); @@ -1375,15 +1346,15 @@ static void psmouse_cleanup(struct serio *serio) if (psmouse->cleanup) psmouse->cleanup(psmouse); -/* - * Reset the mouse to defaults (bare PS/2 protocol). - */ + /* + * Reset the mouse to defaults (bare PS/2 protocol). + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); -/* - * Some boxes, such as HP nx7400, get terribly confused if mouse - * is not fully enabled before suspending/shutting down. - */ + /* + * Some boxes, such as HP nx7400, get terribly confused if mouse + * is not fully enabled before suspending/shutting down. + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); if (parent) { @@ -1399,7 +1370,6 @@ static void psmouse_cleanup(struct serio *serio) /* * psmouse_disconnect() closes and frees. */ - static void psmouse_disconnect(struct serio *serio) { struct psmouse *psmouse, *parent = NULL; @@ -1599,7 +1569,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; } - static int psmouse_reconnect(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); -- cgit v1.2.3 From 2b6f39e9ee7b6a9fa98f6047b05733053876fdbe Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 20:52:36 -0800 Subject: Input: psmouse - rearrange Focaltech init code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fact that we were calling focaltech_init() even when Focaltech support is disabled was confusing. Rearrange the code so that if support is disabled we continue to fall through the rest of protocol probing code until we get to full reset that Focaltech devices need to work properly. Also, replace focaltech_init() with a stub now that it is only called when protocol is enabled. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 22 ++++++---------------- drivers/input/mouse/focaltech.h | 8 ++++++++ drivers/input/mouse/psmouse-base.c | 23 ++++++++++++----------- 3 files changed, 26 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 4d5576de81be..c8c6a8cc329d 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties) return 0; } -static void focaltech_reset(struct psmouse *psmouse) -{ - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - psmouse_reset(psmouse); -} - #ifdef CONFIG_MOUSE_PS2_FOCALTECH /* @@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse) return 0; } +static void focaltech_reset(struct psmouse *psmouse) +{ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + psmouse_reset(psmouse); +} + static void focaltech_disconnect(struct psmouse *psmouse) { focaltech_reset(psmouse); @@ -456,14 +456,4 @@ fail: kfree(priv); return error; } - -#else /* CONFIG_MOUSE_PS2_FOCALTECH */ - -int focaltech_init(struct psmouse *psmouse) -{ - focaltech_reset(psmouse); - - return 0; -} - #endif /* CONFIG_MOUSE_PS2_FOCALTECH */ diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h index ca61ebff373e..783b28e8e10b 100644 --- a/drivers/input/mouse/focaltech.h +++ b/drivers/input/mouse/focaltech.h @@ -18,6 +18,14 @@ #define _FOCALTECH_H int focaltech_detect(struct psmouse *psmouse, bool set_properties); + +#ifdef CONFIG_MOUSE_PS2_FOCALTECH int focaltech_init(struct psmouse *psmouse); +#else +static inline int focaltech_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif #endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 525de2e7c08b..c2bd8665e9f2 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -750,19 +750,20 @@ static int psmouse_extensions(struct psmouse *psmouse, */ if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || focaltech_init(psmouse) == 0) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH)) - return PSMOUSE_FOCALTECH; - /* - * Note that we need to also restrict - * psmouse_max_proto so that psmouse_initialize() - * does not try to reset rate and resolution, - * because even that upsets the device. - */ - psmouse_max_proto = PSMOUSE_PS2; - return PSMOUSE_PS2; + if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && + (!set_properties || focaltech_init(psmouse) == 0)) { + return PSMOUSE_FOCALTECH; } } + /* + * Restrict psmouse_max_proto so that psmouse_initialize() + * does not try to reset rate and resolution, because even + * that upsets the device. + * This also causes us to basically fall through to basic + * protocol detection, where we fully reset the mouse, + * and set it up as bare PS/2 protocol device. + */ + psmouse_max_proto = max_proto = PSMOUSE_PS2; } /* -- cgit v1.2.3 From 24a06f3e3ab640490f49c8e8812d9550d6f15e92 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Dec 2015 16:50:42 -0800 Subject: Input: psmouse - clean up Cypress probe When Cypress protocol support is disabled cypress_init() is a stub that always returns -ENOSYS, so there is not point in testing for CONFIG_MOUSE_PS2_CYPRESS after we decided that we are dealing with a Cypress device. Also, we should only be calling cypress_detect() when set_properties argument is "true", like with other protocols. There is a slight change in behavior to make follow-up patches more uniform: when we detect Cypress but its initialization fails, instead of immediately returning PSMOUSE_PS2 protocol we now continue trying IntelliMouse [Explorer]. Given that Cypress devices only have issue with Sentelic probes probing Imtellimouse should be safe. Reviewed-by: Hans de Goede Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index c2bd8665e9f2..978ba0bb4bd9 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -836,19 +836,15 @@ static int psmouse_extensions(struct psmouse *psmouse, * Trackpads. */ if (max_proto > PSMOUSE_IMEX && - cypress_detect(psmouse, set_properties) == 0) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) { - if (cypress_init(psmouse) == 0) - return PSMOUSE_CYPRESS; - - /* - * Finger Sensing Pad probe upsets some modules of - * Cypress Trackpad, must avoid Finger Sensing Pad - * probe if Cypress Trackpad device detected. - */ - return PSMOUSE_PS2; - } + psmouse_do_detect(cypress_detect, psmouse, set_properties) == 0) { + if (!set_properties || cypress_init(psmouse) == 0) + return PSMOUSE_CYPRESS; + /* + * Finger Sensing Pad probe upsets some modules of + * Cypress Trackpad, must avoid Finger Sensing Pad + * probe if Cypress Trackpad device detected. + */ max_proto = PSMOUSE_IMEX; } -- cgit v1.2.3 From 5fa75cfe23633edf2fd26abe4a08f22ced4415d1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 21:08:28 -0800 Subject: Input: psmouse - move protocol descriptions around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We move protocol descriptions and psmouse_find_by_type() and pmouse_find_by_name() so that we can use them without forward declarations in the subsequent patches. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 379 ++++++++++++++++++------------------- 1 file changed, 189 insertions(+), 190 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 978ba0bb4bd9..131bbc1bd446 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -683,6 +683,195 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) return 0; } +static const struct psmouse_protocol psmouse_protocols[] = { + { + .type = PSMOUSE_PS2, + .name = "PS/2", + .alias = "bare", + .maxproto = true, + .ignore_parity = true, + .detect = ps2bare_detect, + }, +#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP + { + .type = PSMOUSE_PS2PP, + .name = "PS2++", + .alias = "logitech", + .detect = ps2pp_init, + }, +#endif + { + .type = PSMOUSE_THINKPS, + .name = "ThinkPS/2", + .alias = "thinkps", + .detect = thinking_detect, + }, +#ifdef CONFIG_MOUSE_PS2_CYPRESS + { + .type = PSMOUSE_CYPRESS, + .name = "CyPS/2", + .alias = "cypress", + .detect = cypress_detect, + .init = cypress_init, + }, +#endif + { + .type = PSMOUSE_GENPS, + .name = "GenPS/2", + .alias = "genius", + .detect = genius_detect, + }, + { + .type = PSMOUSE_IMPS, + .name = "ImPS/2", + .alias = "imps", + .maxproto = true, + .ignore_parity = true, + .detect = intellimouse_detect, + }, + { + .type = PSMOUSE_IMEX, + .name = "ImExPS/2", + .alias = "exps", + .maxproto = true, + .ignore_parity = true, + .detect = im_explorer_detect, + }, +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS + { + .type = PSMOUSE_SYNAPTICS, + .name = "SynPS/2", + .alias = "synaptics", + .detect = synaptics_detect, + .init = synaptics_init, + }, + { + .type = PSMOUSE_SYNAPTICS_RELATIVE, + .name = "SynRelPS/2", + .alias = "synaptics-relative", + .detect = synaptics_detect, + .init = synaptics_init_relative, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ALPS + { + .type = PSMOUSE_ALPS, + .name = "AlpsPS/2", + .alias = "alps", + .detect = alps_detect, + .init = alps_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_LIFEBOOK + { + .type = PSMOUSE_LIFEBOOK, + .name = "LBPS/2", + .alias = "lifebook", + .init = lifebook_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_TRACKPOINT + { + .type = PSMOUSE_TRACKPOINT, + .name = "TPPS/2", + .alias = "trackpoint", + .detect = trackpoint_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_TOUCHKIT + { + .type = PSMOUSE_TOUCHKIT_PS2, + .name = "touchkitPS/2", + .alias = "touchkit", + .detect = touchkit_ps2_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_OLPC + { + .type = PSMOUSE_HGPK, + .name = "OLPC HGPK", + .alias = "hgpk", + .detect = hgpk_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ELANTECH + { + .type = PSMOUSE_ELANTECH, + .name = "ETPS/2", + .alias = "elantech", + .detect = elantech_detect, + .init = elantech_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_SENTELIC + { + .type = PSMOUSE_FSP, + .name = "FSPPS/2", + .alias = "fsp", + .detect = fsp_detect, + .init = fsp_init, + }, +#endif + { + .type = PSMOUSE_CORTRON, + .name = "CortronPS/2", + .alias = "cortps", + .detect = cortron_detect, + }, +#ifdef CONFIG_MOUSE_PS2_FOCALTECH + { + .type = PSMOUSE_FOCALTECH, + .name = "FocalTechPS/2", + .alias = "focaltech", + .detect = focaltech_detect, + .init = focaltech_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_VMMOUSE + { + .type = PSMOUSE_VMMOUSE, + .name = VMMOUSE_PSNAME, + .alias = "vmmouse", + .detect = vmmouse_detect, + .init = vmmouse_init, + }, +#endif + { + .type = PSMOUSE_AUTO, + .name = "auto", + .alias = "any", + .maxproto = true, + }, +}; + +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) + if (psmouse_protocols[i].type == type) + return &psmouse_protocols[i]; + + WARN_ON(1); + return &psmouse_protocols[0]; +} + +static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) +{ + const struct psmouse_protocol *p; + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { + p = &psmouse_protocols[i]; + + if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || + (strlen(p->alias) == len && !strncmp(p->alias, name, len))) + return &psmouse_protocols[i]; + } + + return NULL; +} + /* * Apply default settings to the psmouse structure. Most of them will * be overridden by individual protocol initialization routines. @@ -950,196 +1139,6 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_PS2; } -static const struct psmouse_protocol psmouse_protocols[] = { - { - .type = PSMOUSE_PS2, - .name = "PS/2", - .alias = "bare", - .maxproto = true, - .ignore_parity = true, - .detect = ps2bare_detect, - }, -#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP - { - .type = PSMOUSE_PS2PP, - .name = "PS2++", - .alias = "logitech", - .detect = ps2pp_init, - }, -#endif - { - .type = PSMOUSE_THINKPS, - .name = "ThinkPS/2", - .alias = "thinkps", - .detect = thinking_detect, - }, -#ifdef CONFIG_MOUSE_PS2_CYPRESS - { - .type = PSMOUSE_CYPRESS, - .name = "CyPS/2", - .alias = "cypress", - .detect = cypress_detect, - .init = cypress_init, - }, -#endif - { - .type = PSMOUSE_GENPS, - .name = "GenPS/2", - .alias = "genius", - .detect = genius_detect, - }, - { - .type = PSMOUSE_IMPS, - .name = "ImPS/2", - .alias = "imps", - .maxproto = true, - .ignore_parity = true, - .detect = intellimouse_detect, - }, - { - .type = PSMOUSE_IMEX, - .name = "ImExPS/2", - .alias = "exps", - .maxproto = true, - .ignore_parity = true, - .detect = im_explorer_detect, - }, -#ifdef CONFIG_MOUSE_PS2_SYNAPTICS - { - .type = PSMOUSE_SYNAPTICS, - .name = "SynPS/2", - .alias = "synaptics", - .detect = synaptics_detect, - .init = synaptics_init, - }, - { - .type = PSMOUSE_SYNAPTICS_RELATIVE, - .name = "SynRelPS/2", - .alias = "synaptics-relative", - .detect = synaptics_detect, - .init = synaptics_init_relative, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_ALPS - { - .type = PSMOUSE_ALPS, - .name = "AlpsPS/2", - .alias = "alps", - .detect = alps_detect, - .init = alps_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_LIFEBOOK - { - .type = PSMOUSE_LIFEBOOK, - .name = "LBPS/2", - .alias = "lifebook", - .init = lifebook_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_TRACKPOINT - { - .type = PSMOUSE_TRACKPOINT, - .name = "TPPS/2", - .alias = "trackpoint", - .detect = trackpoint_detect, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_TOUCHKIT - { - .type = PSMOUSE_TOUCHKIT_PS2, - .name = "touchkitPS/2", - .alias = "touchkit", - .detect = touchkit_ps2_detect, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_OLPC - { - .type = PSMOUSE_HGPK, - .name = "OLPC HGPK", - .alias = "hgpk", - .detect = hgpk_detect, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_ELANTECH - { - .type = PSMOUSE_ELANTECH, - .name = "ETPS/2", - .alias = "elantech", - .detect = elantech_detect, - .init = elantech_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_SENTELIC - { - .type = PSMOUSE_FSP, - .name = "FSPPS/2", - .alias = "fsp", - .detect = fsp_detect, - .init = fsp_init, - }, -#endif - { - .type = PSMOUSE_CORTRON, - .name = "CortronPS/2", - .alias = "cortps", - .detect = cortron_detect, - }, -#ifdef CONFIG_MOUSE_PS2_FOCALTECH - { - .type = PSMOUSE_FOCALTECH, - .name = "FocalTechPS/2", - .alias = "focaltech", - .detect = focaltech_detect, - .init = focaltech_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_VMMOUSE - { - .type = PSMOUSE_VMMOUSE, - .name = VMMOUSE_PSNAME, - .alias = "vmmouse", - .detect = vmmouse_detect, - .init = vmmouse_init, - }, -#endif - { - .type = PSMOUSE_AUTO, - .name = "auto", - .alias = "any", - .maxproto = true, - }, -}; - -static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) - if (psmouse_protocols[i].type == type) - return &psmouse_protocols[i]; - - WARN_ON(1); - return &psmouse_protocols[0]; -} - -static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) -{ - const struct psmouse_protocol *p; - int i; - - for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { - p = &psmouse_protocols[i]; - - if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || - (strlen(p->alias) == len && !strncmp(p->alias, name, len))) - return &psmouse_protocols[i]; - } - - return NULL; -} - - /* * psmouse_probe() probes for a PS/2 mouse. */ -- cgit v1.2.3 From c378b5119eb0ea25bfd3348a06b51fca654d9903 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 21:17:40 -0800 Subject: Input: psmouse - factor out common protocol probing code In preparation of limiting protocols that we try on pass-through ports, let's rework initialization code and factor common code into psmouse_try_protocol() that accepts protocol type (instead of detec() function pointer) and can, for most protocols, perform both detection and initialization. Note that this removes option of forcing Lifebook protocol on devices that are not recognized by lifebook_detect() as having the hardware, but I do not recall anyone using this option. Reviewed-by: Hans de Goede Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 164 +++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 78 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 131bbc1bd446..316105e30064 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -767,6 +767,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_LIFEBOOK, .name = "LBPS/2", .alias = "lifebook", + .detect = lifebook_detect, .init = lifebook_init, }, #endif @@ -844,7 +845,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { }, }; -static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type) { int i; @@ -852,6 +853,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type if (psmouse_protocols[i].type == type) return &psmouse_protocols[i]; + return NULL; +} + +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + const struct psmouse_protocol *proto; + + proto = __psmouse_protocol_by_type(type); + if (proto) + return proto; + WARN_ON(1); return &psmouse_protocols[0]; } @@ -910,18 +922,37 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) psmouse->pt_deactivate = NULL; } -/* - * Apply default settings to the psmouse structure and call specified - * protocol detection or initialization routine. - */ -static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, - bool set_properties), - struct psmouse *psmouse, bool set_properties) +static bool psmouse_try_protocol(struct psmouse *psmouse, + enum psmouse_type type, + unsigned int *max_proto, + bool set_properties, bool init_allowed) { + const struct psmouse_protocol *proto; + + proto = __psmouse_protocol_by_type(type); + if (!proto) + return false; + if (set_properties) psmouse_apply_defaults(psmouse); - return detect(psmouse, set_properties); + if (proto->detect(psmouse, set_properties) != 0) + return false; + + if (set_properties && proto->init && init_allowed) { + if (proto->init(psmouse) != 0) { + /* + * We detected device, but init failed. Adjust + * max_proto so we only try standard protocols. + */ + if (*max_proto > PSMOUSE_IMEX) + *max_proto = PSMOUSE_IMEX; + + return false; + } + } + + return true; } /* @@ -937,12 +968,12 @@ static int psmouse_extensions(struct psmouse *psmouse, * Always check for focaltech, this is safe as it uses pnp-id * matching. */ - if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && - (!set_properties || focaltech_init(psmouse) == 0)) { - return PSMOUSE_FOCALTECH; - } + if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH, + &max_proto, set_properties, false)) { + if (max_proto > PSMOUSE_IMEX && + IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && + (!set_properties || focaltech_init(psmouse) == 0)) { + return PSMOUSE_FOCALTECH; } /* * Restrict psmouse_max_proto so that psmouse_initialize() @@ -959,26 +990,21 @@ static int psmouse_extensions(struct psmouse *psmouse, * We always check for LifeBook because it does not disturb mouse * (it only checks DMI information). */ - if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || lifebook_init(psmouse) == 0) - return PSMOUSE_LIFEBOOK; - } - } + if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto, + set_properties, max_proto > PSMOUSE_IMEX)) + return PSMOUSE_LIFEBOOK; - if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || vmmouse_init(psmouse) == 0) - return PSMOUSE_VMMOUSE; - } - } + if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto, + set_properties, max_proto > PSMOUSE_IMEX)) + return PSMOUSE_VMMOUSE; /* * Try Kensington ThinkingMouse (we try first, because Synaptics * probe upsets the ThinkingMouse). */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto, + set_properties, true)) { return PSMOUSE_THINKPS; } @@ -989,7 +1015,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * probing for IntelliMouse. */ if (max_proto > PSMOUSE_PS2 && - psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto, + set_properties, false)) { synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { @@ -1025,64 +1052,48 @@ static int psmouse_extensions(struct psmouse *psmouse, * Trackpads. */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(cypress_detect, psmouse, set_properties) == 0) { - if (!set_properties || cypress_init(psmouse) == 0) - return PSMOUSE_CYPRESS; - - /* - * Finger Sensing Pad probe upsets some modules of - * Cypress Trackpad, must avoid Finger Sensing Pad - * probe if Cypress Trackpad device detected. - */ - max_proto = PSMOUSE_IMEX; + psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto, + set_properties, true)) { + return PSMOUSE_CYPRESS; } /* Try ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - if (psmouse_do_detect(alps_detect, - psmouse, set_properties) == 0) { - if (!set_properties || alps_init(psmouse) == 0) - return PSMOUSE_ALPS; - - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; - } + if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS, + &max_proto, set_properties, true)) + return PSMOUSE_ALPS; } /* Try OLPC HGPK touchpad */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { - if (!set_properties || hgpk_init(psmouse) == 0) - return PSMOUSE_HGPK; - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; + psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto, + set_properties, true)) { + return PSMOUSE_HGPK; } /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { - if (!set_properties || elantech_init(psmouse) == 0) - return PSMOUSE_ELANTECH; - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; + psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, + &max_proto, set_properties, true)) { + return PSMOUSE_ELANTECH; } if (max_proto > PSMOUSE_IMEX) { - if (psmouse_do_detect(genius_detect, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS, + &max_proto, set_properties, true)) return PSMOUSE_GENPS; - if (psmouse_do_detect(ps2pp_init, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP, + &max_proto, set_properties, true)) return PSMOUSE_PS2PP; - if (psmouse_do_detect(trackpoint_detect, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT, + &max_proto, set_properties, true)) return PSMOUSE_TRACKPOINT; - if (psmouse_do_detect(touchkit_ps2_detect, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, + &max_proto, set_properties, true)) return PSMOUSE_TOUCHKIT_PS2; } @@ -1090,14 +1101,10 @@ static int psmouse_extensions(struct psmouse *psmouse, * Try Finger Sensing Pad. We do it here because its probe upsets * Trackpoint devices (causing TP_READ_ID command to time out). */ - if (max_proto > PSMOUSE_IMEX) { - if (psmouse_do_detect(fsp_detect, - psmouse, set_properties) == 0) { - if (!set_properties || fsp_init(psmouse) == 0) - return PSMOUSE_FSP; - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; - } + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_FSP, + &max_proto, set_properties, true)) { + return PSMOUSE_FSP; } /* @@ -1109,14 +1116,14 @@ static int psmouse_extensions(struct psmouse *psmouse, psmouse_reset(psmouse); if (max_proto >= PSMOUSE_IMEX && - psmouse_do_detect(im_explorer_detect, - psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_IMEX, + &max_proto, set_properties, true)) { return PSMOUSE_IMEX; } if (max_proto >= PSMOUSE_IMPS && - psmouse_do_detect(intellimouse_detect, - psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_IMPS, + &max_proto, set_properties, true)) { return PSMOUSE_IMPS; } @@ -1124,7 +1131,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * Okay, all failed, we have a standard mouse here. The number of * the buttons is still a question, though. We assume 3. */ - psmouse_do_detect(ps2bare_detect, psmouse, set_properties); + psmouse_try_protocol(psmouse, PSMOUSE_PS2, + &max_proto, set_properties, true); if (synaptics_hardware) { /* -- cgit v1.2.3 From ec6184b1c717b8768122e25fe6d312f609cc1bb4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 24 Nov 2015 12:58:46 -0800 Subject: Input: psmouse - limit protocols that we try on passthrough ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PS/2 protocol is slow, and using it with pass-through port (where we encapsulate PS/2 into PS/2) is slower yet so it takes quite a bit of time to do full protocol discovery for device attached to a pass-through port. However, so far we have not see anything but trackpoints or basic PS/2 mice on pass-through ports, so let's limit protocols that we probe there to Trackpoint, IntelliMouse Explorer, IntelliMouse, and bare PS/2 protocol, and avoid other extended protocols, such as Synaptics, ALPS, etc. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 316105e30064..f12df771eeb2 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -119,6 +119,7 @@ struct psmouse_protocol { enum psmouse_type type; bool maxproto; bool ignore_parity; /* Protocol should ignore parity errors from KBC */ + bool try_passthru; /* Try protocol also on passthrough ports */ const char *name; const char *alias; int (*detect)(struct psmouse *, bool); @@ -691,6 +692,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = ps2bare_detect, + .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP { @@ -728,6 +730,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = intellimouse_detect, + .try_passthru = true, }, { .type = PSMOUSE_IMEX, @@ -736,6 +739,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = im_explorer_detect, + .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_SYNAPTICS { @@ -777,6 +781,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "TPPS/2", .alias = "trackpoint", .detect = trackpoint_detect, + .try_passthru = true, }, #endif #ifdef CONFIG_MOUSE_PS2_TOUCHKIT @@ -933,6 +938,11 @@ static bool psmouse_try_protocol(struct psmouse *psmouse, if (!proto) return false; + if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU && + !proto->try_passthru) { + return false; + } + if (set_properties) psmouse_apply_defaults(psmouse); -- cgit v1.2.3 From 190e2031e2db542094659cfa55bfc28545458df5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Dec 2015 11:02:39 -0800 Subject: Input: psmouse - rename ps2pp_init() to ps2pp_detect() This makes Logitech PS2++ protocol implementation consistent with the naming in other protocols. Also mark the stub as "static inline" Reviewed-by: Hans de Goede Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/logips2pp.c | 2 +- drivers/input/mouse/logips2pp.h | 4 ++-- drivers/input/mouse/psmouse-base.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 136e222e2a16..422da1cd9e76 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, * that support it. */ -int ps2pp_init(struct psmouse *psmouse, bool set_properties) +int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h index 0c186f0282d9..bf629453e095 100644 --- a/drivers/input/mouse/logips2pp.h +++ b/drivers/input/mouse/logips2pp.h @@ -12,9 +12,9 @@ #define _LOGIPS2PP_H #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP -int ps2pp_init(struct psmouse *psmouse, bool set_properties); +int ps2pp_detect(struct psmouse *psmouse, bool set_properties); #else -inline int ps2pp_init(struct psmouse *psmouse, bool set_properties) +static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f12df771eeb2..b9e4ee34c132 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -699,7 +699,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_PS2PP, .name = "PS2++", .alias = "logitech", - .detect = ps2pp_init, + .detect = ps2pp_detect, }, #endif { -- cgit v1.2.3 From a779fbc6c931007559b74fd74c2dc7b1c25bac33 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 15:55:21 -0800 Subject: Input: goodix - use actual config length for each device type Each of the Goodix devices supported by this driver has a fixed size for the configuration information registers. The size varies depending on the device and is specified in the datasheet. Use the proper configuration length as specified in the datasheet for each device model, so we do not read more than the actual size of the configuration registers. Signed-off-by: Irina Tirdea Acked-by: Bastien Nocera Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 4d113c9e4b77..5479aa17dba8 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -36,6 +36,7 @@ struct goodix_ts_data { unsigned int max_touch_num; unsigned int int_trigger_type; bool rotated_screen; + int cfg_len; }; #define GOODIX_MAX_HEIGHT 4096 @@ -45,6 +46,8 @@ struct goodix_ts_data { #define GOODIX_MAX_CONTACTS 10 #define GOODIX_CONFIG_MAX_LENGTH 240 +#define GOODIX_CONFIG_911_LENGTH 186 +#define GOODIX_CONFIG_967_LENGTH 228 /* Register defines */ #define GOODIX_READ_COOR_ADDR 0x814E @@ -115,6 +118,25 @@ static int goodix_i2c_read(struct i2c_client *client, return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); } +static int goodix_get_cfg_len(u16 id) +{ + switch (id) { + case 911: + case 9271: + case 9110: + case 927: + case 928: + return GOODIX_CONFIG_911_LENGTH; + + case 912: + case 967: + return GOODIX_CONFIG_967_LENGTH; + + default: + return GOODIX_CONFIG_MAX_LENGTH; + } +} + static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) { int touch_num; @@ -230,8 +252,7 @@ static void goodix_read_config(struct goodix_ts_data *ts) int error; error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, - config, - GOODIX_CONFIG_MAX_LENGTH); + config, ts->cfg_len); if (error) { dev_warn(&ts->client->dev, "Error reading config (%d), using defaults\n", @@ -398,6 +419,8 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } + ts->cfg_len = goodix_get_cfg_len(id_info); + goodix_read_config(ts); error = goodix_request_input_dev(ts, version_info, id_info); -- cgit v1.2.3 From ec6e1b4082d9f5b0858ce33169a1c22a27a982f6 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 15:57:34 -0800 Subject: Input: goodix - reset device at init After power on, it is recommended that the driver resets the device. The reset procedure timing is described in the datasheet and is used at device init (before writing device configuration) and for power management. It is a sequence of setting the interrupt and reset pins high/low at specific timing intervals. This procedure also includes setting the slave address to the one specified in the ACPI/device tree. This is based on Goodix datasheets for GT911 and GT9271 and on Goodix driver gt9xx.c for Android (publicly available in Android kernel trees for various devices). For reset the driver needs to control the interrupt and reset gpio pins (configured through ACPI/device tree). For devices that do not have the gpio pins properly declared, the functionality depending on these pins will not be available, but the device can still be used with basic functionality. For both device tree and ACPI, the interrupt gpio pin configuration is read from the "irq-gpios" property and the reset pin configuration is read from the "reset-gpios" property. For ACPI 5.1, named properties can be specified using the _DSD section. This functionality will not be available for devices that use indexed gpio pins declared in the _CRS section (we need to provide backward compatibility with devices that do not support using the interrupt gpio pin as output). For ACPI, the pins can be specified using ACPI 5.1: Device (STAC) { Name (_HID, "GDIX1001") ... Method (_CRS, 0, Serialized) { Name (RBUF, ResourceTemplate () { I2cSerialBus (0x0014, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\I2C0", 0x00, ResourceConsumer, , ) GpioInt (Edge, ActiveHigh, Exclusive, PullNone, 0x0000, "\\I2C0", 0x00, ResourceConsumer, , ) { // Pin list 0 } GpioIo (Exclusive, PullDown, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\I2C0", 0x00, ResourceConsumer, , ) { 1 } }) Return (RBUF) } Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package (2) {"irq-gpios", Package() {^STAC, 0, 0, 0 }}, Package (2) {"reset-gpios", Package() {^STAC, 1, 0, 0 }}, ... } } Signed-off-by: Octavian Purdila Signed-off-by: Irina Tirdea Acked-by: Rob Herring Acked-by: Bastien Nocera Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/goodix.txt | 9 ++ drivers/input/touchscreen/Kconfig | 1 + drivers/input/touchscreen/goodix.c | 119 +++++++++++++++++++++ 3 files changed, 129 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt index 8ba98eec765b..c42d2cebac8e 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt @@ -13,6 +13,12 @@ Required properties: - interrupt-parent : Interrupt controller to which the chip is connected - interrupts : Interrupt to which the chip is connected +Optional properties: + + - irq-gpios : GPIO pin used for IRQ. The driver uses the + interrupt gpio pin as output to reset the device. + - reset-gpios : GPIO pin used for reset + Example: i2c@00000000 { @@ -23,6 +29,9 @@ Example: reg = <0x5d>; interrupt-parent = <&gpio>; interrupts = <0 0>; + + irq-gpios = <&gpio1 0 0>; + reset-gpios = <&gpio1 1 0>; }; /* ... */ diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ae33da7ab51f..709527cd4c2e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -324,6 +324,7 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" depends on I2C + depends on GPIOLIB help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 5479aa17dba8..6ad379aace72 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -37,8 +38,13 @@ struct goodix_ts_data { unsigned int int_trigger_type; bool rotated_screen; int cfg_len; + struct gpio_desc *gpiod_int; + struct gpio_desc *gpiod_rst; }; +#define GOODIX_GPIO_INT_NAME "irq" +#define GOODIX_GPIO_RST_NAME "reset" + #define GOODIX_MAX_HEIGHT 4096 #define GOODIX_MAX_WIDTH 4096 #define GOODIX_INT_TRIGGER 1 @@ -239,6 +245,106 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int goodix_int_sync(struct goodix_ts_data *ts) +{ + int error; + + error = gpiod_direction_output(ts->gpiod_int, 0); + if (error) + return error; + + msleep(50); /* T5: 50ms */ + + error = gpiod_direction_input(ts->gpiod_int); + if (error) + return error; + + return 0; +} + +/** + * goodix_reset - Reset device during power on + * + * @ts: goodix_ts_data pointer + */ +static int goodix_reset(struct goodix_ts_data *ts) +{ + int error; + + /* begin select I2C slave addr */ + error = gpiod_direction_output(ts->gpiod_rst, 0); + if (error) + return error; + + msleep(20); /* T2: > 10ms */ + + /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ + error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14); + if (error) + return error; + + usleep_range(100, 2000); /* T3: > 100us */ + + error = gpiod_direction_output(ts->gpiod_rst, 1); + if (error) + return error; + + usleep_range(6000, 10000); /* T4: > 5ms */ + + /* end select I2C slave addr */ + error = gpiod_direction_input(ts->gpiod_rst); + if (error) + return error; + + error = goodix_int_sync(ts); + if (error) + return error; + + return 0; +} + +/** + * goodix_get_gpio_config - Get GPIO config from ACPI/DT + * + * @ts: goodix_ts_data pointer + */ +static int goodix_get_gpio_config(struct goodix_ts_data *ts) +{ + int error; + struct device *dev; + struct gpio_desc *gpiod; + + if (!ts->client) + return -EINVAL; + dev = &ts->client->dev; + + /* Get the interrupt GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_INT_NAME, error); + return error; + } + + ts->gpiod_int = gpiod; + + /* Get the reset line GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_RST_NAME, error); + return error; + } + + ts->gpiod_rst = gpiod; + + return 0; +} + /** * goodix_read_config - Read the embedded configuration of the panel * @@ -407,6 +513,19 @@ static int goodix_ts_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + error = goodix_get_gpio_config(ts); + if (error) + return error; + + if (ts->gpiod_int && ts->gpiod_rst) { + /* reset the controller */ + error = goodix_reset(ts); + if (error) { + dev_err(&client->dev, "Controller reset failed.\n"); + return error; + } + } + error = goodix_i2c_test(client); if (error) { dev_err(&client->dev, "I2C communication failure: %d\n", error); -- cgit v1.2.3 From 68caf85881cd842b59d5e2124a236ecce0389a73 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 16:05:42 -0800 Subject: Input: goodix - write configuration data to device Goodix devices can be configured by writing custom data to the device at init. The configuration data is read with request_firmware from "goodix__cfg.bin", where is the product id read from the device (e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for GT9271). The configuration information has a specific format described in the Goodix datasheet. It includes X/Y resolution, maximum supported touch points, interrupt flags, various sensitivity factors and settings for advanced features (like gesture recognition). Before writing the firmware, it is necessary to reset the device. If the device ACPI/DT information does not declare gpio pins (needed for reset), writing the firmware will not be available for these devices. This is based on Goodix datasheets for GT911 and GT9271 and on Goodix driver gt9xx.c for Android (publicly available in Android kernel trees for various devices). Signed-off-by: Octavian Purdila Signed-off-by: Irina Tirdea Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 244 ++++++++++++++++++++++++++++++++----- 1 file changed, 212 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 6ad379aace72..cd92d8ff3059 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,10 @@ struct goodix_ts_data { int cfg_len; struct gpio_desc *gpiod_int; struct gpio_desc *gpiod_rst; + u16 id; + u16 version; + const char *cfg_name; + struct completion firmware_loading_complete; }; #define GOODIX_GPIO_INT_NAME "irq" @@ -124,6 +129,39 @@ static int goodix_i2c_read(struct i2c_client *client, return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); } +/** + * goodix_i2c_write - write data to a register of the i2c slave device. + * + * @client: i2c device. + * @reg: the register to write to. + * @buf: raw data buffer to write. + * @len: length of the buffer to write + */ +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, + unsigned len) +{ + u8 *addr_buf; + struct i2c_msg msg; + int ret; + + addr_buf = kmalloc(len + 2, GFP_KERNEL); + if (!addr_buf) + return -ENOMEM; + + addr_buf[0] = reg >> 8; + addr_buf[1] = reg & 0xFF; + memcpy(&addr_buf[2], buf, len); + + msg.flags = 0; + msg.addr = client->addr; + msg.buf = addr_buf; + msg.len = len + 2; + + ret = i2c_transfer(client->adapter, &msg, 1); + kfree(addr_buf); + return ret < 0 ? ret : (ret != 1 ? -EIO : 0); +} + static int goodix_get_cfg_len(u16 id) { switch (id) { @@ -245,6 +283,73 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * goodix_check_cfg - Checks if config fw is valid + * + * @ts: goodix_ts_data pointer + * @cfg: firmware config data + */ +static int goodix_check_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int i, raw_cfg_len; + u8 check_sum = 0; + + if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) { + dev_err(&ts->client->dev, + "The length of the config fw is not correct"); + return -EINVAL; + } + + raw_cfg_len = cfg->size - 2; + for (i = 0; i < raw_cfg_len; i++) + check_sum += cfg->data[i]; + check_sum = (~check_sum) + 1; + if (check_sum != cfg->data[raw_cfg_len]) { + dev_err(&ts->client->dev, + "The checksum of the config fw is not correct"); + return -EINVAL; + } + + if (cfg->data[raw_cfg_len + 1] != 1) { + dev_err(&ts->client->dev, + "Config fw must have Config_Fresh register set"); + return -EINVAL; + } + + return 0; +} + +/** + * goodix_send_cfg - Write fw config to device + * + * @ts: goodix_ts_data pointer + * @cfg: config firmware to write to device + */ +static int goodix_send_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int error; + + error = goodix_check_cfg(ts, cfg); + if (error) + return error; + + error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data, + cfg->size); + if (error) { + dev_err(&ts->client->dev, "Failed to write config data: %d", + error); + return error; + } + dev_dbg(&ts->client->dev, "Config sent successfully."); + + /* Let the firmware reconfigure itself, so sleep for 10ms */ + usleep_range(10000, 11000); + + return 0; +} + static int goodix_int_sync(struct goodix_ts_data *ts) { int error; @@ -391,30 +496,29 @@ static void goodix_read_config(struct goodix_ts_data *ts) /** * goodix_read_version - Read goodix touchscreen version * - * @client: the i2c client - * @version: output buffer containing the version on success - * @id: output buffer containing the id on success + * @ts: our goodix_ts_data pointer */ -static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id) +static int goodix_read_version(struct goodix_ts_data *ts) { int error; u8 buf[6]; char id_str[5]; - error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf)); + error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf)); if (error) { - dev_err(&client->dev, "read version failed: %d\n", error); + dev_err(&ts->client->dev, "read version failed: %d\n", error); return error; } memcpy(id_str, buf, 4); id_str[4] = 0; - if (kstrtou16(id_str, 10, id)) - *id = 0x1001; + if (kstrtou16(id_str, 10, &ts->id)) + ts->id = 0x1001; - *version = get_unaligned_le16(&buf[4]); + ts->version = get_unaligned_le16(&buf[4]); - dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version); + dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id, + ts->version); return 0; } @@ -448,13 +552,10 @@ static int goodix_i2c_test(struct i2c_client *client) * goodix_request_input_dev - Allocate, populate and register the input device * * @ts: our goodix_ts_data pointer - * @version: device firmware version - * @id: device ID * * Must be called during probe */ -static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, - u16 id) +static int goodix_request_input_dev(struct goodix_ts_data *ts) { int error; @@ -478,8 +579,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, ts->input_dev->phys = "input/ts"; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0x0416; - ts->input_dev->id.product = id; - ts->input_dev->id.version = version; + ts->input_dev->id.product = ts->id; + ts->input_dev->id.version = ts->version; error = input_register_device(ts->input_dev); if (error) { @@ -491,13 +592,71 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, return 0; } +/** + * goodix_configure_dev - Finish device initialization + * + * @ts: our goodix_ts_data pointer + * + * Must be called from probe to finish initialization of the device. + * Contains the common initialization code for both devices that + * declare gpio pins and devices that do not. It is either called + * directly from probe or from request_firmware_wait callback. + */ +static int goodix_configure_dev(struct goodix_ts_data *ts) +{ + int error; + unsigned long irq_flags; + + goodix_read_config(ts); + + error = goodix_request_input_dev(ts); + if (error) + return error; + + irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + irq_flags, ts->client->name, ts); + if (error) { + dev_err(&ts->client->dev, "request IRQ failed: %d\n", error); + return error; + } + + return 0; +} + +/** + * goodix_config_cb - Callback to finish device init + * + * @ts: our goodix_ts_data pointer + * + * request_firmware_wait callback that finishes + * initialization of the device. + */ +static void goodix_config_cb(const struct firmware *cfg, void *ctx) +{ + struct goodix_ts_data *ts = ctx; + int error; + + if (cfg) { + /* send device configuration to the firmware */ + error = goodix_send_cfg(ts, cfg); + if (error) + goto err_release_cfg; + } + + goodix_configure_dev(ts); + +err_release_cfg: + release_firmware(cfg); + complete_all(&ts->firmware_loading_complete); +} + static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; - unsigned long irq_flags; int error; - u16 version_info, id_info; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -512,6 +671,7 @@ static int goodix_ts_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + init_completion(&ts->firmware_loading_complete); error = goodix_get_gpio_config(ts); if (error) @@ -532,32 +692,51 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } - error = goodix_read_version(client, &version_info, &id_info); + error = goodix_read_version(ts); if (error) { dev_err(&client->dev, "Read version failed.\n"); return error; } - ts->cfg_len = goodix_get_cfg_len(id_info); - - goodix_read_config(ts); + ts->cfg_len = goodix_get_cfg_len(ts->id); - error = goodix_request_input_dev(ts, version_info, id_info); - if (error) - return error; + if (ts->gpiod_int && ts->gpiod_rst) { + /* update device config */ + ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL, + "goodix_%d_cfg.bin", ts->id); + if (!ts->cfg_name) + return -ENOMEM; + + error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name, + &client->dev, GFP_KERNEL, ts, + goodix_config_cb); + if (error) { + dev_err(&client->dev, + "Failed to invoke firmware loader: %d\n", + error); + return error; + } - irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; - error = devm_request_threaded_irq(&ts->client->dev, client->irq, - NULL, goodix_ts_irq_handler, - irq_flags, client->name, ts); - if (error) { - dev_err(&client->dev, "request IRQ failed: %d\n", error); - return error; + return 0; + } else { + error = goodix_configure_dev(ts); + if (error) + return error; } return 0; } +static int goodix_ts_remove(struct i2c_client *client) +{ + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->gpiod_int && ts->gpiod_rst) + wait_for_completion(&ts->firmware_loading_complete); + + return 0; +} + static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } @@ -588,6 +767,7 @@ MODULE_DEVICE_TABLE(of, goodix_of_match); static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, + .remove = goodix_ts_remove, .id_table = goodix_ts_id, .driver = { .name = "Goodix-TS", -- cgit v1.2.3 From 5ab09d6a8f6406134085fb3f30ab61968c6f1ddf Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 16:43:39 -0800 Subject: Input: goodix - add power management support Implement suspend/resume for goodix driver. The suspend and resume process uses the gpio pins. If the device ACPI/DT information does not declare gpio pins, suspend/resume will not be available for these devices. This is based on Goodix datasheets for GT911 and GT9271 and on Goodix driver gt9xx.c for Android (publicly available in Android kernel trees for various devices). Signed-off-by: Octavian Purdila Signed-off-by: Irina Tirdea Tested-by: Bastien Nocera Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 103 +++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index cd92d8ff3059..34ed68679853 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -45,6 +45,7 @@ struct goodix_ts_data { u16 version; const char *cfg_name; struct completion firmware_loading_complete; + unsigned long irq_flags; }; #define GOODIX_GPIO_INT_NAME "irq" @@ -61,6 +62,9 @@ struct goodix_ts_data { #define GOODIX_CONFIG_967_LENGTH 228 /* Register defines */ +#define GOODIX_REG_COMMAND 0x8040 +#define GOODIX_CMD_SCREEN_OFF 0x05 + #define GOODIX_READ_COOR_ADDR 0x814E #define GOODIX_REG_CONFIG_DATA 0x8047 #define GOODIX_REG_ID 0x8140 @@ -162,6 +166,11 @@ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, return ret < 0 ? ret : (ret != 1 ? -EIO : 0); } +static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) +{ + return goodix_i2c_write(client, reg, &value, sizeof(value)); +} + static int goodix_get_cfg_len(u16 id) { switch (id) { @@ -283,6 +292,18 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static void goodix_free_irq(struct goodix_ts_data *ts) +{ + devm_free_irq(&ts->client->dev, ts->client->irq, ts); +} + +static int goodix_request_irq(struct goodix_ts_data *ts) +{ + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + ts->irq_flags, ts->client->name, ts); +} + /** * goodix_check_cfg - Checks if config fw is valid * @@ -605,7 +626,6 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) static int goodix_configure_dev(struct goodix_ts_data *ts) { int error; - unsigned long irq_flags; goodix_read_config(ts); @@ -613,10 +633,8 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) if (error) return error; - irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; - error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq, - NULL, goodix_ts_irq_handler, - irq_flags, ts->client->name, ts); + ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = goodix_request_irq(ts); if (error) { dev_err(&ts->client->dev, "request IRQ failed: %d\n", error); return error; @@ -737,6 +755,80 @@ static int goodix_ts_remove(struct i2c_client *client) return 0; } +static int __maybe_unused goodix_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; + + /* We need gpio pins to suspend/resume */ + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + wait_for_completion(&ts->firmware_loading_complete); + + /* Free IRQ as IRQ pin is used as output in the suspend sequence */ + goodix_free_irq(ts); + + /* Output LOW on the INT pin for 5 ms */ + error = gpiod_direction_output(ts->gpiod_int, 0); + if (error) { + goodix_request_irq(ts); + return error; + } + + usleep_range(5000, 6000); + + error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND, + GOODIX_CMD_SCREEN_OFF); + if (error) { + dev_err(&ts->client->dev, "Screen off command failed\n"); + gpiod_direction_input(ts->gpiod_int); + goodix_request_irq(ts); + return -EAGAIN; + } + + /* + * The datasheet specifies that the interval between sending screen-off + * command and wake-up should be longer than 58 ms. To avoid waking up + * sooner, delay 58ms here. + */ + msleep(58); + return 0; +} + +static int __maybe_unused goodix_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; + + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + /* + * Exit sleep mode by outputting HIGH level to INT pin + * for 2ms~5ms. + */ + error = gpiod_direction_output(ts->gpiod_int, 1); + if (error) + return error; + + usleep_range(2000, 5000); + + error = goodix_int_sync(ts); + if (error) + return error; + + error = goodix_request_irq(ts); + if (error) + return error; + + return 0; +} + +static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume); + static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } @@ -773,6 +865,7 @@ static struct i2c_driver goodix_ts_driver = { .name = "Goodix-TS", .acpi_match_table = ACPI_PTR(goodix_acpi_match), .of_match_table = of_match_ptr(goodix_of_match), + .pm = &goodix_pm_ops, }, }; module_i2c_driver(goodix_ts_driver); -- cgit v1.2.3 From 5d655b35466835c6bb8774122db95ecb4e18888d Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 16:47:42 -0800 Subject: Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send Use goodix_i2c_write_u8 instead of i2c_master_send to simplify code. Signed-off-by: Irina Tirdea Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 34ed68679853..0acefe49540d 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -277,16 +277,11 @@ static void goodix_process_events(struct goodix_ts_data *ts) */ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) { - static const u8 end_cmd[] = { - GOODIX_READ_COOR_ADDR >> 8, - GOODIX_READ_COOR_ADDR & 0xff, - 0 - }; struct goodix_ts_data *ts = dev_id; goodix_process_events(ts); - if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0) + if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0) dev_err(&ts->client->dev, "I2C write end_cmd error\n"); return IRQ_HANDLED; -- cgit v1.2.3 From ad48cf5e9597147bb2bb526a6d379ee88970dec8 Mon Sep 17 00:00:00 2001 From: Karsten Merker Date: Thu, 17 Dec 2015 17:02:53 -0800 Subject: Input: goodix - add axis swapping and axis inversion support Implement support for the following device-tree and ACPI 5.1 DSD properties in the goodix touchscreen driver: - touchscreen-inverted-x: X axis is inverted (boolean) - touchscreen-inverted-y: Y axis is inverted (boolean) - touchscreen-swapped-x-y: X and Y axis are swapped (boolean) These are necessary on tablets which have a display in portrait format while the touchscreen is in landscape format, such as e.g. the MSI Primo 81. Signed-off-by: Karsten Merker Tested-by: Bastien Nocera Tested-by: Irina Tirdea (with ACPI DSD properties) Tested-by: Aleksei Mamlin (with device-tree properties) Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/goodix.txt | 5 +++++ drivers/input/touchscreen/goodix.c | 25 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt index c42d2cebac8e..c98757a69110 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt @@ -19,6 +19,11 @@ Optional properties: interrupt gpio pin as output to reset the device. - reset-gpios : GPIO pin used for reset + - touchscreen-inverted-x : X axis is inverted (boolean) + - touchscreen-inverted-y : Y axis is inverted (boolean) + - touchscreen-swapped-x-y : X and Y axis are swapped (boolean) + (swapping is done after inverting the axis) + Example: i2c@00000000 { diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 0acefe49540d..b5e910a44cdf 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -2,6 +2,7 @@ * Driver for Goodix Touchscreens * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2015 K. Merker * * This code is based on gt9xx.c authored by andrew@goodix.com: * @@ -35,6 +36,9 @@ struct goodix_ts_data { struct input_dev *input_dev; int abs_x_max; int abs_y_max; + bool swapped_x_y; + bool inverted_x; + bool inverted_y; unsigned int max_touch_num; unsigned int int_trigger_type; bool rotated_screen; @@ -235,6 +239,14 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) input_y = ts->abs_y_max - input_y; } + /* Inversions have to happen before axis swapping */ + if (ts->inverted_x) + input_x = ts->abs_x_max - input_x; + if (ts->inverted_y) + input_y = ts->abs_y_max - input_y; + if (ts->swapped_x_y) + swap(input_x, input_y); + input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); @@ -486,6 +498,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) error); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = GOODIX_INT_TRIGGER; ts->max_touch_num = GOODIX_MAX_CONTACTS; return; @@ -493,6 +507,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { @@ -500,6 +516,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->max_touch_num = GOODIX_MAX_CONTACTS; } @@ -622,6 +640,13 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) { int error; + ts->swapped_x_y = device_property_read_bool(&ts->client->dev, + "touchscreen-swapped-x-y"); + ts->inverted_x = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-x"); + ts->inverted_y = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-y"); + goodix_read_config(ts); error = goodix_request_input_dev(ts); -- cgit v1.2.3 From 57c80e8e5d1cddae0651e5314394e6069ebbbe3c Mon Sep 17 00:00:00 2001 From: Karsten Merker Date: Thu, 17 Dec 2015 17:08:31 -0800 Subject: Input: goodix - use "inverted_[xy]" flags instead of "rotated_screen" The goodix touchscreen driver uses a "rotated_screen" flag for systems on which the touchscreen is mounted rotated by 180 degrees with respect to the display. With the addition of support for the dt properties "touchscreen-inverted-x" and "touchscreen-inverted-y", a separate "rotated_screen" flag is not necessary anymore. This patch replaces it by setting the inverted_x and inverted_y flags instead. Signed-off-by: Karsten Merker Reviewed-by: Irina Tirdea Tested-by: Bastien Nocera Acked-by: Bastien Nocera Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index b5e910a44cdf..240b16f3ee97 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -41,7 +41,6 @@ struct goodix_ts_data { bool inverted_y; unsigned int max_touch_num; unsigned int int_trigger_type; - bool rotated_screen; int cfg_len; struct gpio_desc *gpiod_int; struct gpio_desc *gpiod_rst; @@ -234,11 +233,6 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) int input_y = get_unaligned_le16(&coor_data[3]); int input_w = get_unaligned_le16(&coor_data[5]); - if (ts->rotated_screen) { - input_x = ts->abs_x_max - input_x; - input_y = ts->abs_y_max - input_y; - } - /* Inversions have to happen before axis swapping */ if (ts->inverted_x) input_x = ts->abs_x_max - input_x; @@ -521,10 +515,12 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->max_touch_num = GOODIX_MAX_CONTACTS; } - ts->rotated_screen = dmi_check_system(rotated_screen); - if (ts->rotated_screen) + if (dmi_check_system(rotated_screen)) { + ts->inverted_x = true; + ts->inverted_y = true; dev_dbg(&ts->client->dev, "Applying '180 degrees rotated screen' quirk\n"); + } } /** -- cgit v1.2.3 From 052876f8e5aec887d22c4d06e54aa5531ffcec75 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 18 Dec 2015 17:20:09 -0800 Subject: Input: uinput - add new UINPUT_DEV_SETUP and UI_ABS_SETUP ioctl This adds two new ioctls, UINPUT_DEV_SETUP and UI_ABS_SETUP, that replaces the old device setup method (by write()'ing "struct uinput_user_dev" to the node). The old method is not easily extendable and requires huge payloads. Furthermore, overloading write() without properly versioned objects is error-prone. Therefore, we introduce two new ioctls to replace the old method. These ioctls support all features of the old method, plus a "resolution" field for absinfo. Furthermore, it's properly forward-compatible to new ABS codes and a growing "struct input_absinfo" structure. UI_ABS_SETUP also allows user-space to skip unknown axes if not set. There is no need to copy the whole array temporarily into the kernel, but instead the caller issues several ioctl where we copy each value manually. Signed-off-by: David Herrmann Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 86 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/uinput.h | 5 +++ include/uapi/linux/uinput.h | 83 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 5adbcedcb81c..a16fc4a4bb1f 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -370,8 +370,78 @@ static int uinput_allocate_device(struct uinput_device *udev) return 0; } -static int uinput_setup_device(struct uinput_device *udev, - const char __user *buffer, size_t count) +static int uinput_dev_setup(struct uinput_device *udev, + struct uinput_setup __user *arg) +{ + struct uinput_setup setup; + struct input_dev *dev; + int retval; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, sizeof(setup))) + return -EFAULT; + + if (!setup.name[0]) + return -EINVAL; + + dev = udev->dev; + dev->id = setup.id; + udev->ff_effects_max = setup.ff_effects_max; + + kfree(dev->name); + dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); + if (!dev->name) + return -ENOMEM; + + retval = uinput_validate_absbits(dev); + if (retval < 0) + return retval; + + udev->state = UIST_SETUP_COMPLETE; + return 0; +} + +static int uinput_abs_setup(struct uinput_device *udev, + struct uinput_setup __user *arg, size_t size) +{ + struct uinput_abs_setup setup = {}; + struct input_dev *dev; + + if (size > sizeof(setup)) + return -E2BIG; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, size)) + return -EFAULT; + + if (setup.code > ABS_MAX) + return -ERANGE; + + dev = udev->dev; + + input_alloc_absinfo(dev); + if (!dev->absinfo) + return -ENOMEM; + + set_bit(setup.code, dev->absbit); + dev->absinfo[setup.code] = setup.absinfo; + + /* + * We restore the state to UIST_NEW_DEVICE because the user has to call + * UI_DEV_SETUP in the last place before UI_DEV_CREATE to check the + * validity of the absbits. + */ + udev->state = UIST_NEW_DEVICE; + return 0; +} + +/* legacy setup via write() */ +static int uinput_setup_device_legacy(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct uinput_user_dev *user_dev; struct input_dev *dev; @@ -474,7 +544,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, retval = udev->state == UIST_CREATED ? uinput_inject_events(udev, buffer, count) : - uinput_setup_device(udev, buffer, count); + uinput_setup_device_legacy(udev, buffer, count); mutex_unlock(&udev->mutex); @@ -735,6 +805,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, uinput_destroy_device(udev); goto out; + case UI_DEV_SETUP: + retval = uinput_dev_setup(udev, p); + goto out; + + /* UI_ABS_SETUP is handled in the variable size ioctls */ + case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); goto out; @@ -879,6 +955,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, name = dev_name(&udev->dev->dev); retval = uinput_str_to_user(p, name, size); goto out; + + case UI_ABS_SETUP & ~IOCSIZE_MASK: + retval = uinput_abs_setup(udev, p, size); + goto out; } retval = -EINVAL; diff --git a/include/linux/uinput.h b/include/linux/uinput.h index 0994c0d01a09..75de43da2301 100644 --- a/include/linux/uinput.h +++ b/include/linux/uinput.h @@ -20,6 +20,11 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.5 08/13/2015 (David Herrmann & + * Benjamin Tissoires ) + * - add UI_DEV_SETUP ioctl + * - add UI_ABS_SETUP ioctl + * - add UI_GET_VERSION ioctl * 0.4 01/09/2014 (Benjamin Tissoires ) * - add UI_GET_SYSNAME ioctl * 0.3 24/05/2006 (Anssi Hannula ) diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index 013c9d8db372..77b8cf73a178 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -20,6 +20,11 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.5 08/13/2015 (David Herrmann & + * Benjamin Tissoires ) + * - add UI_DEV_SETUP ioctl + * - add UI_ABS_SETUP ioctl + * - add UI_GET_VERSION ioctl * 0.4 01/09/2014 (Benjamin Tissoires ) * - add UI_GET_SYSNAME ioctl * 0.3 24/05/2006 (Anssi Hannula ) @@ -37,8 +42,8 @@ #include #include -#define UINPUT_VERSION 4 - +#define UINPUT_VERSION 5 +#define UINPUT_MAX_NAME_SIZE 80 struct uinput_ff_upload { __u32 request_id; @@ -58,6 +63,79 @@ struct uinput_ff_erase { #define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1) #define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2) +struct uinput_setup { + struct input_id id; + char name[UINPUT_MAX_NAME_SIZE]; + __u32 ff_effects_max; +}; + +/** + * UI_DEV_SETUP - Set device parameters for setup + * + * This ioctl sets parameters for the input device to be created. It must be + * issued *before* calling UI_DEV_CREATE or it will fail. This ioctl supersedes + * the old "struct uinput_user_dev" method, which wrote this data via write(). + * To actually set the absolute axes, you also need to call the ioctl + * UI_ABS_SETUP *before* calling this ioctl. + * + * This ioctl takes a "struct uinput_setup" object as argument. The fields of + * this object are as follows: + * id: See the description of "struct input_id". This field is + * copied unchanged into the new device. + * name: This is used unchanged as name for the new device. + * ff_effects_max: This limits the maximum numbers of force-feedback effects. + * See below for a description of FF with uinput. + * + * This ioctl can be called multiple times and will overwrite previous values. + * If this ioctl fails with -EINVAL, you're recommended to use the old + * "uinput_user_dev" method via write() as fallback, in case you run on an old + * kernel that does not support this ioctl. + * + * This ioctl may fail with -EINVAL if it is not supported or if you passed + * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the + * passed uinput_setup object cannot be read/written. + * If this call fails, partial data may have already been applied to the + * internal device. + */ +#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup) + +struct uinput_abs_setup { + __u16 code; /* axis code */ + /* __u16 filler; */ + struct input_absinfo absinfo; +}; + +/** + * UI_ABS_SETUP - Set absolute axis information for the device to setup + * + * This ioctl sets one absolute axis information for the input device to be + * created. It must be issued *before* calling UI_DEV_SETUP and UI_DEV_CREATE + * for every absolute axis the device exports. + * This ioctl supersedes the old "struct uinput_user_dev" method, which wrote + * part of this data and the content of UI_DEV_SETUP via write(). + * + * This ioctl takes a "struct uinput_abs_setup" object as argument. The fields + * of this object are as follows: + * code: The corresponding input code associated with this axis + * (ABS_X, ABS_Y, etc...) + * absinfo: See "struct input_absinfo" for a description of this field. + * This field is copied unchanged into the kernel for the + * specified axis. If the axis is not enabled via + * UI_SET_ABSBIT, this ioctl will enable it. + * + * This ioctl can be called multiple times and will overwrite previous values. + * If this ioctl fails with -EINVAL, you're recommended to use the old + * "uinput_user_dev" method via write() as fallback, in case you run on an old + * kernel that does not support this ioctl. + * + * This ioctl may fail with -EINVAL if it is not supported or if you passed + * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the + * passed uinput_setup object cannot be read/written. + * If this call fails, partial data may have already been applied to the + * internal device. + */ +#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup) + #define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int) #define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int) #define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int) @@ -144,7 +222,6 @@ struct uinput_ff_erase { #define UI_FF_UPLOAD 1 #define UI_FF_ERASE 2 -#define UINPUT_MAX_NAME_SIZE 80 struct uinput_user_dev { char name[UINPUT_MAX_NAME_SIZE]; struct input_id id; -- cgit v1.2.3 From fbae10db094046dba1d59e1c2ee5140835045f14 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 Oct 2015 10:34:13 +0100 Subject: Input: uinput - rework ABS validation Rework the uinput ABS validation to check passed absinfo data immediately, but do ABS initialization as last step in UI_DEV_CREATE. The behavior observed by user-space is not changed, as ABS initialization was never checked for errors. With this in place, the order of device initialization and abs configuration is no longer fixed. Userspace can initialize the device and afterwards set absinfo just fine. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Tested-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 89 +++++++++++++++++++++++---------------------- include/uapi/linux/uinput.h | 29 +++++++-------- 2 files changed, 58 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index a16fc4a4bb1f..782df415e4d5 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -256,13 +256,22 @@ static void uinput_destroy_device(struct uinput_device *udev) static int uinput_create_device(struct uinput_device *udev) { struct input_dev *dev = udev->dev; - int error; + int error, nslot; if (udev->state != UIST_SETUP_COMPLETE) { printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); return -EINVAL; } + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + error = input_mt_init_slots(dev, nslot, 0); + if (error) + goto fail1; + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + if (udev->ff_effects_max) { error = input_ff_create(dev, udev->ff_effects_max); if (error) @@ -308,10 +317,35 @@ static int uinput_open(struct inode *inode, struct file *file) return 0; } +static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, + const struct input_absinfo *abs) +{ + int min, max; + + min = abs->minimum; + max = abs->maximum; + + if ((min != 0 || max != 0) && max <= min) { + printk(KERN_DEBUG + "%s: invalid abs[%02x] min:%d max:%d\n", + UINPUT_NAME, code, min, max); + return -EINVAL; + } + + if (abs->flat > max - min) { + printk(KERN_DEBUG + "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", + UINPUT_NAME, code, abs->flat, min, max); + return -EINVAL; + } + + return 0; +} + static int uinput_validate_absbits(struct input_dev *dev) { unsigned int cnt; - int nslot; + int error; if (!test_bit(EV_ABS, dev->evbit)) return 0; @@ -321,38 +355,12 @@ static int uinput_validate_absbits(struct input_dev *dev) */ for_each_set_bit(cnt, dev->absbit, ABS_CNT) { - int min, max; - - min = input_abs_get_min(dev, cnt); - max = input_abs_get_max(dev, cnt); - - if ((min != 0 || max != 0) && max <= min) { - printk(KERN_DEBUG - "%s: invalid abs[%02x] min:%d max:%d\n", - UINPUT_NAME, cnt, - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); + if (!dev->absinfo) return -EINVAL; - } - - if (input_abs_get_flat(dev, cnt) > - input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { - printk(KERN_DEBUG - "%s: abs_flat #%02x out of range: %d " - "(min:%d/max:%d)\n", - UINPUT_NAME, cnt, - input_abs_get_flat(dev, cnt), - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); - return -EINVAL; - } - } - if (test_bit(ABS_MT_SLOT, dev->absbit)) { - nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot, 0); - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { - input_set_events_per_packet(dev, 60); + error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); + if (error) + return error; } return 0; @@ -375,7 +383,6 @@ static int uinput_dev_setup(struct uinput_device *udev, { struct uinput_setup setup; struct input_dev *dev; - int retval; if (udev->state == UIST_CREATED) return -EINVAL; @@ -395,10 +402,6 @@ static int uinput_dev_setup(struct uinput_device *udev, if (!dev->name) return -ENOMEM; - retval = uinput_validate_absbits(dev); - if (retval < 0) - return retval; - udev->state = UIST_SETUP_COMPLETE; return 0; } @@ -408,6 +411,7 @@ static int uinput_abs_setup(struct uinput_device *udev, { struct uinput_abs_setup setup = {}; struct input_dev *dev; + int error; if (size > sizeof(setup)) return -E2BIG; @@ -423,19 +427,16 @@ static int uinput_abs_setup(struct uinput_device *udev, dev = udev->dev; + error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); + if (error) + return error; + input_alloc_absinfo(dev); if (!dev->absinfo) return -ENOMEM; set_bit(setup.code, dev->absbit); dev->absinfo[setup.code] = setup.absinfo; - - /* - * We restore the state to UIST_NEW_DEVICE because the user has to call - * UI_DEV_SETUP in the last place before UI_DEV_CREATE to check the - * validity of the absbits. - */ - udev->state = UIST_NEW_DEVICE; return 0; } diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index 77b8cf73a178..dc652e224b67 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -72,13 +72,12 @@ struct uinput_setup { /** * UI_DEV_SETUP - Set device parameters for setup * - * This ioctl sets parameters for the input device to be created. It must be - * issued *before* calling UI_DEV_CREATE or it will fail. This ioctl supersedes - * the old "struct uinput_user_dev" method, which wrote this data via write(). - * To actually set the absolute axes, you also need to call the ioctl - * UI_ABS_SETUP *before* calling this ioctl. + * This ioctl sets parameters for the input device to be created. It + * supersedes the old "struct uinput_user_dev" method, which wrote this data + * via write(). To actually set the absolute axes UI_ABS_SETUP should be + * used. * - * This ioctl takes a "struct uinput_setup" object as argument. The fields of + * The ioctl takes a "struct uinput_setup" object as argument. The fields of * this object are as follows: * id: See the description of "struct input_id". This field is * copied unchanged into the new device. @@ -87,9 +86,9 @@ struct uinput_setup { * See below for a description of FF with uinput. * * This ioctl can be called multiple times and will overwrite previous values. - * If this ioctl fails with -EINVAL, you're recommended to use the old - * "uinput_user_dev" method via write() as fallback, in case you run on an old - * kernel that does not support this ioctl. + * If this ioctl fails with -EINVAL, it is recommended to use the old + * "uinput_user_dev" method via write() as a fallback, in case you run on an + * old kernel that does not support this ioctl. * * This ioctl may fail with -EINVAL if it is not supported or if you passed * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the @@ -109,12 +108,10 @@ struct uinput_abs_setup { * UI_ABS_SETUP - Set absolute axis information for the device to setup * * This ioctl sets one absolute axis information for the input device to be - * created. It must be issued *before* calling UI_DEV_SETUP and UI_DEV_CREATE - * for every absolute axis the device exports. - * This ioctl supersedes the old "struct uinput_user_dev" method, which wrote + * created. It supersedes the old "struct uinput_user_dev" method, which wrote * part of this data and the content of UI_DEV_SETUP via write(). * - * This ioctl takes a "struct uinput_abs_setup" object as argument. The fields + * The ioctl takes a "struct uinput_abs_setup" object as argument. The fields * of this object are as follows: * code: The corresponding input code associated with this axis * (ABS_X, ABS_Y, etc...) @@ -124,9 +121,9 @@ struct uinput_abs_setup { * UI_SET_ABSBIT, this ioctl will enable it. * * This ioctl can be called multiple times and will overwrite previous values. - * If this ioctl fails with -EINVAL, you're recommended to use the old - * "uinput_user_dev" method via write() as fallback, in case you run on an old - * kernel that does not support this ioctl. + * If this ioctl fails with -EINVAL, it is recommended to use the old + * "uinput_user_dev" method via write() as a fallback, in case you run on an + * old kernel that does not support this ioctl. * * This ioctl may fail with -EINVAL if it is not supported or if you passed * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the -- cgit v1.2.3 From daf6cd0c1829c48cba197bd87d57fc8bf3f65faa Mon Sep 17 00:00:00 2001 From: Elias Vanderstuyft Date: Fri, 18 Dec 2015 17:32:19 -0800 Subject: Input: uinput - sanity check on ff_effects_max and EV_FF Currently the user can set ff_effects_max to zero with the EV_FF bit (and the FF_GAIN and/or FF_AUTOCENTER bits) set, in this case the uninitialized methods ff->set_gain and/or ff->set_autocenter can be dereferenced, resulting in a kernel oops. Check in uinput_create_device() and print a helpful message and return -EINVAL in case the check fails. Signed-off-by: Elias Vanderstuyft Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 782df415e4d5..4eb9e4d94f46 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -272,6 +272,13 @@ static int uinput_create_device(struct uinput_device *udev) input_set_events_per_packet(dev, 60); } + if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) { + printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n", + UINPUT_NAME); + error = -EINVAL; + goto fail1; + } + if (udev->ff_effects_max) { error = input_ff_create(dev, udev->ff_effects_max); if (error) -- cgit v1.2.3 From 8dcb3c7628f19192dd568fbee9094a2d4b14b6af Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 19 Dec 2015 09:22:21 -0800 Subject: Input: egalax_ts_serial - fix potential NULL dereference on error We didn't check input_allocate_device() for failures so it could lead to a NULL deref. Fixes: 6b0f8f9c52ef ('Input: add eGalaxTouch serial touchscreen driver') Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/egalax_ts_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c index a078c1c2c3f9..657bbae608c8 100644 --- a/drivers/input/touchscreen/egalax_ts_serial.c +++ b/drivers/input/touchscreen/egalax_ts_serial.c @@ -105,7 +105,7 @@ static int egalax_connect(struct serio *serio, struct serio_driver *drv) egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); input_dev = input_allocate_device(); - if (!egalax) { + if (!egalax || !input_dev) { error = -ENOMEM; goto err_free_mem; } -- cgit v1.2.3 From dd0d0d4de582a6a61c032332c91f4f4cb2bab569 Mon Sep 17 00:00:00 2001 From: Aurélien Francillon Date: Sat, 2 Jan 2016 20:39:54 -0800 Subject: Input: i8042 - add Fujitsu Lifebook U745 to the nomux list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without i8042.nomux=1 the Elantech touch pad is not working at all on a Fujitsu Lifebook U745. This patch does not seem necessary for all U745 (maybe because of different BIOS versions?). However, it was verified that the patch does not break those (see opensuse bug 883192: https://bugzilla.opensuse.org/show_bug.cgi?id=883192). Signed-off-by: Aurélien Francillon Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index c11556563ef0..68f5f4a0f1e7 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -257,6 +257,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, }, + { + /* Fujitsu Lifebook U745 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), + }, + }, { /* Fujitsu T70H */ .matches = { -- cgit v1.2.3 From 7ed5ff82c2f06965652f8d1a17c427ba8d363b92 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 2 Jan 2016 21:04:30 -0800 Subject: Input: bma150 - constify bma150_cfg structure The bma150_cfg structure is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Dmitry Torokhov --- drivers/input/misc/bma150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 1d0e61d7c131..b0d445390ee4 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -147,7 +147,7 @@ struct bma150_data { * are stated and verified by Bosch Sensortec where they are configured * to provide a generic sensitivity performance. */ -static struct bma150_cfg default_cfg = { +static const struct bma150_cfg default_cfg = { .any_motion_int = 1, .hg_int = 1, .lg_int = 1, -- cgit v1.2.3 From fa68e2777cbc410fafb4e45e92222a63f6e60e57 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sat, 2 Jan 2016 21:06:53 -0800 Subject: Input: pcap_ts - use to_delayed_work Use to_delayed_work() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pcap_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c index 23a354a392ae..0e3fc419a3cf 100644 --- a/drivers/input/touchscreen/pcap_ts.c +++ b/drivers/input/touchscreen/pcap_ts.c @@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2]) static void pcap_ts_work(struct work_struct *work) { - struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct delayed_work *dw = to_delayed_work(work); struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); u8 ch[2]; -- cgit v1.2.3 From dae928ec3c29e7e16723c9c4c1299e00f4e9e949 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 6 Jan 2016 15:50:08 -0800 Subject: Input: ALPS - detect trackstick presence for v7 protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds detection of trackstick for v7 protocol devices. Code in this patch is used in official Dell touchpad linux drivers for Dell models: Dell Latitude E5250/5250, E5450/5450, E5550/5550 Detection code and base reg for alps v3 rushmore and v7 devices is exacly same. Also user in bug https://bugzilla.kernel.org/show_bug.cgi?id=94801 reported that Toshiba Sattellite Z30-A-1DG has only alps v7 touchpad device without trackstick and kernel reports to userspace also redundant trackstick device. Signed-off-by: Pali Rohár Reviewed-by: Hans de Goede Tested-by: Alex Hung Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 41e6cb501e6a..1aa170987028 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -31,6 +31,7 @@ #define ALPS_CMD_NIBBLE_10 0x01f2 #define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_V7 0xc2c0 #define ALPS_REG_BASE_PINNACLE 0x0000 static const struct alps_nibble_commands alps_v3_nibble_commands[] = { @@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse) return 0; } -static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base) +static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base) { int ret = -EIO, reg_val; @@ -2132,7 +2133,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse) int reg_val; unsigned char param[4]; - reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); + reg_val = alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_PINNACLE); if (reg_val == -EIO) goto error; @@ -2625,8 +2626,8 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->x_bits = 16; priv->y_bits = 12; - if (alps_probe_trackstick_v3(psmouse, - ALPS_REG_BASE_RUSHMORE) < 0) + if (alps_probe_trackstick_v3_v7(psmouse, + ALPS_REG_BASE_RUSHMORE) < 0) priv->flags &= ~ALPS_DUALPOINT; break; @@ -2676,6 +2677,9 @@ static int alps_set_protocol(struct psmouse *psmouse, if (priv->fw_ver[1] != 0xba) priv->flags |= ALPS_BUTTONPAD; + if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0) + priv->flags &= ~ALPS_DUALPOINT; + break; case ALPS_PROTO_V8: -- cgit v1.2.3 From 4b1af853646ca893fa686701d1605998fffcc82c Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 6 Jan 2016 15:58:56 -0800 Subject: Input: ALPS - report v3 pinnacle trackstick device only if is present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch moves v3 pinnacle code for trackstick detection from alps_hw_init_v3() to alps_set_protocol() so ALPS_DUALPOINT flag can be cleared before registering trackstick input device in kernel. Signed-off-by: Pali Rohár Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 1aa170987028..936f07a4e35f 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2129,15 +2129,12 @@ error: static int alps_hw_init_v3(struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; int reg_val; unsigned char param[4]; - reg_val = alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_PINNACLE); - if (reg_val == -EIO) - goto error; - - if (reg_val == 0 && + if ((priv->flags & ALPS_DUALPOINT) && alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) goto error; @@ -2614,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->decode_fields = alps_decode_pinnacle; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_probe_trackstick_v3_v7(psmouse, + ALPS_REG_BASE_PINNACLE) < 0) + priv->flags &= ~ALPS_DUALPOINT; + break; case ALPS_PROTO_V3_RUSHMORE: -- cgit v1.2.3 From 415a249f88fd263bed0f7cca86ecde8c57aba9dc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Jan 2016 14:44:02 -0800 Subject: Input: rohm_bu21023 - fix handling of retrying firmware update Because of the wrong condition we'd never retry firmware update. Acked-by: Yoichi Yuasa Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/rohm_bu21023.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c index ba6024f93469..611156a2ef80 100644 --- a/drivers/input/touchscreen/rohm_bu21023.c +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client, break; error = -EIO; - } while (++retry >= FIRMWARE_RETRY_MAX); + } while (++retry <= FIRMWARE_RETRY_MAX); out: error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL); -- cgit v1.2.3 From 7ae6cfe8d24681bea5a4a4b7a04915ac08c09ba5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Jan 2016 09:55:14 -0800 Subject: Input: omap-keypad - set tasklet data earlier It feels like we should set the tasklet data before enabling it. Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 7502e46165fa..75ad6661e130 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -292,8 +292,8 @@ static int omap_kp_probe(struct platform_device *pdev) setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); /* get the irq and init timer*/ - tasklet_enable(&kp_tasklet); kp_tasklet.data = (unsigned long) omap_kp; + tasklet_enable(&kp_tasklet); ret = device_create_file(&pdev->dev, &dev_attr_enable); if (ret < 0) -- cgit v1.2.3 From 3b7474ec0d7150044f91e3af460067f79d466522 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 7 Jan 2016 11:13:34 -0800 Subject: Input: ti_am335x_tsc - fix HWPEN interrupt handling Remove write to REG_IRQCLR and REG_IRQWAKEUP in interrupt handler for IRQENB_HW_PEN as the resume handler should and does clear REG_IRQWAKEUP. IRQENB_HW_PEN bit is set in irqclr so that all interrupts get cleared later so let IRQENB_HW_PEN be cleared by that. Without this patch wakeup events from TSC_ADC do not work because pending interrupts in TSC_ADC were causing HW_PEN interrupt, needed for wake from suspend modes, to get disabled immediately by IRQ handler after being enabled and preventing wake from happening. Signed-off-by: Dave Gerlach Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ti_am335x_tsc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 191a1b87895f..a21a07c3ab6d 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev) status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); if (status & IRQENB_HW_PEN) { ts_dev->pen_down = true; - titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); - titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); irqclr |= IRQENB_HW_PEN; } -- cgit v1.2.3 From 35f8679f577ae5673a778598bcbe7b45cbec8923 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 7 Jan 2016 15:06:51 -0800 Subject: Input: omap-keypad - remove dead check Commit da1f026b532ce944d74461497dc6d8c16456466e ("Keyboard: omap-keypad: use matrix_keypad.h") switched the driver to use matrix keypad infrastructure, which made array of keycodes to be unsigned short, and caused the test for negativity never trigger. This leads to the following static checker warning: drivers/input/keyboard/omap-keypad.c:158 omap_kp_tasklet() warn: 'keycodes[]' is never negative. Given that we did not care about this check for a few years already let's simply remove it. Reported-by: Dan Carpenter Acked-by: Aaro Koskinen Acked-by: Tony Lindgren Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 75ad6661e130..e0d72c8c01e4 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data) "pressed" : "released"); #else key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; - if (key < 0) { - printk(KERN_WARNING - "omap-keypad: Spurious key event %d-%d\n", - col, row); - /* We scan again after a couple of seconds */ - spurious = 1; - continue; - } if (!(kp_cur_group == (key & GROUP_MASK) || kp_cur_group == -1)) -- cgit v1.2.3 From 8679ee4204cfd5cf78b996508ccadc1ec6130f1a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Jan 2016 14:20:07 -0800 Subject: Input: gpio-keys - fix check for disabling unsupported keys Commit 4ea14a53d8f881034fa9e186653821c4e3d9a8fb ("Input: gpio-keys - report error when disabling unsupported key") tried let user know that they attempted to disable an unsupported key, unfortunately the check is wrong as it believes that all codes are invalid. Fix it by ensuring that keys that we try to disable are subset of keys (or switches) that device reports. Fixes: 4ea14a53d8f8 ("Input: gpio-keys - report error when disabling unsupported key") Reported-by: Ivaylo Dimitrov Tested-by: Ivaylo Dimitrov Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index bef317ff7352..b9f01bd1b7ef 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -96,13 +96,29 @@ struct gpio_keys_drvdata { * Return value of this function can be used to allocate bitmap * large enough to hold all bits for given type. */ -static inline int get_n_events_by_type(int type) +static int get_n_events_by_type(int type) { BUG_ON(type != EV_SW && type != EV_KEY); return (type == EV_KEY) ? KEY_CNT : SW_CNT; } +/** + * get_bm_events_by_type() - returns bitmap of supported events per @type + * @input: input device from which bitmap is retrieved + * @type: type of button (%EV_KEY, %EV_SW) + * + * Return value of this function can be used to allocate bitmap + * large enough to hold all bits for given type. + */ +static const unsigned long *get_bm_events_by_type(struct input_dev *dev, + int type) +{ + BUG_ON(type != EV_SW && type != EV_KEY); + + return (type == EV_KEY) ? dev->keybit : dev->swbit; +} + /** * gpio_keys_disable_button() - disables given GPIO button * @bdata: button data for button to be disabled @@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, const char *buf, unsigned int type) { int n_events = get_n_events_by_type(type); + const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type); unsigned long *bits; ssize_t error; int i; @@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, goto out; /* First validate */ + if (!bitmap_subset(bits, bitmap, n_events)) { + error = -EINVAL; + goto out; + } + for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; @@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, } } - if (i == ddata->pdata->nbuttons) { - error = -EINVAL; - goto out; - } - mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->pdata->nbuttons; i++) { -- cgit v1.2.3 From 5f7e5445a2de848c66d2d80ba5479197e8287c33 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 11 Jan 2016 00:08:58 -0800 Subject: Input: wacom_w8001 - drop use of ABS_MT_TOOL_TYPE As of e0361b70175 ("Input: wacom_w8001 - split the touch and pen devices into two devices") the touch events aren't multiplexed over the same device anymore, the use of ABS_MT_TOOL_TYPE is superfluous. And even before then it only ever sent MT_TOOL_TYPE_FINGER anyway. Signed-off-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index fe983e781bca..bab3c6acf6a2 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -155,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001) bool touch = data[0] & (1 << i); 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]; @@ -514,8 +513,6 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, 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, MT_TOOL_MAX, 0, 0); strlcat(basename, " 2FG", basename_sz); if (w8001->max_pen_x && w8001->max_pen_y) -- cgit v1.2.3 From 6544a1df11c48c8413071aac3316792e4678fbfb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 11 Jan 2016 17:35:38 -0800 Subject: Input: elantech - mark protocols v2 and v3 as semi-mt When using a protocol v2 or v3 hardware, elantech uses the function elantech_report_semi_mt_data() to report data. This devices are rather creepy because if num_finger is 3, (x2,y2) is (0,0). Yes, only one valid touch is reported. Anyway, userspace (libinput) is now confused by these (0,0) touches, and detect them as palm, and rejects them. Commit 3c0213d17a09 ("Input: elantech - fix semi-mt protocol for v3 HW") was sufficient enough for xf86-input-synaptics and libinput before it has palm rejection. Now we need to actually tell libinput that this device is a semi-mt one and it should not rely on the actual values of the 2 touches. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 537ebb0e193a..78f93cf68840 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, ETP_WMAX_V2, 0, 0); } - input_mt_init_slots(dev, 2, 0); + input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); break; -- cgit v1.2.3