diff options
Diffstat (limited to 'drivers/input')
26 files changed, 1315 insertions, 70 deletions
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index f50f6dd92274..b81c88c43452 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -23,8 +23,6 @@ /* #define DEBUG */ -#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt - #include <linux/input.h> #include <linux/module.h> #include <linux/mutex.h> @@ -116,7 +114,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX || !test_bit(effect->type, dev->ffbit)) { - pr_debug("invalid or not supported effect type in upload\n"); + dev_dbg(&dev->dev, "invalid or not supported effect type in upload\n"); return -EINVAL; } @@ -124,7 +122,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, (effect->u.periodic.waveform < FF_WAVEFORM_MIN || effect->u.periodic.waveform > FF_WAVEFORM_MAX || !test_bit(effect->u.periodic.waveform, dev->ffbit))) { - pr_debug("invalid or not supported wave form in upload\n"); + dev_dbg(&dev->dev, "invalid or not supported wave form in upload\n"); return -EINVAL; } @@ -246,7 +244,7 @@ static int flush_effects(struct input_dev *dev, struct file *file) struct ff_device *ff = dev->ff; int i; - pr_debug("flushing now\n"); + dev_dbg(&dev->dev, "flushing now\n"); mutex_lock(&ff->mutex); @@ -316,7 +314,7 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects) int i; if (!max_effects) { - pr_err("cannot allocate device without any effects\n"); + dev_err(&dev->dev, "cannot allocate device without any effects\n"); return -EINVAL; } diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 74c0d8c6002a..fcc6c3368182 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -237,6 +237,18 @@ static u16 ml_calculate_direction(u16 direction, u16 force, (force + new_force)) << 1; } +#define FRAC_N 8 +static inline s16 fixp_new16(s16 a) +{ + return ((s32)a) >> (16 - FRAC_N); +} + +static inline s16 fixp_mult(s16 a, s16 b) +{ + a = ((s32)a * 0x100) / 0x7fff; + return ((s32)(a * b)) >> FRAC_N; +} + /* * Combine two effects and apply gain. */ @@ -247,7 +259,7 @@ static void ml_combine_effects(struct ff_effect *effect, struct ff_effect *new = state->effect; unsigned int strong, weak, i; int x, y; - fixp_t level; + s16 level; switch (new->type) { case FF_CONSTANT: @@ -255,8 +267,8 @@ static void ml_combine_effects(struct ff_effect *effect, level = fixp_new16(apply_envelope(state, new->u.constant.level, &new->u.constant.envelope)); - x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; - y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff; /* * here we abuse ff_ramp to hold x and y of constant force * If in future any driver wants something else than x and y diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index f362883c94e3..1d247bcf2ae2 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -747,6 +747,63 @@ static void joydev_cleanup(struct joydev *joydev) input_close_device(handle); } +static bool joydev_dev_is_absolute_mouse(struct input_dev *dev) +{ + DECLARE_BITMAP(jd_scratch, KEY_CNT); + + BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT); + + /* + * Virtualization (VMware, etc) and remote management (HP + * ILO2) solutions use absolute coordinates for their virtual + * pointing devices so that there is one-to-one relationship + * between pointer position on the host screen and virtual + * guest screen, and so their mice use ABS_X, ABS_Y and 3 + * primary button events. This clashes with what joydev + * considers to be joysticks (a device with at minimum ABS_X + * axis). + * + * Here we are trying to separate absolute mice from + * joysticks. A device is, for joystick detection purposes, + * considered to be an absolute mouse if the following is + * true: + * + * 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN. + * 2) Absolute events are exactly ABS_X and ABS_Y. + * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE. + * 4) Device is not on "Amiga" bus. + */ + + bitmap_zero(jd_scratch, EV_CNT); + __set_bit(EV_ABS, jd_scratch); + __set_bit(EV_KEY, jd_scratch); + __set_bit(EV_SYN, jd_scratch); + if (!bitmap_equal(jd_scratch, dev->evbit, EV_CNT)) + return false; + + bitmap_zero(jd_scratch, ABS_CNT); + __set_bit(ABS_X, jd_scratch); + __set_bit(ABS_Y, jd_scratch); + if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT)) + return false; + + bitmap_zero(jd_scratch, KEY_CNT); + __set_bit(BTN_LEFT, jd_scratch); + __set_bit(BTN_RIGHT, jd_scratch); + __set_bit(BTN_MIDDLE, jd_scratch); + + if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT)) + return false; + + /* + * Amiga joystick (amijoy) historically uses left/middle/right + * button events. + */ + if (dev->id.bustype == BUS_AMIGA) + return false; + + return true; +} static bool joydev_match(struct input_handler *handler, struct input_dev *dev) { @@ -758,6 +815,10 @@ static bool joydev_match(struct input_handler *handler, struct input_dev *dev) if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit)) return false; + /* Avoid absolute mice */ + if (joydev_dev_is_absolute_mouse(dev)) + return false; + return true; } diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 3aa2f3f3da5b..61c761156371 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -31,12 +31,14 @@ * - the iForce driver drivers/char/joystick/iforce.c * - the skeleton-driver drivers/usb/usb-skeleton.c * - Xbox 360 information http://www.free60.org/wiki/Gamepad + * - Xbox One information https://github.com/quantus/xbox-one-controller-protocol * * Thanks to: * - ITO Takayuki for providing essential xpad information on his website * - Vojtech Pavlik - iforce driver / input subsystem * - Greg Kroah-Hartman - usb-skeleton driver * - XBOX Linux project - extra USB id's + * - Pekka Pöyry (quantus) - Xbox One controller reverse engineering * * TODO: * - fine tune axes (especially trigger axes) @@ -828,6 +830,23 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + case XTYPE_XBOXONE: + xpad->odata[0] = 0x09; /* activate rumble */ + xpad->odata[1] = 0x08; + xpad->odata[2] = 0x00; + xpad->odata[3] = 0x08; /* continuous effect */ + xpad->odata[4] = 0x00; /* simple rumble mode */ + xpad->odata[5] = 0x03; /* L and R actuator only */ + xpad->odata[6] = 0x00; /* TODO: LT actuator */ + xpad->odata[7] = 0x00; /* TODO: RT actuator */ + xpad->odata[8] = strong / 256; /* left actuator */ + xpad->odata[9] = weak / 256; /* right actuator */ + xpad->odata[10] = 0x80; /* length of pulse */ + xpad->odata[11] = 0x00; /* stop period of pulse */ + xpad->irq_out->transfer_buffer_length = 12; + + return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + default: dev_dbg(&xpad->dev->dev, "%s - rumble command sent to unsupported xpad type: %d\n", @@ -841,7 +860,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect static int xpad_init_ff(struct usb_xpad *xpad) { - if (xpad->xtype == XTYPE_UNKNOWN || xpad->xtype == XTYPE_XBOXONE) + if (xpad->xtype == XTYPE_UNKNOWN) return 0; input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 64b9b59ad4cb..b50c5b8b8a4d 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -148,16 +148,19 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { + int ret; struct cros_ec_command msg = { - .version = 0, .command = EC_CMD_MKBP_STATE, - .outdata = NULL, - .outsize = 0, - .indata = kb_state, .insize = ckdev->cols, }; - return cros_ec_cmd_xfer(ckdev->ec, &msg); + ret = cros_ec_cmd_xfer(ckdev->ec, &msg); + if (ret < 0) + return ret; + + memcpy(kb_state, msg.indata, ckdev->cols); + + return 0; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 90df4df58b07..097d7216d98e 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device_for_each_child_node(dev, child) { struct gpio_desc *desc; - desc = devm_get_gpiod_from_child(dev, child); + desc = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(desc)) { error = PTR_ERR(desc); if (error != -EPROBE_DEFER) diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index 9081cbef11ea..0ad422b8a260 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -1,6 +1,6 @@ /* * LM8333 keypad driver - * Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de> + * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -231,6 +231,6 @@ static struct i2c_driver lm8333_driver = { }; module_i2c_driver(lm8333_driver); -MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>"); MODULE_DESCRIPTION("LM8333 keyboard driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 4658b5d41dd7..d7820d1152d2 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -149,6 +149,18 @@ config MOUSE_PS2_FOCALTECH If unsure, say Y. +config MOUSE_PS2_VMMOUSE + bool "Virtual mouse (vmmouse)" + depends on MOUSE_PS2 && X86 && HYPERVISOR_GUEST + help + Say Y here if you are running under control of VMware hypervisor + (ESXi, Workstation or Fusion). Also make sure that when you enable + this option, you remove the xf86-input-vmmouse user-space driver + or upgrade it to at least xf86-input-vmmouse 13.1.0, which doesn't + load in the presence of an in-kernel vmmouse driver. + + If unsure, say N. + config MOUSE_SERIAL tristate "Serial mouse" select SERIO diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 8a9c98e76d9c..793300bfbddd 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -36,6 +36,7 @@ psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o +psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o elan_i2c-objs := elan_i2c_core.o elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index e6708f6efb4d..a353b7de6d22 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -941,6 +941,11 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, case V7_PACKET_ID_TWO: mt[1].x &= ~0x000F; mt[1].y |= 0x000F; + /* Detect false-postive touches where x & y report max value */ + if (mt[1].y == 0x7ff && mt[1].x == 0xff0) { + mt[1].x = 0; + /* y gets set to 0 at the end of this function */ + } break; case V7_PACKET_ID_MULTI: @@ -1058,9 +1063,8 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) right = (packet[1] & 0x02) >> 1; middle = (packet[1] & 0x04) >> 2; - /* Divide 2 since trackpoint's speed is too fast */ - input_report_rel(dev2, REL_X, (char)x / 2); - input_report_rel(dev2, REL_Y, -((char)y / 2)); + input_report_rel(dev2, REL_X, (char)x); + input_report_rel(dev2, REL_Y, -((char)y)); input_report_key(dev2, BTN_LEFT, left); input_report_key(dev2, BTN_RIGHT, right); diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 58f4f6fa4857..efe148474e7f 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -723,7 +723,7 @@ static ssize_t cyapa_update_suspend_scanrate(struct device *dev, } else if (sysfs_streq(buf, OFF_MODE_NAME)) { cyapa->suspend_power_mode = PWR_MODE_OFF; } else if (!kstrtou16(buf, 10, &sleep_time)) { - cyapa->suspend_sleep_time = max_t(u16, sleep_time, 1000); + cyapa->suspend_sleep_time = min_t(u16, sleep_time, 1000); cyapa->suspend_power_mode = cyapa_sleep_time_to_pwr_cmd(cyapa->suspend_sleep_time); } else { @@ -840,7 +840,7 @@ static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev, if (error) return error; - cyapa->runtime_suspend_sleep_time = max_t(u16, time, 1000); + cyapa->runtime_suspend_sleep_time = min_t(u16, time, 1000); cyapa->runtime_suspend_power_mode = cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time); diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index 9b2dc015f20c..6d5f8a4c1748 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -25,6 +25,7 @@ #define ETP_ENABLE_CALIBRATE 0x0002 #define ETP_DISABLE_CALIBRATE 0x0000 #define ETP_DISABLE_POWER 0x0001 +#define ETP_PRESSURE_OFFSET 25 /* IAP Firmware handling */ #define ETP_FW_NAME "elan_i2c.bin" @@ -79,6 +80,8 @@ struct elan_transport_ops { struct completion *reset_done); int (*get_report)(struct i2c_client *client, u8 *report); + int (*get_pressure_adjustment)(struct i2c_client *client, + int *adjustment); }; extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 375d98f47483..fd5068b2542d 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.6 + * Version: 1.5.7 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -40,8 +40,7 @@ #include "elan_i2c.h" #define DRIVER_NAME "elan_i2c" -#define ELAN_DRIVER_VERSION "1.5.6" -#define ETP_PRESSURE_OFFSET 25 +#define ELAN_DRIVER_VERSION "1.5.7" #define ETP_MAX_PRESSURE 255 #define ETP_FWIDTH_REDUCE 90 #define ETP_FINGER_WIDTH 15 @@ -53,6 +52,7 @@ #define ETP_REPORT_ID_OFFSET 2 #define ETP_TOUCH_INFO_OFFSET 3 #define ETP_FINGER_DATA_OFFSET 4 +#define ETP_HOVER_INFO_OFFSET 30 #define ETP_MAX_REPORT_LEN 34 /* The main device structure */ @@ -81,7 +81,7 @@ struct elan_tp_data { u8 sm_version; u8 iap_version; u16 fw_checksum; - + int pressure_adjustment; u8 mode; bool irq_wake; @@ -229,6 +229,11 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; + error = data->ops->get_pressure_adjustment(data->client, + &data->pressure_adjustment); + if (error) + return error; + return 0; } @@ -721,13 +726,13 @@ static const struct attribute_group *elan_sysfs_groups[] = { */ static void elan_report_contact(struct elan_tp_data *data, int contact_num, bool contact_valid, - u8 *finger_data) + bool hover_event, u8 *finger_data) { struct input_dev *input = data->input; unsigned int pos_x, pos_y; unsigned int pressure, mk_x, mk_y; - unsigned int area_x, area_y, major, minor, new_pressure; - + unsigned int area_x, area_y, major, minor; + unsigned int scaled_pressure; if (contact_valid) { pos_x = ((finger_data[0] & 0xf0) << 4) | @@ -756,15 +761,18 @@ static void elan_report_contact(struct elan_tp_data *data, major = max(area_x, area_y); minor = min(area_x, area_y); - new_pressure = pressure + ETP_PRESSURE_OFFSET; - if (new_pressure > ETP_MAX_PRESSURE) - new_pressure = ETP_MAX_PRESSURE; + scaled_pressure = pressure + data->pressure_adjustment; + + if (scaled_pressure > ETP_MAX_PRESSURE) + scaled_pressure = ETP_MAX_PRESSURE; input_mt_slot(input, contact_num); input_mt_report_slot_state(input, MT_TOOL_FINGER, true); input_report_abs(input, ABS_MT_POSITION_X, pos_x); input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y); - input_report_abs(input, ABS_MT_PRESSURE, new_pressure); + input_report_abs(input, ABS_MT_DISTANCE, hover_event); + input_report_abs(input, ABS_MT_PRESSURE, + hover_event ? 0 : scaled_pressure); input_report_abs(input, ABS_TOOL_WIDTH, mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR, major); input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); @@ -780,11 +788,14 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet) u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET]; int i; u8 tp_info = packet[ETP_TOUCH_INFO_OFFSET]; - bool contact_valid; + u8 hover_info = packet[ETP_HOVER_INFO_OFFSET]; + bool contact_valid, hover_event; + hover_event = hover_info & 0x40; for (i = 0; i < ETP_MAX_FINGERS; i++) { contact_valid = tp_info & (1U << (3 + i)); - elan_report_contact(data, i, contact_valid, finger_data); + elan_report_contact(data, i, contact_valid, hover_event, + finger_data); if (contact_valid) finger_data += ETP_FINGER_DATA_LEN; @@ -878,6 +889,7 @@ static int elan_setup_input_device(struct elan_tp_data *data) ETP_FINGER_WIDTH * max_width, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, ETP_FINGER_WIDTH * min_width, 0, 0); + input_set_abs_params(input, ABS_MT_DISTANCE, 0, 1, 0, 0); data->input = input; diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 6cf0def6d35e..a0acbbf83bfd 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -41,6 +41,7 @@ #define ETP_I2C_MAX_X_AXIS_CMD 0x0106 #define ETP_I2C_MAX_Y_AXIS_CMD 0x0107 #define ETP_I2C_RESOLUTION_CMD 0x0108 +#define ETP_I2C_PRESSURE_CMD 0x010A #define ETP_I2C_IAP_VERSION_CMD 0x0110 #define ETP_I2C_SET_CMD 0x0300 #define ETP_I2C_POWER_CMD 0x0307 @@ -364,8 +365,29 @@ static int elan_i2c_get_num_traces(struct i2c_client *client, return error; } - *x_traces = val[0] - 1; - *y_traces = val[1] - 1; + *x_traces = val[0]; + *y_traces = val[1]; + + return 0; +} + +static int elan_i2c_get_pressure_adjustment(struct i2c_client *client, + int *adjustment) +{ + int error; + u8 val[3]; + + error = elan_i2c_read_cmd(client, ETP_I2C_PRESSURE_CMD, val); + if (error) { + dev_err(&client->dev, "failed to get pressure format: %d\n", + error); + return error; + } + + if ((val[0] >> 4) & 0x1) + *adjustment = 0; + else + *adjustment = ETP_PRESSURE_OFFSET; return 0; } @@ -602,6 +624,7 @@ const struct elan_transport_ops elan_i2c_ops = { .get_sm_version = elan_i2c_get_sm_version, .get_product_id = elan_i2c_get_product_id, .get_checksum = elan_i2c_get_checksum, + .get_pressure_adjustment = elan_i2c_get_pressure_adjustment, .get_max = elan_i2c_get_max, .get_resolution = elan_i2c_get_resolution, diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index 06a2bcd1cda2..30ab80dbcdd6 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -268,12 +268,19 @@ static int elan_smbus_get_num_traces(struct i2c_client *client, return error; } - *x_traces = val[1] - 1; - *y_traces = val[2] - 1; + *x_traces = val[1]; + *y_traces = val[2]; return 0; } +static int elan_smbus_get_pressure_adjustment(struct i2c_client *client, + int *adjustment) +{ + *adjustment = ETP_PRESSURE_OFFSET; + return 0; +} + static int elan_smbus_iap_get_mode(struct i2c_client *client, enum tp_mode *mode) { @@ -497,6 +504,7 @@ const struct elan_transport_ops elan_smbus_ops = { .get_sm_version = elan_smbus_get_sm_version, .get_product_id = elan_smbus_get_product_id, .get_checksum = elan_smbus_get_checksum, + .get_pressure_adjustment = elan_smbus_get_pressure_adjustment, .get_max = elan_smbus_get_max, .get_resolution = elan_smbus_get_resolution, diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 991dc6b20a58..ce3d40004458 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -315,7 +315,7 @@ static void elantech_report_semi_mt_data(struct input_dev *dev, unsigned int x2, unsigned int y2) { elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); - elantech_set_slot(dev, 1, num_fingers == 2, x2, y2); + elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2); } /* @@ -1376,10 +1376,11 @@ static bool elantech_is_signature_valid(const unsigned char *param) return true; /* - * Some models have a revision higher then 20. Meaning param[2] may - * be 10 or 20, skip the rates check for these. + * Some hw_version >= 4 models have a revision higher then 20. Meaning + * that param[2] may be 10 or 20, skip the rates check for these. */ - if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40) + if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f && + param[2] < 40) return true; for (i = 0; i < ARRAY_SIZE(rates); i++) @@ -1555,6 +1556,7 @@ static int elantech_set_properties(struct elantech_data *etd) case 9: case 10: case 13: + case 14: etd->hw_version = 4; break; default: diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 27057df7ba74..5bb1658f60c7 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -36,6 +36,7 @@ #include "sentelic.h" #include "cypress_ps2.h" #include "focaltech.h" +#include "vmmouse.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -790,6 +791,13 @@ static int psmouse_extensions(struct psmouse *psmouse, } } + 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; + } + } + /* * Try Kensington ThinkingMouse (we try first, because synaptics probe * upsets the thinkingmouse). @@ -1113,6 +1121,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .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", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index d02e1bdc9ae4..ad5a5a1ea872 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -103,6 +103,7 @@ enum psmouse_type { PSMOUSE_SYNAPTICS_RELATIVE, PSMOUSE_CYPRESS, PSMOUSE_FOCALTECH, + PSMOUSE_VMMOUSE, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mouse/vmmouse.c b/drivers/input/mouse/vmmouse.c new file mode 100644 index 000000000000..e272f06258ce --- /dev/null +++ b/drivers/input/mouse/vmmouse.c @@ -0,0 +1,508 @@ +/* + * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors. + * + * Copyright (C) 2014, VMware, Inc. All Rights Reserved. + * + * 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. + * + * Twin device code is hugely inspired by the ALPS driver. + * Authors: + * Dmitry Torokhov <dmitry.torokhov@gmail.com> + * Thomas Hellstrom <thellstrom@vmware.com> + */ + +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/libps2.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <asm/hypervisor.h> + +#include "psmouse.h" +#include "vmmouse.h" + +#define VMMOUSE_PROTO_MAGIC 0x564D5868U +#define VMMOUSE_PROTO_PORT 0x5658 + +/* + * Main commands supported by the vmmouse hypervisor port. + */ +#define VMMOUSE_PROTO_CMD_GETVERSION 10 +#define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA 39 +#define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS 40 +#define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND 41 +#define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT 86 + +/* + * Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND + */ +#define VMMOUSE_CMD_ENABLE 0x45414552U +#define VMMOUSE_CMD_DISABLE 0x000000f5U +#define VMMOUSE_CMD_REQUEST_RELATIVE 0x4c455252U +#define VMMOUSE_CMD_REQUEST_ABSOLUTE 0x53424152U + +#define VMMOUSE_ERROR 0xffff0000U + +#define VMMOUSE_VERSION_ID 0x3442554aU + +#define VMMOUSE_RELATIVE_PACKET 0x00010000U + +#define VMMOUSE_LEFT_BUTTON 0x20 +#define VMMOUSE_RIGHT_BUTTON 0x10 +#define VMMOUSE_MIDDLE_BUTTON 0x08 + +/* + * VMMouse Restrict command + */ +#define VMMOUSE_RESTRICT_ANY 0x00 +#define VMMOUSE_RESTRICT_CPL0 0x01 +#define VMMOUSE_RESTRICT_IOPL 0x02 + +#define VMMOUSE_MAX_X 0xFFFF +#define VMMOUSE_MAX_Y 0xFFFF + +#define VMMOUSE_VENDOR "VMware" +#define VMMOUSE_NAME "VMMouse" + +/** + * struct vmmouse_data - private data structure for the vmmouse driver + * + * @abs_dev: "Absolute" device used to report absolute mouse movement. + * @phys: Physical path for the absolute device. + * @dev_name: Name attribute name for the absolute device. + */ +struct vmmouse_data { + struct input_dev *abs_dev; + char phys[32]; + char dev_name[128]; +}; + +/** + * Hypervisor-specific bi-directional communication channel + * implementing the vmmouse protocol. Should never execute on + * bare metal hardware. + */ +#define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4) \ +({ \ + unsigned long __dummy1, __dummy2; \ + __asm__ __volatile__ ("inl %%dx" : \ + "=a"(out1), \ + "=b"(out2), \ + "=c"(out3), \ + "=d"(out4), \ + "=S"(__dummy1), \ + "=D"(__dummy2) : \ + "a"(VMMOUSE_PROTO_MAGIC), \ + "b"(in1), \ + "c"(VMMOUSE_PROTO_CMD_##cmd), \ + "d"(VMMOUSE_PROTO_PORT) : \ + "memory"); \ +}) + +/** + * vmmouse_report_button - report button state on the correct input device + * + * @psmouse: Pointer to the psmouse struct + * @abs_dev: The absolute input device + * @rel_dev: The relative input device + * @pref_dev: The preferred device for reporting + * @code: Button code + * @value: Button value + * + * Report @value and @code on @pref_dev, unless the button is already + * pressed on the other device, in which case the state is reported on that + * device. + */ +static void vmmouse_report_button(struct psmouse *psmouse, + struct input_dev *abs_dev, + struct input_dev *rel_dev, + struct input_dev *pref_dev, + unsigned int code, int value) +{ + if (test_bit(code, abs_dev->key)) + pref_dev = abs_dev; + else if (test_bit(code, rel_dev->key)) + pref_dev = rel_dev; + + input_report_key(pref_dev, code, value); +} + +/** + * vmmouse_report_events - process events on the vmmouse communications channel + * + * @psmouse: Pointer to the psmouse struct + * + * This function pulls events from the vmmouse communications channel and + * reports them on the correct (absolute or relative) input device. When the + * communications channel is drained, or if we've processed more than 255 + * psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a + * host- or synchronization error, the function returns PSMOUSE_BAD_DATA in + * the hope that the caller will reset the communications channel. + */ +static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse) +{ + struct input_dev *rel_dev = psmouse->dev; + struct vmmouse_data *priv = psmouse->private; + struct input_dev *abs_dev = priv->abs_dev; + struct input_dev *pref_dev; + u32 status, x, y, z; + u32 dummy1, dummy2, dummy3; + unsigned int queue_length; + unsigned int count = 255; + + while (count--) { + /* See if we have motion data. */ + VMMOUSE_CMD(ABSPOINTER_STATUS, 0, + status, dummy1, dummy2, dummy3); + if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) { + psmouse_err(psmouse, "failed to fetch status data\n"); + /* + * After a few attempts this will result in + * reconnect. + */ + return PSMOUSE_BAD_DATA; + } + + queue_length = status & 0xffff; + if (queue_length == 0) + break; + + if (queue_length % 4) { + psmouse_err(psmouse, "invalid queue length\n"); + return PSMOUSE_BAD_DATA; + } + + /* Now get it */ + VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z); + + /* + * And report what we've got. Prefer to report button + * events on the same device where we report motion events. + * This doesn't work well with the mouse wheel, though. See + * below. Ideally we would want to report that on the + * preferred device as well. + */ + if (status & VMMOUSE_RELATIVE_PACKET) { + pref_dev = rel_dev; + input_report_rel(rel_dev, REL_X, (s32)x); + input_report_rel(rel_dev, REL_Y, -(s32)y); + } else { + pref_dev = abs_dev; + input_report_abs(abs_dev, ABS_X, x); + input_report_abs(abs_dev, ABS_Y, y); + } + + /* Xorg seems to ignore wheel events on absolute devices */ + input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z)); + + vmmouse_report_button(psmouse, abs_dev, rel_dev, + pref_dev, BTN_LEFT, + status & VMMOUSE_LEFT_BUTTON); + vmmouse_report_button(psmouse, abs_dev, rel_dev, + pref_dev, BTN_RIGHT, + status & VMMOUSE_RIGHT_BUTTON); + vmmouse_report_button(psmouse, abs_dev, rel_dev, + pref_dev, BTN_MIDDLE, + status & VMMOUSE_MIDDLE_BUTTON); + input_sync(abs_dev); + input_sync(rel_dev); + } + + return PSMOUSE_FULL_PACKET; +} + +/** + * vmmouse_process_byte - process data on the ps/2 channel + * + * @psmouse: Pointer to the psmouse struct + * + * When the ps/2 channel indicates that there is vmmouse data available, + * call vmmouse channel processing. Otherwise, continue to accept bytes. If + * there is a synchronization or communication data error, return + * PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse. + */ +static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + switch (psmouse->pktcnt) { + case 1: + return (packet[0] & 0x8) == 0x8 ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + + case 2: + return PSMOUSE_GOOD_DATA; + + default: + return vmmouse_report_events(psmouse); + } +} + +/** + * vmmouse_disable - Disable vmmouse + * + * @psmouse: Pointer to the psmouse struct + * + * Tries to disable vmmouse mode. + */ +static void vmmouse_disable(struct psmouse *psmouse) +{ + u32 status; + u32 dummy1, dummy2, dummy3, dummy4; + + VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE, + dummy1, dummy2, dummy3, dummy4); + + VMMOUSE_CMD(ABSPOINTER_STATUS, 0, + status, dummy1, dummy2, dummy3); + + if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR) + psmouse_warn(psmouse, "failed to disable vmmouse device\n"); +} + +/** + * vmmouse_enable - Enable vmmouse and request absolute mode. + * + * @psmouse: Pointer to the psmouse struct + * + * Tries to enable vmmouse mode. Performs basic checks and requests + * absolute vmmouse mode. + * Returns 0 on success, -ENODEV on failure. + */ +static int vmmouse_enable(struct psmouse *psmouse) +{ + u32 status, version; + u32 dummy1, dummy2, dummy3, dummy4; + + /* + * Try enabling the device. If successful, we should be able to + * read valid version ID back from it. + */ + VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE, + dummy1, dummy2, dummy3, dummy4); + + /* + * See if version ID can be retrieved. + */ + VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3); + if ((status & 0x0000ffff) == 0) { + psmouse_dbg(psmouse, "empty flags - assuming no device\n"); + return -ENXIO; + } + + VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */, + version, dummy1, dummy2, dummy3); + if (version != VMMOUSE_VERSION_ID) { + psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n", + (unsigned) version, VMMOUSE_VERSION_ID); + vmmouse_disable(psmouse); + return -ENXIO; + } + + /* + * Restrict ioport access, if possible. + */ + VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0, + dummy1, dummy2, dummy3, dummy4); + + VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE, + dummy1, dummy2, dummy3, dummy4); + + return 0; +} + +/* + * Array of supported hypervisors. + */ +static const struct hypervisor_x86 *vmmouse_supported_hypervisors[] = { + &x86_hyper_vmware, +#ifdef CONFIG_KVM_GUEST + &x86_hyper_kvm, +#endif +}; + +/** + * vmmouse_check_hypervisor - Check if we're running on a supported hypervisor + */ +static bool vmmouse_check_hypervisor(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++) + if (vmmouse_supported_hypervisors[i] == x86_hyper) + return true; + + return false; +} + +/** + * vmmouse_detect - Probe whether vmmouse is available + * + * @psmouse: Pointer to the psmouse struct + * @set_properties: Whether to set psmouse name and vendor + * + * Returns 0 if vmmouse channel is available. Negative error code if not. + */ +int vmmouse_detect(struct psmouse *psmouse, bool set_properties) +{ + u32 response, version, dummy1, dummy2; + + if (!vmmouse_check_hypervisor()) { + psmouse_dbg(psmouse, + "VMMouse not running on supported hypervisor.\n"); + return -ENXIO; + } + + if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) { + psmouse_dbg(psmouse, "VMMouse port in use.\n"); + return -EBUSY; + } + + /* Check if the device is present */ + response = ~VMMOUSE_PROTO_MAGIC; + VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2); + if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU) { + release_region(VMMOUSE_PROTO_PORT, 4); + return -ENXIO; + } + + if (set_properties) { + psmouse->vendor = VMMOUSE_VENDOR; + psmouse->name = VMMOUSE_NAME; + psmouse->model = version; + } + + release_region(VMMOUSE_PROTO_PORT, 4); + + return 0; +} + +/** + * vmmouse_disconnect - Take down vmmouse driver + * + * @psmouse: Pointer to the psmouse struct + * + * Takes down vmmouse driver and frees resources set up in vmmouse_init(). + */ +static void vmmouse_disconnect(struct psmouse *psmouse) +{ + struct vmmouse_data *priv = psmouse->private; + + vmmouse_disable(psmouse); + psmouse_reset(psmouse); + input_unregister_device(priv->abs_dev); + kfree(priv); + release_region(VMMOUSE_PROTO_PORT, 4); +} + +/** + * vmmouse_reconnect - Reset the ps/2 - and vmmouse connections + * + * @psmouse: Pointer to the psmouse struct + * + * Attempts to reset the mouse connections. Returns 0 on success and + * -1 on failure. + */ +static int vmmouse_reconnect(struct psmouse *psmouse) +{ + int error; + + psmouse_reset(psmouse); + vmmouse_disable(psmouse); + error = vmmouse_enable(psmouse); + if (error) { + psmouse_err(psmouse, + "Unable to re-enable mouse when reconnecting, err: %d\n", + error); + return error; + } + + return 0; +} + +/** + * vmmouse_init - Initialize the vmmouse driver + * + * @psmouse: Pointer to the psmouse struct + * + * Requests the device and tries to enable vmmouse mode. + * If successful, sets up the input device for relative movement events. + * It also allocates another input device and sets it up for absolute motion + * events. Returns 0 on success and -1 on failure. + */ +int vmmouse_init(struct psmouse *psmouse) +{ + struct vmmouse_data *priv; + struct input_dev *rel_dev = psmouse->dev, *abs_dev; + int error; + + if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) { + psmouse_dbg(psmouse, "VMMouse port in use.\n"); + return -EBUSY; + } + + psmouse_reset(psmouse); + error = vmmouse_enable(psmouse); + if (error) + goto release_region; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + abs_dev = input_allocate_device(); + if (!priv || !abs_dev) { + error = -ENOMEM; + goto init_fail; + } + + priv->abs_dev = abs_dev; + psmouse->private = priv; + + input_set_capability(rel_dev, EV_REL, REL_WHEEL); + + /* Set up and register absolute device */ + snprintf(priv->phys, sizeof(priv->phys), "%s/input1", + psmouse->ps2dev.serio->phys); + + /* Mimic name setup for relative device in psmouse-base.c */ + snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s", + VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME); + abs_dev->phys = priv->phys; + abs_dev->name = priv->dev_name; + abs_dev->id.bustype = BUS_I8042; + abs_dev->id.vendor = 0x0002; + abs_dev->id.product = PSMOUSE_VMMOUSE; + abs_dev->id.version = psmouse->model; + abs_dev->dev.parent = &psmouse->ps2dev.serio->dev; + + error = input_register_device(priv->abs_dev); + if (error) + goto init_fail; + + /* Set absolute device capabilities */ + input_set_capability(abs_dev, EV_KEY, BTN_LEFT); + input_set_capability(abs_dev, EV_KEY, BTN_RIGHT); + input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE); + input_set_capability(abs_dev, EV_ABS, ABS_X); + input_set_capability(abs_dev, EV_ABS, ABS_Y); + input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0); + input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0); + + psmouse->protocol_handler = vmmouse_process_byte; + psmouse->disconnect = vmmouse_disconnect; + psmouse->reconnect = vmmouse_reconnect; + + return 0; + +init_fail: + vmmouse_disable(psmouse); + psmouse_reset(psmouse); + input_free_device(abs_dev); + kfree(priv); + psmouse->private = NULL; + +release_region: + release_region(VMMOUSE_PROTO_PORT, 4); + + return error; +} diff --git a/drivers/input/mouse/vmmouse.h b/drivers/input/mouse/vmmouse.h new file mode 100644 index 000000000000..6f126017a24c --- /dev/null +++ b/drivers/input/mouse/vmmouse.h @@ -0,0 +1,30 @@ +/* + * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors. + * + * Copyright (C) 2014, VMware, Inc. All Rights Reserved. + * + * 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. + */ + +#ifndef _VMMOUSE_H +#define _VMMOUSE_H + +#ifdef CONFIG_MOUSE_PS2_VMMOUSE +#define VMMOUSE_PSNAME "VirtualPS/2" + +int vmmouse_detect(struct psmouse *psmouse, bool set_properties); +int vmmouse_init(struct psmouse *psmouse); +#else +static inline int vmmouse_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +static inline int vmmouse_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif + +#endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 547f67d65372..80f6386709bf 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -980,7 +980,9 @@ config TOUCHSCREEN_SUN4I config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" depends on USB + depends on MEDIA_USB_SUPPORT select INPUT_POLLDEV + select VIDEOBUF2_DMA_SG help Say Y here if you want support for the Samsung SUR40 touchscreen (also known as Microsoft Surface 2.0 or Microsoft PixelSense). diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2875ddf37289..40b98dda8f38 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -14,6 +14,8 @@ * */ +#include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/module.h> #include <linux/init.h> #include <linux/completion.h> @@ -2371,7 +2373,7 @@ static void mxt_input_close(struct input_dev *dev) } #ifdef CONFIG_OF -static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { struct mxt_platform_data *pdata; u32 *keymap; @@ -2379,7 +2381,7 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) int proplen, i, ret; if (!client->dev.of_node) - return ERR_PTR(-ENODEV); + return ERR_PTR(-ENOENT); pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -2410,25 +2412,132 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) return pdata; } #else -static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { - dev_dbg(&client->dev, "No platform data specified\n"); - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOENT); +} +#endif + +#ifdef CONFIG_ACPI + +struct mxt_acpi_platform_data { + const char *hid; + struct mxt_platform_data pdata; +}; + +static unsigned int samus_touchpad_buttons[] = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + +static struct mxt_acpi_platform_data samus_platform_data[] = { + { + /* Touchpad */ + .hid = "ATML0000", + .pdata = { + .t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons), + .t19_keymap = samus_touchpad_buttons, + }, + }, + { + /* Touchscreen */ + .hid = "ATML0001", + }, + { } +}; + +static const struct dmi_system_id mxt_dmi_table[] = { + { + /* 2015 Google Pixel */ + .ident = "Chromebook Pixel 2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), + }, + .driver_data = samus_platform_data, + }, + { } +}; + +static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client) +{ + struct acpi_device *adev; + const struct dmi_system_id *system_id; + const struct mxt_acpi_platform_data *acpi_pdata; + + /* + * Ignore ACPI devices representing bootloader mode. + * + * This is a bit of a hack: Google Chromebook BIOS creates ACPI + * devices for both application and bootloader modes, but we are + * interested in application mode only (if device is in bootloader + * mode we'll end up switching into application anyway). So far + * application mode addresses were all above 0x40, so we'll use it + * as a threshold. + */ + if (client->addr < 0x40) + return ERR_PTR(-ENXIO); + + adev = ACPI_COMPANION(&client->dev); + if (!adev) + return ERR_PTR(-ENOENT); + + system_id = dmi_first_match(mxt_dmi_table); + if (!system_id) + return ERR_PTR(-ENOENT); + + acpi_pdata = system_id->driver_data; + if (!acpi_pdata) + return ERR_PTR(-ENOENT); + + while (acpi_pdata->hid) { + if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid)) + return &acpi_pdata->pdata; + + acpi_pdata++; + } + + return ERR_PTR(-ENOENT); +} +#else +static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client) +{ + return ERR_PTR(-ENOENT); } #endif +static const struct mxt_platform_data * +mxt_get_platform_data(struct i2c_client *client) +{ + const struct mxt_platform_data *pdata; + + pdata = dev_get_platdata(&client->dev); + if (pdata) + return pdata; + + pdata = mxt_parse_dt(client); + if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT) + return pdata; + + pdata = mxt_parse_acpi(client); + if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT) + return pdata; + + dev_err(&client->dev, "No platform data specified\n"); + return ERR_PTR(-EINVAL); +} + static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mxt_data *data; const struct mxt_platform_data *pdata; int error; - pdata = dev_get_platdata(&client->dev); - if (!pdata) { - pdata = mxt_parse_dt(client); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - } + pdata = mxt_get_platform_data(client); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); if (!data) { @@ -2536,6 +2645,15 @@ static const struct of_device_id mxt_of_match[] = { }; MODULE_DEVICE_TABLE(of, mxt_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id mxt_acpi_id[] = { + { "ATML0000", 0 }, /* Touchpad */ + { "ATML0001", 0 }, /* Touchscreen */ + { } +}; +MODULE_DEVICE_TABLE(acpi, mxt_acpi_id); +#endif + static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, @@ -2550,6 +2668,7 @@ static struct i2c_driver mxt_driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mxt_of_match), + .acpi_match_table = ACPI_PTR(mxt_acpi_id), .pm = &mxt_pm_ops, }, .probe = mxt_probe, diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 43b3c9c2d788..0efd766a545b 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -699,7 +699,7 @@ static int elants_i2c_fw_update(struct elants_data *ts) char *fw_name; int error; - fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version); + fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version); if (!fw_name) return -ENOMEM; diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 2d5ff86b343f..e4c31256a74d 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -164,7 +164,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); /* start polling for touch_det to detect release */ - schedule_delayed_work(&ts->work, HZ / 50); + schedule_delayed_work(&ts->work, msecs_to_jiffies(50)); return IRQ_HANDLED; } diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index f1cb05148b46..a24eba5ea843 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -1,7 +1,7 @@ /* * Surface2.0/SUR40/PixelSense input driver * - * Copyright (c) 2013 by Florian 'floe' Echtler <floe@butterbrot.org> + * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org> * * Derived from the USB Skeleton driver 1.1, * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) @@ -12,6 +12,9 @@ * and from the generic hid-multitouch driver, * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> * + * and from the v4l2-pci-skeleton driver, + * Copyright (c) Copyright 2014 Cisco Systems, Inc. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of @@ -31,6 +34,11 @@ #include <linux/input-polldev.h> #include <linux/input/mt.h> #include <linux/usb/input.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-sg.h> /* read 512 bytes from endpoint 0x86 -> get header + blobs */ struct sur40_header { @@ -82,9 +90,19 @@ struct sur40_data { struct sur40_blob blobs[]; } __packed; +/* read 512 bytes from endpoint 0x82 -> get header below + * continue reading 16k blocks until header.size bytes read */ +struct sur40_image_header { + __le32 magic; /* "SUBF" */ + __le32 packet_id; + __le32 size; /* always 0x0007e900 = 960x540 */ + __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */ + __le32 unknown; /* "epoch?" always 02/03 00 00 00 */ +} __packed; /* version information */ #define DRIVER_SHORT "sur40" +#define DRIVER_LONG "Samsung SUR40" #define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>" #define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver" @@ -99,6 +117,13 @@ struct sur40_data { /* touch data endpoint */ #define TOUCH_ENDPOINT 0x86 +/* video data endpoint */ +#define VIDEO_ENDPOINT 0x82 + +/* video header fields */ +#define VIDEO_HEADER_MAGIC 0x46425553 +#define VIDEO_PACKET_SIZE 16384 + /* polling interval (ms) */ #define POLL_INTERVAL 10 @@ -113,21 +138,23 @@ struct sur40_data { #define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ #define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ -/* - * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT - * here by mistake which is very likely to have corrupted the firmware EEPROM - * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. - * Should you ever run into a similar problem, the background story to this - * incident and instructions on how to fix the corrupted EEPROM are available - * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html -*/ - +/* master device state */ struct sur40_state { struct usb_device *usbdev; struct device *dev; struct input_polled_dev *input; + struct v4l2_device v4l2; + struct video_device vdev; + struct mutex lock; + + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + struct list_head buf_list; + spinlock_t qlock; + int sequence; + struct sur40_data *bulk_in_buffer; size_t bulk_in_size; u8 bulk_in_epaddr; @@ -135,6 +162,27 @@ struct sur40_state { char phys[64]; }; +struct sur40_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +/* forward declarations */ +static const struct video_device sur40_video_device; +static const struct v4l2_pix_format sur40_video_format; +static const struct vb2_queue sur40_queue; +static void sur40_process_video(struct sur40_state *sur40); + +/* + * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT + * here by mistake which is very likely to have corrupted the firmware EEPROM + * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. + * Should you ever run into a similar problem, the background story to this + * incident and instructions on how to fix the corrupted EEPROM are available + * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html +*/ + +/* command wrapper */ static int sur40_command(struct sur40_state *dev, u8 command, u16 index, void *buffer, u16 size) { @@ -247,7 +295,6 @@ static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input) /* core function: poll for new input data */ static void sur40_poll(struct input_polled_dev *polldev) { - struct sur40_state *sur40 = polldev->private; struct input_dev *input = polldev->input; int result, bulk_read, need_blobs, packet_blobs, i; @@ -314,6 +361,86 @@ static void sur40_poll(struct input_polled_dev *polldev) input_mt_sync_frame(input); input_sync(input); + + sur40_process_video(sur40); +} + +/* deal with video data */ +static void sur40_process_video(struct sur40_state *sur40) +{ + + struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer); + struct sur40_buffer *new_buf; + struct usb_sg_request sgr; + struct sg_table *sgt; + int result, bulk_read; + + if (!vb2_start_streaming_called(&sur40->queue)) + return; + + /* get a new buffer from the list */ + spin_lock(&sur40->qlock); + if (list_empty(&sur40->buf_list)) { + dev_dbg(sur40->dev, "buffer queue empty\n"); + spin_unlock(&sur40->qlock); + return; + } + new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); + list_del(&new_buf->list); + spin_unlock(&sur40->qlock); + + /* retrieve data via bulk read */ + result = usb_bulk_msg(sur40->usbdev, + usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), + sur40->bulk_in_buffer, sur40->bulk_in_size, + &bulk_read, 1000); + + if (result < 0) { + dev_err(sur40->dev, "error in usb_bulk_read\n"); + goto err_poll; + } + + if (bulk_read != sizeof(struct sur40_image_header)) { + dev_err(sur40->dev, "received %d bytes (%zd expected)\n", + bulk_read, sizeof(struct sur40_image_header)); + goto err_poll; + } + + if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) { + dev_err(sur40->dev, "image magic mismatch\n"); + goto err_poll; + } + + if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) { + dev_err(sur40->dev, "image size mismatch\n"); + goto err_poll; + } + + sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); + + result = usb_sg_init(&sgr, sur40->usbdev, + usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, + sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0); + if (result < 0) { + dev_err(sur40->dev, "error %d in usb_sg_init\n", result); + goto err_poll; + } + + usb_sg_wait(&sgr); + if (sgr.status < 0) { + dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status); + goto err_poll; + } + + /* mark as finished */ + v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); + new_buf->vb.v4l2_buf.sequence = sur40->sequence++; + new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + return; + +err_poll: + vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR); } /* Initialize input device parameters. */ @@ -377,6 +504,11 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_dev; } + /* initialize locks/lists */ + INIT_LIST_HEAD(&sur40->buf_list); + spin_lock_init(&sur40->qlock); + mutex_init(&sur40->lock); + /* Set up polled input device control structure */ poll_dev->private = sur40; poll_dev->poll_interval = POLL_INTERVAL; @@ -387,7 +519,7 @@ static int sur40_probe(struct usb_interface *interface, /* Set up regular input device structure */ sur40_input_setup(poll_dev->input); - poll_dev->input->name = "Samsung SUR40"; + poll_dev->input->name = DRIVER_LONG; usb_to_input_id(usbdev, &poll_dev->input->id); usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys)); strlcat(sur40->phys, "/input0", sizeof(sur40->phys)); @@ -408,6 +540,7 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_polldev; } + /* register the polled input device */ error = input_register_polled_device(poll_dev); if (error) { dev_err(&interface->dev, @@ -415,12 +548,54 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_buffer; } + /* register the video master device */ + snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); + error = v4l2_device_register(sur40->dev, &sur40->v4l2); + if (error) { + dev_err(&interface->dev, + "Unable to register video master device."); + goto err_unreg_v4l2; + } + + /* initialize the lock and subdevice */ + sur40->queue = sur40_queue; + sur40->queue.drv_priv = sur40; + sur40->queue.lock = &sur40->lock; + + /* initialize the queue */ + error = vb2_queue_init(&sur40->queue); + if (error) + goto err_unreg_v4l2; + + sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev); + if (IS_ERR(sur40->alloc_ctx)) { + dev_err(sur40->dev, "Can't allocate buffer context"); + goto err_unreg_v4l2; + } + + sur40->vdev = sur40_video_device; + sur40->vdev.v4l2_dev = &sur40->v4l2; + sur40->vdev.lock = &sur40->lock; + sur40->vdev.queue = &sur40->queue; + video_set_drvdata(&sur40->vdev, sur40); + + error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1); + if (error) { + dev_err(&interface->dev, + "Unable to register video subdevice."); + goto err_unreg_video; + } + /* we can register the device now, as it is ready */ usb_set_intfdata(interface, sur40); dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); return 0; +err_unreg_video: + video_unregister_device(&sur40->vdev); +err_unreg_v4l2: + v4l2_device_unregister(&sur40->v4l2); err_free_buffer: kfree(sur40->bulk_in_buffer); err_free_polldev: @@ -436,6 +611,10 @@ static void sur40_disconnect(struct usb_interface *interface) { struct sur40_state *sur40 = usb_get_intfdata(interface); + video_unregister_device(&sur40->vdev); + v4l2_device_unregister(&sur40->v4l2); + vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx); + input_unregister_polled_device(sur40->input); input_free_polled_device(sur40->input); kfree(sur40->bulk_in_buffer); @@ -445,12 +624,243 @@ static void sur40_disconnect(struct usb_interface *interface) dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC); } +/* + * Setup the constraints of the queue: besides setting the number of planes + * per buffer and the size and allocation context of each plane, it also + * checks if sufficient buffers have been allocated. Usually 3 is a good + * minimum number: many DMA engines need a minimum of 2 buffers in the + * queue and you need to have another available for userspace processing. + */ +static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(q); + + if (q->num_buffers + *nbuffers < 3) + *nbuffers = 3 - q->num_buffers; + + if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage) + return -EINVAL; + + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage; + alloc_ctxs[0] = sur40->alloc_ctx; + + return 0; +} + +/* + * Prepare the buffer for queueing to the DMA engine: check and set the + * payload size. + */ +static int sur40_buffer_prepare(struct vb2_buffer *vb) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = sur40_video_format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + return 0; +} + +/* + * Queue this buffer to the DMA engine. + */ +static void sur40_buffer_queue(struct vb2_buffer *vb) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); + struct sur40_buffer *buf = (struct sur40_buffer *)vb; + + spin_lock(&sur40->qlock); + list_add_tail(&buf->list, &sur40->buf_list); + spin_unlock(&sur40->qlock); +} + +static void return_all_buffers(struct sur40_state *sur40, + enum vb2_buffer_state state) +{ + struct sur40_buffer *buf, *node; + + spin_lock(&sur40->qlock); + list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { + vb2_buffer_done(&buf->vb, state); + list_del(&buf->list); + } + spin_unlock(&sur40->qlock); +} + +/* + * Start streaming. First check if the minimum number of buffers have been + * queued. If not, then return -ENOBUFS and the vb2 framework will call + * this function again the next time a buffer has been queued until enough + * buffers are available to actually start the DMA engine. + */ +static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vq); + + sur40->sequence = 0; + return 0; +} + +/* + * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued + * and passed on to the vb2 framework marked as STATE_ERROR. + */ +static void sur40_stop_streaming(struct vb2_queue *vq) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vq); + + /* Release all active buffers */ + return_all_buffers(sur40, VB2_BUF_STATE_ERROR); +} + +/* V4L ioctl */ +static int sur40_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct sur40_state *sur40 = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver)); + strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card)); + usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int sur40_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = V4L2_STD_UNKNOWN; + strlcpy(i->name, "In-Cell Sensor", sizeof(i->name)); + i->capabilities = 0; + return 0; +} + +static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return (i == 0) ? 0 : -EINVAL; +} + +static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int sur40_vidioc_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + f->fmt.pix = sur40_video_format; + return 0; +} + +static int sur40_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + strlcpy(f->description, "8-bit greyscale", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_GREY; + f->flags = 0; + return 0; +} + static const struct usb_device_id sur40_table[] = { { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */ { } /* terminating null entry */ }; MODULE_DEVICE_TABLE(usb, sur40_table); +/* V4L2 structures */ +static const struct vb2_ops sur40_queue_ops = { + .queue_setup = sur40_queue_setup, + .buf_prepare = sur40_buffer_prepare, + .buf_queue = sur40_buffer_queue, + .start_streaming = sur40_start_streaming, + .stop_streaming = sur40_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue sur40_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + /* + * VB2_USERPTR in currently not enabled: passing a user pointer to + * dma-sg will result in segment sizes that are not a multiple of + * 512 bytes, which is required by the host controller. + */ + .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF, + .buf_struct_size = sizeof(struct sur40_buffer), + .ops = &sur40_queue_ops, + .mem_ops = &vb2_dma_sg_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 3, +}; + +static const struct v4l2_file_operations sur40_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { + + .vidioc_querycap = sur40_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt, + .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + + .vidioc_enum_input = sur40_vidioc_enum_input, + .vidioc_g_input = sur40_vidioc_g_input, + .vidioc_s_input = sur40_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device sur40_video_device = { + .name = DRIVER_LONG, + .fops = &sur40_video_fops, + .ioctl_ops = &sur40_video_ioctl_ops, + .release = video_device_release_empty, +}; + +static const struct v4l2_pix_format sur40_video_format = { + .pixelformat = V4L2_PIX_FMT_GREY, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), +}; + /* USB-specific object needed to register this driver with the USB subsystem. */ static struct usb_driver sur40_driver = { .name = DRIVER_SHORT, diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index aecb9ad2e701..642f4a53de50 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -187,7 +187,7 @@ static int sx8654_probe(struct i2c_client *client, return -ENOMEM; input = devm_input_allocate_device(&client->dev); - if (!sx8654) + if (!input) return -ENOMEM; input->name = "SX8654 I2C Touchscreen"; |