diff options
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/appletouch.c | 143 | ||||
-rw-r--r-- | drivers/input/mouse/cypress_ps2.c | 1 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.c | 55 |
3 files changed, 163 insertions, 36 deletions
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 800ca7dfafc..ef234c9b2f2 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -48,6 +48,7 @@ struct atp_info { int yfact; /* Y multiplication factor */ int datalen; /* size of USB transfers */ void (*callback)(struct urb *); /* callback function */ + int fuzz; /* fuzz touchpad generates */ }; static void atp_complete_geyser_1_2(struct urb *urb); @@ -61,6 +62,7 @@ static const struct atp_info fountain_info = { .yfact = 43, .datalen = 81, .callback = atp_complete_geyser_1_2, + .fuzz = 16, }; static const struct atp_info geyser1_info = { @@ -71,6 +73,7 @@ static const struct atp_info geyser1_info = { .yfact = 43, .datalen = 81, .callback = atp_complete_geyser_1_2, + .fuzz = 16, }; static const struct atp_info geyser2_info = { @@ -81,6 +84,7 @@ static const struct atp_info geyser2_info = { .yfact = 43, .datalen = 64, .callback = atp_complete_geyser_1_2, + .fuzz = 0, }; static const struct atp_info geyser3_info = { @@ -90,6 +94,7 @@ static const struct atp_info geyser3_info = { .yfact = 64, .datalen = 64, .callback = atp_complete_geyser_3_4, + .fuzz = 0, }; static const struct atp_info geyser4_info = { @@ -99,6 +104,7 @@ static const struct atp_info geyser4_info = { .yfact = 64, .datalen = 64, .callback = atp_complete_geyser_3_4, + .fuzz = 0, }; #define ATP_DEVICE(prod, info) \ @@ -155,8 +161,11 @@ MODULE_DEVICE_TABLE(usb, atp_table); #define ATP_XSENSORS 26 #define ATP_YSENSORS 16 -/* amount of fuzz this touchpad generates */ -#define ATP_FUZZ 16 +/* + * The largest possible bank of sensors with additional buffer of 4 extra values + * on either side, for an array of smoothed sensor values. + */ +#define ATP_SMOOTHSIZE 34 /* maximum pressure this driver will report */ #define ATP_PRESSURE 300 @@ -165,7 +174,13 @@ MODULE_DEVICE_TABLE(usb, atp_table); * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is * ignored. */ -#define ATP_THRESHOLD 5 +#define ATP_THRESHOLD 5 + +/* + * How far we'll bitshift our sensor values before averaging them. Mitigates + * rounding errors. + */ +#define ATP_SCALE 12 /* Geyser initialization constants */ #define ATP_GEYSER_MODE_READ_REQUEST_ID 1 @@ -203,11 +218,14 @@ struct atp { bool valid; /* are the samples valid? */ bool size_detect_done; bool overflow_warned; + int fingers_old; /* last reported finger count */ int x_old; /* last reported x/y, */ int y_old; /* used for smoothing */ signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS]; signed char xy_old[ATP_XSENSORS + ATP_YSENSORS]; int xy_acc[ATP_XSENSORS + ATP_YSENSORS]; + int smooth[ATP_SMOOTHSIZE]; + int smooth_tmp[ATP_SMOOTHSIZE]; int idlecount; /* number of empty packets */ struct work_struct work; }; @@ -326,10 +344,17 @@ static void atp_reinit(struct work_struct *work) retval); } -static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, - int *z, int *fingers) +static int atp_calculate_abs(struct atp *dev, int offset, int nb_sensors, + int fact, int *z, int *fingers) { - int i; + int i, pass; + + /* + * Use offset to point xy_sensors at the first value in dev->xy_acc + * for whichever dimension we're looking at this particular go-round. + */ + int *xy_sensors = dev->xy_acc + offset; + /* values to calculate mean */ int pcum = 0, psum = 0; int is_increasing = 0; @@ -341,9 +366,6 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, if (is_increasing) is_increasing = 0; - continue; - } - /* * Makes the finger detection more versatile. For example, * two fingers with no gap will be detected. Also, my @@ -358,27 +380,63 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, * * - Jason Parekh <jasonparekh@gmail.com> */ - if (i < 1 || + + } else if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) { (*fingers)++; is_increasing = 1; } else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) { is_increasing = 0; } + } + + if (*fingers < 1) /* No need to continue if no fingers are found. */ + return 0; + /* + * Use a smoothed version of sensor data for movement calculations, to + * combat noise without needing to rely so heavily on a threshold. + * This improves tracking. + * + * The smoothed array is bigger than the original so that the smoothing + * doesn't result in edge values being truncated. + */ + + memset(dev->smooth, 0, 4 * sizeof(dev->smooth[0])); + /* Pull base values, scaled up to help avoid truncation errors. */ + for (i = 0; i < nb_sensors; i++) + dev->smooth[i + 4] = xy_sensors[i] << ATP_SCALE; + memset(&dev->smooth[nb_sensors + 4], 0, 4 * sizeof(dev->smooth[0])); + + for (pass = 0; pass < 4; pass++) { + /* Handle edge. */ + dev->smooth_tmp[0] = (dev->smooth[0] + dev->smooth[1]) / 2; + + /* Average values with neighbors. */ + for (i = 1; i < nb_sensors + 7; i++) + dev->smooth_tmp[i] = (dev->smooth[i - 1] + + dev->smooth[i] * 2 + + dev->smooth[i + 1]) / 4; + + /* Handle other edge. */ + dev->smooth_tmp[i] = (dev->smooth[i - 1] + dev->smooth[i]) / 2; + + memcpy(dev->smooth, dev->smooth_tmp, sizeof(dev->smooth)); + } + + for (i = 0; i < nb_sensors + 8; i++) { /* - * Subtracts threshold so a high sensor that just passes the - * threshold won't skew the calculated absolute coordinate. - * Fixes an issue where slowly moving the mouse would - * occasionally jump a number of pixels (slowly moving the - * finger makes this issue most apparent.) + * Skip values if they're small enough to be truncated to 0 + * by scale. Mostly noise. */ - pcum += (xy_sensors[i] - threshold) * i; - psum += (xy_sensors[i] - threshold); + if ((dev->smooth[i] >> ATP_SCALE) > 0) { + pcum += dev->smooth[i] * i; + psum += dev->smooth[i]; + } } if (psum > 0) { - *z = psum; + *z = psum >> ATP_SCALE; /* Scale down pressure output. */ return pcum * fact / psum; } @@ -455,7 +513,7 @@ static void atp_detect_size(struct atp *dev) input_set_abs_params(dev->input, ABS_X, 0, (dev->info->xsensors_17 - 1) * dev->info->xfact - 1, - ATP_FUZZ, 0); + dev->info->fuzz, 0); break; } } @@ -471,7 +529,7 @@ static void atp_complete_geyser_1_2(struct urb *urb) { int x, y, x_z, y_z, x_f, y_f; int retval, i, j; - int key; + int key, fingers; struct atp *dev = urb->context; int status = atp_status_check(urb); @@ -548,16 +606,18 @@ static void atp_complete_geyser_1_2(struct urb *urb) dbg_dump("accumulator", dev->xy_acc); - x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + x = atp_calculate_abs(dev, 0, ATP_XSENSORS, dev->info->xfact, &x_z, &x_f); - y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS, dev->info->yfact, &y_z, &y_f); key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; - if (x && y) { + fingers = max(x_f, y_f); + + if (x && y && fingers == dev->fingers_old) { if (dev->x_old != -1) { - x = (dev->x_old * 3 + x) >> 2; - y = (dev->y_old * 3 + y) >> 2; + x = (dev->x_old * 7 + x) >> 3; + y = (dev->y_old * 7 + y) >> 3; dev->x_old = x; dev->y_old = y; @@ -571,7 +631,7 @@ static void atp_complete_geyser_1_2(struct urb *urb) input_report_abs(dev->input, ABS_Y, y); input_report_abs(dev->input, ABS_PRESSURE, min(ATP_PRESSURE, x_z + y_z)); - atp_report_fingers(dev->input, max(x_f, y_f)); + atp_report_fingers(dev->input, fingers); } dev->x_old = x; dev->y_old = y; @@ -579,6 +639,7 @@ static void atp_complete_geyser_1_2(struct urb *urb) } else if (!x && !y) { dev->x_old = dev->y_old = -1; + dev->fingers_old = 0; input_report_key(dev->input, BTN_TOUCH, 0); input_report_abs(dev->input, ABS_PRESSURE, 0); atp_report_fingers(dev->input, 0); @@ -587,6 +648,10 @@ static void atp_complete_geyser_1_2(struct urb *urb) memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); } + if (fingers != dev->fingers_old) + dev->x_old = dev->y_old = -1; + dev->fingers_old = fingers; + input_report_key(dev->input, BTN_LEFT, key); input_sync(dev->input); @@ -604,7 +669,7 @@ static void atp_complete_geyser_3_4(struct urb *urb) { int x, y, x_z, y_z, x_f, y_f; int retval, i, j; - int key; + int key, fingers; struct atp *dev = urb->context; int status = atp_status_check(urb); @@ -660,16 +725,19 @@ static void atp_complete_geyser_3_4(struct urb *urb) dbg_dump("accumulator", dev->xy_acc); - x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + x = atp_calculate_abs(dev, 0, ATP_XSENSORS, dev->info->xfact, &x_z, &x_f); - y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS, dev->info->yfact, &y_z, &y_f); + key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; - if (x && y) { + fingers = max(x_f, y_f); + + if (x && y && fingers == dev->fingers_old) { if (dev->x_old != -1) { - x = (dev->x_old * 3 + x) >> 2; - y = (dev->y_old * 3 + y) >> 2; + x = (dev->x_old * 7 + x) >> 3; + y = (dev->y_old * 7 + y) >> 3; dev->x_old = x; dev->y_old = y; @@ -683,7 +751,7 @@ static void atp_complete_geyser_3_4(struct urb *urb) input_report_abs(dev->input, ABS_Y, y); input_report_abs(dev->input, ABS_PRESSURE, min(ATP_PRESSURE, x_z + y_z)); - atp_report_fingers(dev->input, max(x_f, y_f)); + atp_report_fingers(dev->input, fingers); } dev->x_old = x; dev->y_old = y; @@ -691,6 +759,7 @@ static void atp_complete_geyser_3_4(struct urb *urb) } else if (!x && !y) { dev->x_old = dev->y_old = -1; + dev->fingers_old = 0; input_report_key(dev->input, BTN_TOUCH, 0); input_report_abs(dev->input, ABS_PRESSURE, 0); atp_report_fingers(dev->input, 0); @@ -699,6 +768,10 @@ static void atp_complete_geyser_3_4(struct urb *urb) memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); } + if (fingers != dev->fingers_old) + dev->x_old = dev->y_old = -1; + dev->fingers_old = fingers; + input_report_key(dev->input, BTN_LEFT, key); input_sync(dev->input); @@ -843,10 +916,10 @@ static int atp_probe(struct usb_interface *iface, input_set_abs_params(input_dev, ABS_X, 0, (dev->info->xsensors - 1) * dev->info->xfact - 1, - ATP_FUZZ, 0); + dev->info->fuzz, 0); input_set_abs_params(input_dev, ABS_Y, 0, (dev->info->ysensors - 1) * dev->info->yfact - 1, - ATP_FUZZ, 0); + dev->info->fuzz, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0); set_bit(EV_KEY, input_dev->evbit); diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c index 87095e2f515..8af34ffe208 100644 --- a/drivers/input/mouse/cypress_ps2.c +++ b/drivers/input/mouse/cypress_ps2.c @@ -409,7 +409,6 @@ static int cypress_set_input_params(struct input_dev *input, __clear_bit(REL_X, input->relbit); __clear_bit(REL_Y, input->relbit); - __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); __set_bit(EV_KEY, input->evbit); __set_bit(BTN_LEFT, input->keybit); __set_bit(BTN_RIGHT, input->keybit); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 26386f9d256..d8d49d10f9b 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -265,11 +265,22 @@ static int synaptics_identify(struct psmouse *psmouse) * Read touchpad resolution and maximum reported coordinates * Resolution is left zero if touchpad does not support the query */ + +static const int *quirk_min_max; + static int synaptics_resolution(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char resp[3]; + if (quirk_min_max) { + priv->x_min = quirk_min_max[0]; + priv->x_max = quirk_min_max[1]; + priv->y_min = quirk_min_max[2]; + priv->y_max = quirk_min_max[3]; + return 0; + } + if (SYN_ID_MAJOR(priv->identity) < 4) return 0; @@ -1485,10 +1496,54 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = { { } }; +static const struct dmi_system_id min_max_dmi_table[] __initconst = { +#if defined(CONFIG_DMI) + { + /* Lenovo ThinkPad Helix */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"), + }, + .driver_data = (int []){1024, 5052, 2258, 4832}, + }, + { + /* Lenovo ThinkPad X240 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X240"), + }, + .driver_data = (int []){1232, 5710, 1156, 4696}, + }, + { + /* Lenovo ThinkPad T440s */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T440"), + }, + .driver_data = (int []){1024, 5112, 2024, 4832}, + }, + { + /* Lenovo ThinkPad T540p */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T540"), + }, + .driver_data = (int []){1024, 5056, 2058, 4832}, + }, +#endif + { } +}; + void __init synaptics_module_init(void) { + const struct dmi_system_id *min_max_dmi; + impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table); + + min_max_dmi = dmi_first_match(min_max_dmi_table); + if (min_max_dmi) + quirk_min_max = min_max_dmi->driver_data; } static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) |