mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-01 08:04:22 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - an update to Elan touchpad controller driver supporting newer ICs with enhanced precision reports and a new firmware update process - an update to EXC3000 touch controller supporting additional parts - assorted driver fixups * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (27 commits) Input: exc3000 - add support to query model and fw_version Input: exc3000 - add reset gpio support Input: exc3000 - add EXC80H60 and EXC80H84 support dt-bindings: touchscreen: Convert EETI EXC3000 touchscreen to json-schema Input: sentelic - fix error return when fsp_reg_write fails Input: alps - remove redundant assignment to variable ret Input: ims-pcu - return error code rather than -ENOMEM Input: elan_i2c - add ic type 0x15 Input: atmel_mxt_ts - only read messages in mxt_acquire_irq() when necessary Input: uinput - fix typo in function name documentation Input: ati_remote2 - add missing newlines when printing module parameters Input: psmouse - add a newline when printing 'proto' by sysfs Input: synaptics-rmi4 - drop a duplicated word Input: elan_i2c - add support for high resolution reports Input: elan_i2c - do not constantly re-query pattern ID Input: elan_i2c - add firmware update info for ICs 0x11, 0x13, 0x14 Input: elan_i2c - handle firmware updated on newer ICs Input: elan_i2c - add support for different firmware page sizes Input: elan_i2c - fix detecting IAP version on older controllers Input: elan_i2c - handle devices with patterns above 1 ...
This commit is contained in:
commit
4bcf69e570
15
Documentation/ABI/testing/sysfs-driver-input-exc3000
Normal file
15
Documentation/ABI/testing/sysfs-driver-input-exc3000
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
What: /sys/bus/i2c/devices/xxx/fw_version
|
||||||
|
Date: Aug 2020
|
||||||
|
Contact: linux-input@vger.kernel.org
|
||||||
|
Description: Reports the firmware version provided by the touchscreen, for example "00_T6" on a EXC80H60
|
||||||
|
|
||||||
|
Access: Read
|
||||||
|
Valid values: Represented as string
|
||||||
|
|
||||||
|
What: /sys/bus/i2c/devices/xxx/model
|
||||||
|
Date: Aug 2020
|
||||||
|
Contact: linux-input@vger.kernel.org
|
||||||
|
Description: Reports the model identification provided by the touchscreen, for example "Orion_1320" on a EXC80H60
|
||||||
|
|
||||||
|
Access: Read
|
||||||
|
Valid values: Represented as string
|
@ -0,0 +1,58 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/input/touchscreen/eeti,exc3000.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: EETI EXC3000 series touchscreen controller
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: touchscreen.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- eeti,exc3000
|
||||||
|
- eeti,exc80h60
|
||||||
|
- eeti,exc80h84
|
||||||
|
reg:
|
||||||
|
const: 0x2a
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
reset-gpios:
|
||||||
|
maxItems: 1
|
||||||
|
touchscreen-size-x: true
|
||||||
|
touchscreen-size-y: true
|
||||||
|
touchscreen-inverted-x: true
|
||||||
|
touchscreen-inverted-y: true
|
||||||
|
touchscreen-swapped-x-y: true
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- touchscreen-size-x
|
||||||
|
- touchscreen-size-y
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include "dt-bindings/interrupt-controller/irq.h"
|
||||||
|
i2c {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
touchscreen@2a {
|
||||||
|
compatible = "eeti,exc3000";
|
||||||
|
reg = <0x2a>;
|
||||||
|
interrupt-parent = <&gpio1>;
|
||||||
|
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
touchscreen-size-x = <4096>;
|
||||||
|
touchscreen-size-y = <4096>;
|
||||||
|
touchscreen-inverted-x;
|
||||||
|
touchscreen-swapped-x-y;
|
||||||
|
};
|
||||||
|
};
|
@ -1,26 +0,0 @@
|
|||||||
* EETI EXC3000 Multiple Touch Controller
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible: must be "eeti,exc3000"
|
|
||||||
- reg: i2c slave address
|
|
||||||
- interrupts: touch controller interrupt
|
|
||||||
- touchscreen-size-x: See touchscreen.txt
|
|
||||||
- touchscreen-size-y: See touchscreen.txt
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- touchscreen-inverted-x: See touchscreen.txt
|
|
||||||
- touchscreen-inverted-y: See touchscreen.txt
|
|
||||||
- touchscreen-swapped-x-y: See touchscreen.txt
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
touchscreen@2a {
|
|
||||||
compatible = "eeti,exc3000";
|
|
||||||
reg = <0x2a>;
|
|
||||||
interrupt-parent = <&gpio1>;
|
|
||||||
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
|
|
||||||
touchscreen-size-x = <4096>;
|
|
||||||
touchscreen-size-y = <4096>;
|
|
||||||
touchscreen-inverted-x;
|
|
||||||
touchscreen-swapped-x-y;
|
|
||||||
};
|
|
@ -99,7 +99,7 @@ the sake of simplicity.
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Give userspace some time to read the events before we destroy the
|
* Give userspace some time to read the events before we destroy the
|
||||||
* device with UI_DEV_DESTOY.
|
* device with UI_DEV_DESTROY.
|
||||||
*/
|
*/
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ mouse.
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Give userspace some time to read the events before we destroy the
|
* Give userspace some time to read the events before we destroy the
|
||||||
* device with UI_DEV_DESTOY.
|
* device with UI_DEV_DESTROY.
|
||||||
*/
|
*/
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ but interact with uinput via ioctl calls, or use libevdev.
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Give userspace some time to read the events before we destroy the
|
* Give userspace some time to read the events before we destroy the
|
||||||
* device with UI_DEV_DESTOY.
|
* device with UI_DEV_DESTROY.
|
||||||
*/
|
*/
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
|
|||||||
if (dev->absinfo && test_bit(src, dev->absbit)) {
|
if (dev->absinfo && test_bit(src, dev->absbit)) {
|
||||||
dev->absinfo[dst] = dev->absinfo[src];
|
dev->absinfo[dst] = dev->absinfo[src];
|
||||||
dev->absinfo[dst].fuzz = 0;
|
dev->absinfo[dst].fuzz = 0;
|
||||||
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
|
__set_bit(dst, dev->absbit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char
|
|||||||
db9_saturn_write_sub(port, type, 3, powered, 0);
|
db9_saturn_write_sub(port, type, 3, powered, 0);
|
||||||
return data[0] = 0xe3;
|
return data[0] = 0xe3;
|
||||||
}
|
}
|
||||||
/* fall through */
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
return data[0];
|
return data[0];
|
||||||
}
|
}
|
||||||
@ -267,14 +267,14 @@ static int db9_saturn_report(unsigned char id, unsigned char data[60], struct in
|
|||||||
switch (data[j]) {
|
switch (data[j]) {
|
||||||
case 0x16: /* multi controller (analog 4 axis) */
|
case 0x16: /* multi controller (analog 4 axis) */
|
||||||
input_report_abs(dev, db9_abs[5], data[j + 6]);
|
input_report_abs(dev, db9_abs[5], data[j + 6]);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case 0x15: /* mission stick (analog 3 axis) */
|
case 0x15: /* mission stick (analog 3 axis) */
|
||||||
input_report_abs(dev, db9_abs[3], data[j + 4]);
|
input_report_abs(dev, db9_abs[3], data[j + 4]);
|
||||||
input_report_abs(dev, db9_abs[4], data[j + 5]);
|
input_report_abs(dev, db9_abs[4], data[j + 5]);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case 0x13: /* racing controller (analog 1 axis) */
|
case 0x13: /* racing controller (analog 1 axis) */
|
||||||
input_report_abs(dev, db9_abs[2], data[j + 3]);
|
input_report_abs(dev, db9_abs[2], data[j + 3]);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
|
case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
|
||||||
case 0x02: /* digital pad (digital 2 axis + buttons) */
|
case 0x02: /* digital pad (digital 2 axis + buttons) */
|
||||||
input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
|
input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
|
||||||
@ -368,7 +368,7 @@ static void db9_timer(struct timer_list *t)
|
|||||||
input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
|
input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
|
||||||
input_report_abs(dev2, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
|
input_report_abs(dev2, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
|
||||||
input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1);
|
input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case DB9_MULTI_0802:
|
case DB9_MULTI_0802:
|
||||||
|
|
||||||
|
@ -485,7 +485,7 @@ static void gc_multi_process_packet(struct gc *gc)
|
|||||||
switch (pad->type) {
|
switch (pad->type) {
|
||||||
case GC_MULTI2:
|
case GC_MULTI2:
|
||||||
input_report_key(dev, BTN_THUMB, s & data[5]);
|
input_report_key(dev, BTN_THUMB, s & data[5]);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case GC_MULTI:
|
case GC_MULTI:
|
||||||
input_report_abs(dev, ABS_X,
|
input_report_abs(dev, ABS_X,
|
||||||
@ -638,7 +638,7 @@ static void gc_psx_report_one(struct gc_pad *pad, unsigned char psx_type,
|
|||||||
|
|
||||||
input_report_key(dev, BTN_THUMBL, ~data[0] & 0x04);
|
input_report_key(dev, BTN_THUMBL, ~data[0] & 0x04);
|
||||||
input_report_key(dev, BTN_THUMBR, ~data[0] & 0x02);
|
input_report_key(dev, BTN_THUMBR, ~data[0] & 0x02);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case GC_PSX_NEGCON:
|
case GC_PSX_NEGCON:
|
||||||
case GC_PSX_ANALOG:
|
case GC_PSX_ANALOG:
|
||||||
@ -872,7 +872,8 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
|
|||||||
case GC_SNES:
|
case GC_SNES:
|
||||||
for (i = 4; i < 8; i++)
|
for (i = 4; i < 8; i++)
|
||||||
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
|
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case GC_NES:
|
case GC_NES:
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
|
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
|
||||||
@ -880,7 +881,8 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
|
|||||||
|
|
||||||
case GC_MULTI2:
|
case GC_MULTI2:
|
||||||
input_set_capability(input_dev, EV_KEY, BTN_THUMB);
|
input_set_capability(input_dev, EV_KEY, BTN_THUMB);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case GC_MULTI:
|
case GC_MULTI:
|
||||||
input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
|
input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
@ -656,16 +656,19 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
|
|||||||
|
|
||||||
switch (i * m) {
|
switch (i * m) {
|
||||||
case 60:
|
case 60:
|
||||||
sw->number++; /* fall through */
|
sw->number++;
|
||||||
|
fallthrough;
|
||||||
case 45: /* Ambiguous packet length */
|
case 45: /* Ambiguous packet length */
|
||||||
if (j <= 40) { /* ID length less or eq 40 -> FSP */
|
if (j <= 40) { /* ID length less or eq 40 -> FSP */
|
||||||
case 43:
|
case 43:
|
||||||
sw->type = SW_ID_FSP;
|
sw->type = SW_ID_FSP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sw->number++; /* fall through */
|
sw->number++;
|
||||||
|
fallthrough;
|
||||||
case 30:
|
case 30:
|
||||||
sw->number++; /* fall through */
|
sw->number++;
|
||||||
|
fallthrough;
|
||||||
case 15:
|
case 15:
|
||||||
sw->type = SW_ID_GP;
|
sw->type = SW_ID_GP;
|
||||||
break;
|
break;
|
||||||
@ -681,9 +684,11 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
|
|||||||
sw->type = SW_ID_PP;
|
sw->type = SW_ID_PP;
|
||||||
break;
|
break;
|
||||||
case 66:
|
case 66:
|
||||||
sw->bits = 3; /* fall through */
|
sw->bits = 3;
|
||||||
|
fallthrough;
|
||||||
case 198:
|
case 198:
|
||||||
sw->length = 22; /* fall through */
|
sw->length = 22;
|
||||||
|
fallthrough;
|
||||||
case 64:
|
case 64:
|
||||||
sw->type = SW_ID_3DP;
|
sw->type = SW_ID_3DP;
|
||||||
if (j == 160)
|
if (j == 160)
|
||||||
|
@ -146,7 +146,7 @@ static irqreturn_t spaceball_interrupt(struct serio *serio,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spaceball->escape = 0;
|
spaceball->escape = 0;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case 'M':
|
case 'M':
|
||||||
case 'Q':
|
case 'Q':
|
||||||
case 'S':
|
case 'S':
|
||||||
@ -154,7 +154,7 @@ static irqreturn_t spaceball_interrupt(struct serio *serio,
|
|||||||
spaceball->escape = 0;
|
spaceball->escape = 0;
|
||||||
data &= 0x1f;
|
data &= 0x1f;
|
||||||
}
|
}
|
||||||
/* fall through */
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
if (spaceball->escape)
|
if (spaceball->escape)
|
||||||
spaceball->escape = 0;
|
spaceball->escape = 0;
|
||||||
@ -220,13 +220,13 @@ static int spaceball_connect(struct serio *serio, struct serio_driver *drv)
|
|||||||
input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) |
|
input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) |
|
||||||
BIT_MASK(BTN_B) | BIT_MASK(BTN_C) |
|
BIT_MASK(BTN_B) | BIT_MASK(BTN_C) |
|
||||||
BIT_MASK(BTN_MODE);
|
BIT_MASK(BTN_MODE);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) |
|
input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) |
|
||||||
BIT_MASK(BTN_3) | BIT_MASK(BTN_4) |
|
BIT_MASK(BTN_3) | BIT_MASK(BTN_4) |
|
||||||
BIT_MASK(BTN_5) | BIT_MASK(BTN_6) |
|
BIT_MASK(BTN_5) | BIT_MASK(BTN_6) |
|
||||||
BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
|
BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case SPACEBALL_3003C:
|
case SPACEBALL_3003C:
|
||||||
input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) |
|
input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) |
|
||||||
BIT_MASK(BTN_8);
|
BIT_MASK(BTN_8);
|
||||||
|
@ -1016,7 +1016,7 @@ static int adp5589_probe(struct i2c_client *client,
|
|||||||
switch (id->driver_data) {
|
switch (id->driver_data) {
|
||||||
case ADP5585_02:
|
case ADP5585_02:
|
||||||
kpad->support_row5 = true;
|
kpad->support_row5 = true;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case ADP5585_01:
|
case ADP5585_01:
|
||||||
kpad->is_adp5585 = true;
|
kpad->is_adp5585 = true;
|
||||||
kpad->var = &const_adp5585;
|
kpad->var = &const_adp5585;
|
||||||
|
@ -1241,7 +1241,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
|
|||||||
|
|
||||||
case SERIO_8042_XL:
|
case SERIO_8042_XL:
|
||||||
atkbd->translated = true;
|
atkbd->translated = true;
|
||||||
/* Fall through */
|
fallthrough;
|
||||||
|
|
||||||
case SERIO_8042:
|
case SERIO_8042:
|
||||||
if (serio->write)
|
if (serio->write)
|
||||||
|
@ -574,7 +574,6 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
|
|||||||
IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
|
IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
|
||||||
break;
|
break;
|
||||||
case EV_ACT_ANY:
|
case EV_ACT_ANY:
|
||||||
/* fall through */
|
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
* For other cases, we are OK letting suspend/resume
|
* For other cases, we are OK letting suspend/resume
|
||||||
|
@ -68,7 +68,7 @@ static int ati_remote2_get_channel_mask(char *buffer,
|
|||||||
{
|
{
|
||||||
pr_debug("%s()\n", __func__);
|
pr_debug("%s()\n", __func__);
|
||||||
|
|
||||||
return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
|
return sprintf(buffer, "0x%04x\n", *(unsigned int *)kp->arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ati_remote2_set_mode_mask(const char *val,
|
static int ati_remote2_set_mode_mask(const char *val,
|
||||||
@ -84,7 +84,7 @@ static int ati_remote2_get_mode_mask(char *buffer,
|
|||||||
{
|
{
|
||||||
pr_debug("%s()\n", __func__);
|
pr_debug("%s()\n", __func__);
|
||||||
|
|
||||||
return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
|
return sprintf(buffer, "0x%02x\n", *(unsigned int *)kp->arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
|
static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
|
||||||
|
@ -663,12 +663,8 @@ static const struct usb_device_id cm109_usb_table[] = {
|
|||||||
static void cm109_usb_cleanup(struct cm109_dev *dev)
|
static void cm109_usb_cleanup(struct cm109_dev *dev)
|
||||||
{
|
{
|
||||||
kfree(dev->ctl_req);
|
kfree(dev->ctl_req);
|
||||||
if (dev->ctl_data)
|
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
|
||||||
usb_free_coherent(dev->udev, USB_PKT_LEN,
|
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->irq_data, dev->irq_dma);
|
||||||
dev->ctl_data, dev->ctl_dma);
|
|
||||||
if (dev->irq_data)
|
|
||||||
usb_free_coherent(dev->udev, USB_PKT_LEN,
|
|
||||||
dev->irq_data, dev->irq_dma);
|
|
||||||
|
|
||||||
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
|
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
|
||||||
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
|
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
|
||||||
|
@ -335,7 +335,7 @@ static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
|
|||||||
err_free_mem:
|
err_free_mem:
|
||||||
input_free_device(input);
|
input_free_device(input);
|
||||||
kfree(gamepad);
|
kfree(gamepad);
|
||||||
return -ENOMEM;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
|
static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
|
||||||
|
@ -575,8 +575,7 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269,
|
|||||||
|
|
||||||
case IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5:
|
case IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5:
|
||||||
engine_a |= IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
|
engine_a |= IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
case IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY:
|
case IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY:
|
||||||
engine_b |= IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE;
|
engine_b |= IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE;
|
||||||
@ -731,14 +730,12 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269,
|
|||||||
iqs269->switches[i].code = val;
|
iqs269->switches[i].code = val;
|
||||||
iqs269->switches[i].enabled = true;
|
iqs269->switches[i].enabled = true;
|
||||||
}
|
}
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
case IQS269_CHx_HALL_INACTIVE:
|
case IQS269_CHx_HALL_INACTIVE:
|
||||||
if (iqs269->hall_enable)
|
if (iqs269->hall_enable)
|
||||||
break;
|
break;
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
iqs269->keycode[i * IQS269_NUM_CH + reg] = val;
|
iqs269->keycode[i * IQS269_NUM_CH + reg] = val;
|
||||||
@ -1143,14 +1140,12 @@ static int iqs269_input_init(struct iqs269_private *iqs269)
|
|||||||
sw_code,
|
sw_code,
|
||||||
state & BIT(j));
|
state & BIT(j));
|
||||||
}
|
}
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
case IQS269_CHx_HALL_INACTIVE:
|
case IQS269_CHx_HALL_INACTIVE:
|
||||||
if (iqs269->hall_enable)
|
if (iqs269->hall_enable)
|
||||||
continue;
|
continue;
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (keycode != KEY_RESERVED)
|
if (keycode != KEY_RESERVED)
|
||||||
@ -1273,14 +1268,12 @@ static int iqs269_report(struct iqs269_private *iqs269)
|
|||||||
input_report_switch(iqs269->keypad,
|
input_report_switch(iqs269->keypad,
|
||||||
sw_code,
|
sw_code,
|
||||||
state & BIT(j));
|
state & BIT(j));
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
case IQS269_CHx_HALL_INACTIVE:
|
case IQS269_CHx_HALL_INACTIVE:
|
||||||
if (iqs269->hall_enable)
|
if (iqs269->hall_enable)
|
||||||
continue;
|
continue;
|
||||||
|
fallthrough;
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
input_report_key(iqs269->keypad, keycode,
|
input_report_key(iqs269->keypad, keycode,
|
||||||
|
@ -190,7 +190,7 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
|
dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
|
||||||
/* Fall through */
|
fallthrough;
|
||||||
|
|
||||||
case -EPROBE_DEFER:
|
case -EPROBE_DEFER:
|
||||||
return err;
|
return err;
|
||||||
|
@ -124,7 +124,7 @@ static void xenkbd_handle_mt_event(struct xenkbd_info *info,
|
|||||||
switch (mtouch->event_type) {
|
switch (mtouch->event_type) {
|
||||||
case XENKBD_MT_EV_DOWN:
|
case XENKBD_MT_EV_DOWN:
|
||||||
input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
|
input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case XENKBD_MT_EV_MOTION:
|
case XENKBD_MT_EV_MOTION:
|
||||||
input_report_abs(info->mtouch, ABS_MT_POSITION_X,
|
input_report_abs(info->mtouch, ABS_MT_POSITION_X,
|
||||||
@ -524,7 +524,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
|
|||||||
case XenbusStateClosed:
|
case XenbusStateClosed:
|
||||||
if (dev->state == XenbusStateClosed)
|
if (dev->state == XenbusStateClosed)
|
||||||
break;
|
break;
|
||||||
/* fall through - Missed the backend's CLOSING state */
|
fallthrough; /* Missed the backend's CLOSING state */
|
||||||
case XenbusStateClosing:
|
case XenbusStateClosing:
|
||||||
xenbus_frontend_closed(dev);
|
xenbus_frontend_closed(dev);
|
||||||
break;
|
break;
|
||||||
|
@ -1929,7 +1929,7 @@ static int alps_monitor_mode(struct psmouse *psmouse, bool enable)
|
|||||||
static int alps_absolute_mode_v6(struct psmouse *psmouse)
|
static int alps_absolute_mode_v6(struct psmouse *psmouse)
|
||||||
{
|
{
|
||||||
u16 reg_val = 0x181;
|
u16 reg_val = 0x181;
|
||||||
int ret = -1;
|
int ret;
|
||||||
|
|
||||||
/* enter monitor mode, to write the register */
|
/* enter monitor mode, to write the register */
|
||||||
if (alps_monitor_mode(psmouse, true))
|
if (alps_monitor_mode(psmouse, true))
|
||||||
|
@ -458,7 +458,7 @@ static int atp_status_check(struct urb *urb)
|
|||||||
dev->info->datalen, dev->urb->actual_length);
|
dev->info->datalen, dev->urb->actual_length);
|
||||||
dev->overflow_warned = true;
|
dev->overflow_warned = true;
|
||||||
}
|
}
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case -ECONNRESET:
|
case -ECONNRESET:
|
||||||
case -ENOENT:
|
case -ENOENT:
|
||||||
case -ESHUTDOWN:
|
case -ESHUTDOWN:
|
||||||
|
@ -1067,7 +1067,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fall through */
|
fallthrough;
|
||||||
case CYAPA_STATE_BL_IDLE:
|
case CYAPA_STATE_BL_IDLE:
|
||||||
/* Try to get firmware version in bootloader mode. */
|
/* Try to get firmware version in bootloader mode. */
|
||||||
cyapa_gen3_bl_query_data(cyapa);
|
cyapa_gen3_bl_query_data(cyapa);
|
||||||
@ -1078,7 +1078,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fall through */
|
fallthrough;
|
||||||
case CYAPA_STATE_OP:
|
case CYAPA_STATE_OP:
|
||||||
/*
|
/*
|
||||||
* Reading query data before going back to the full mode
|
* Reading query data before going back to the full mode
|
||||||
|
@ -2554,7 +2554,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cyapa->state = CYAPA_STATE_GEN5_APP;
|
cyapa->state = CYAPA_STATE_GEN5_APP;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case CYAPA_STATE_GEN5_APP:
|
case CYAPA_STATE_GEN5_APP:
|
||||||
/*
|
/*
|
||||||
|
@ -680,7 +680,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cyapa->state = CYAPA_STATE_GEN6_APP;
|
cyapa->state = CYAPA_STATE_GEN6_APP;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case CYAPA_STATE_GEN6_APP:
|
case CYAPA_STATE_GEN6_APP:
|
||||||
/*
|
/*
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#define ETP_CALIBRATE_MAX_LEN 3
|
#define ETP_CALIBRATE_MAX_LEN 3
|
||||||
|
|
||||||
|
#define ETP_FEATURE_REPORT_MK BIT(0)
|
||||||
|
|
||||||
/* IAP Firmware handling */
|
/* IAP Firmware handling */
|
||||||
#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0"
|
#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0"
|
||||||
#define ETP_FW_NAME "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin"
|
#define ETP_FW_NAME "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin"
|
||||||
@ -33,6 +35,8 @@
|
|||||||
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
|
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
|
||||||
#define ETP_FW_IAP_INTF_ERR (1 << 4)
|
#define ETP_FW_IAP_INTF_ERR (1 << 4)
|
||||||
#define ETP_FW_PAGE_SIZE 64
|
#define ETP_FW_PAGE_SIZE 64
|
||||||
|
#define ETP_FW_PAGE_SIZE_128 128
|
||||||
|
#define ETP_FW_PAGE_SIZE_512 512
|
||||||
#define ETP_FW_SIGNATURE_SIZE 6
|
#define ETP_FW_SIGNATURE_SIZE 6
|
||||||
|
|
||||||
struct i2c_client;
|
struct i2c_client;
|
||||||
@ -55,8 +59,9 @@ struct elan_transport_ops {
|
|||||||
int (*get_baseline_data)(struct i2c_client *client,
|
int (*get_baseline_data)(struct i2c_client *client,
|
||||||
bool max_baseliune, u8 *value);
|
bool max_baseliune, u8 *value);
|
||||||
|
|
||||||
int (*get_version)(struct i2c_client *client, bool iap, u8 *version);
|
int (*get_version)(struct i2c_client *client, u8 pattern, bool iap,
|
||||||
int (*get_sm_version)(struct i2c_client *client,
|
u8 *version);
|
||||||
|
int (*get_sm_version)(struct i2c_client *client, u8 pattern,
|
||||||
u16 *ic_type, u8 *version, u8 *clickpad);
|
u16 *ic_type, u8 *version, u8 *clickpad);
|
||||||
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
|
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
|
||||||
int (*get_product_id)(struct i2c_client *client, u16 *id);
|
int (*get_product_id)(struct i2c_client *client, u16 *id);
|
||||||
@ -72,13 +77,18 @@ struct elan_transport_ops {
|
|||||||
int (*iap_get_mode)(struct i2c_client *client, enum tp_mode *mode);
|
int (*iap_get_mode)(struct i2c_client *client, enum tp_mode *mode);
|
||||||
int (*iap_reset)(struct i2c_client *client);
|
int (*iap_reset)(struct i2c_client *client);
|
||||||
|
|
||||||
int (*prepare_fw_update)(struct i2c_client *client);
|
int (*prepare_fw_update)(struct i2c_client *client, u16 ic_type,
|
||||||
int (*write_fw_block)(struct i2c_client *client,
|
u8 iap_version);
|
||||||
|
int (*write_fw_block)(struct i2c_client *client, u16 fw_page_size,
|
||||||
const u8 *page, u16 checksum, int idx);
|
const u8 *page, u16 checksum, int idx);
|
||||||
int (*finish_fw_update)(struct i2c_client *client,
|
int (*finish_fw_update)(struct i2c_client *client,
|
||||||
struct completion *reset_done);
|
struct completion *reset_done);
|
||||||
|
|
||||||
int (*get_report)(struct i2c_client *client, u8 *report);
|
int (*get_report_features)(struct i2c_client *client, u8 pattern,
|
||||||
|
unsigned int *features,
|
||||||
|
unsigned int *report_len);
|
||||||
|
int (*get_report)(struct i2c_client *client, u8 *report,
|
||||||
|
unsigned int report_len);
|
||||||
int (*get_pressure_adjustment)(struct i2c_client *client,
|
int (*get_pressure_adjustment)(struct i2c_client *client,
|
||||||
int *adjustment);
|
int *adjustment);
|
||||||
int (*get_pattern)(struct i2c_client *client, u8 *pattern);
|
int (*get_pattern)(struct i2c_client *client, u8 *pattern);
|
||||||
|
@ -50,12 +50,14 @@
|
|||||||
#define ETP_MAX_FINGERS 5
|
#define ETP_MAX_FINGERS 5
|
||||||
#define ETP_FINGER_DATA_LEN 5
|
#define ETP_FINGER_DATA_LEN 5
|
||||||
#define ETP_REPORT_ID 0x5D
|
#define ETP_REPORT_ID 0x5D
|
||||||
|
#define ETP_REPORT_ID2 0x60 /* High precision report */
|
||||||
#define ETP_TP_REPORT_ID 0x5E
|
#define ETP_TP_REPORT_ID 0x5E
|
||||||
#define ETP_REPORT_ID_OFFSET 2
|
#define ETP_REPORT_ID_OFFSET 2
|
||||||
#define ETP_TOUCH_INFO_OFFSET 3
|
#define ETP_TOUCH_INFO_OFFSET 3
|
||||||
#define ETP_FINGER_DATA_OFFSET 4
|
#define ETP_FINGER_DATA_OFFSET 4
|
||||||
#define ETP_HOVER_INFO_OFFSET 30
|
#define ETP_HOVER_INFO_OFFSET 30
|
||||||
#define ETP_MAX_REPORT_LEN 34
|
#define ETP_MK_DATA_OFFSET 33 /* For high precision reports */
|
||||||
|
#define ETP_MAX_REPORT_LEN 39
|
||||||
|
|
||||||
/* The main device structure */
|
/* The main device structure */
|
||||||
struct elan_tp_data {
|
struct elan_tp_data {
|
||||||
@ -85,11 +87,14 @@ struct elan_tp_data {
|
|||||||
u8 sm_version;
|
u8 sm_version;
|
||||||
u8 iap_version;
|
u8 iap_version;
|
||||||
u16 fw_checksum;
|
u16 fw_checksum;
|
||||||
|
unsigned int report_features;
|
||||||
|
unsigned int report_len;
|
||||||
int pressure_adjustment;
|
int pressure_adjustment;
|
||||||
u8 mode;
|
u8 mode;
|
||||||
u16 ic_type;
|
u16 ic_type;
|
||||||
u16 fw_validpage_count;
|
u16 fw_validpage_count;
|
||||||
u16 fw_signature_address;
|
u16 fw_page_size;
|
||||||
|
u32 fw_signature_address;
|
||||||
|
|
||||||
bool irq_wake;
|
bool irq_wake;
|
||||||
|
|
||||||
@ -100,8 +105,8 @@ struct elan_tp_data {
|
|||||||
bool middle_button;
|
bool middle_button;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
|
static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count,
|
||||||
u16 *signature_address)
|
u32 *signature_address, u16 *page_size)
|
||||||
{
|
{
|
||||||
switch (ic_type) {
|
switch (ic_type) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
@ -126,16 +131,37 @@ static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
|
|||||||
case 0x10:
|
case 0x10:
|
||||||
*validpage_count = 1024;
|
*validpage_count = 1024;
|
||||||
break;
|
break;
|
||||||
|
case 0x11:
|
||||||
|
*validpage_count = 1280;
|
||||||
|
break;
|
||||||
|
case 0x13:
|
||||||
|
*validpage_count = 2048;
|
||||||
|
break;
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
*validpage_count = 1024;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* unknown ic type clear value */
|
/* unknown ic type clear value */
|
||||||
*validpage_count = 0;
|
*validpage_count = 0;
|
||||||
*signature_address = 0;
|
*signature_address = 0;
|
||||||
|
*page_size = 0;
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
*signature_address =
|
*signature_address =
|
||||||
(*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
|
(*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
|
||||||
|
|
||||||
|
if ((ic_type == 0x14 || ic_type == 0x15) && iap_version >= 2) {
|
||||||
|
*validpage_count /= 8;
|
||||||
|
*page_size = ETP_FW_PAGE_SIZE_512;
|
||||||
|
} else if (ic_type >= 0x0D && iap_version >= 1) {
|
||||||
|
*validpage_count /= 2;
|
||||||
|
*page_size = ETP_FW_PAGE_SIZE_128;
|
||||||
|
} else {
|
||||||
|
*page_size = ETP_FW_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,8 +241,13 @@ static int elan_query_product(struct elan_tp_data *data)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = data->ops->get_sm_version(data->client, &data->ic_type,
|
error = data->ops->get_pattern(data->client, &data->pattern);
|
||||||
&data->sm_version, &data->clickpad);
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = data->ops->get_sm_version(data->client, data->pattern,
|
||||||
|
&data->ic_type, &data->sm_version,
|
||||||
|
&data->clickpad);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -312,9 +343,9 @@ static int elan_initialize(struct elan_tp_data *data)
|
|||||||
static int elan_query_device_info(struct elan_tp_data *data)
|
static int elan_query_device_info(struct elan_tp_data *data)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
u16 ic_type;
|
|
||||||
|
|
||||||
error = data->ops->get_version(data->client, false, &data->fw_version);
|
error = data->ops->get_version(data->client, data->pattern, false,
|
||||||
|
&data->fw_version);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -323,7 +354,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = data->ops->get_version(data->client, true, &data->iap_version);
|
error = data->ops->get_version(data->client, data->pattern,
|
||||||
|
true, &data->iap_version);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -332,17 +364,16 @@ static int elan_query_device_info(struct elan_tp_data *data)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = data->ops->get_pattern(data->client, &data->pattern);
|
error = data->ops->get_report_features(data->client, data->pattern,
|
||||||
|
&data->report_features,
|
||||||
|
&data->report_len);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (data->pattern == 0x01)
|
error = elan_get_fwinfo(data->ic_type, data->iap_version,
|
||||||
ic_type = data->ic_type;
|
&data->fw_validpage_count,
|
||||||
else
|
&data->fw_signature_address,
|
||||||
ic_type = data->iap_version;
|
&data->fw_page_size);
|
||||||
|
|
||||||
error = elan_get_fwinfo(ic_type, &data->fw_validpage_count,
|
|
||||||
&data->fw_signature_address);
|
|
||||||
if (error)
|
if (error)
|
||||||
dev_warn(&data->client->dev,
|
dev_warn(&data->client->dev,
|
||||||
"unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n",
|
"unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n",
|
||||||
@ -351,16 +382,21 @@ static int elan_query_device_info(struct elan_tp_data *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int elan_convert_resolution(u8 val)
|
static unsigned int elan_convert_resolution(u8 val, u8 pattern)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* (value from firmware) * 10 + 790 = dpi
|
* pattern <= 0x01:
|
||||||
*
|
* (value from firmware) * 10 + 790 = dpi
|
||||||
|
* else
|
||||||
|
* ((value from firmware) + 3) * 100 = dpi
|
||||||
|
*/
|
||||||
|
int res = pattern <= 0x01 ?
|
||||||
|
(int)(char)val * 10 + 790 : ((int)(char)val + 3) * 100;
|
||||||
|
/*
|
||||||
* We also have to convert dpi to dots/mm (*10/254 to avoid floating
|
* We also have to convert dpi to dots/mm (*10/254 to avoid floating
|
||||||
* point).
|
* point).
|
||||||
*/
|
*/
|
||||||
|
return res * 10 / 254;
|
||||||
return ((int)(char)val * 10 + 790) * 10 / 254;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_query_device_parameters(struct elan_tp_data *data)
|
static int elan_query_device_parameters(struct elan_tp_data *data)
|
||||||
@ -409,8 +445,8 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
data->x_res = elan_convert_resolution(hw_x_res);
|
data->x_res = elan_convert_resolution(hw_x_res, data->pattern);
|
||||||
data->y_res = elan_convert_resolution(hw_y_res);
|
data->y_res = elan_convert_resolution(hw_y_res, data->pattern);
|
||||||
} else {
|
} else {
|
||||||
data->x_res = (data->max_x + 1) / x_mm;
|
data->x_res = (data->max_x + 1) / x_mm;
|
||||||
data->y_res = (data->max_y + 1) / y_mm;
|
data->y_res = (data->max_y + 1) / y_mm;
|
||||||
@ -430,14 +466,14 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
|
|||||||
* IAP firmware updater related routines
|
* IAP firmware updater related routines
|
||||||
**********************************************************
|
**********************************************************
|
||||||
*/
|
*/
|
||||||
static int elan_write_fw_block(struct elan_tp_data *data,
|
static int elan_write_fw_block(struct elan_tp_data *data, u16 page_size,
|
||||||
const u8 *page, u16 checksum, int idx)
|
const u8 *page, u16 checksum, int idx)
|
||||||
{
|
{
|
||||||
int retry = ETP_RETRY_COUNT;
|
int retry = ETP_RETRY_COUNT;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
error = data->ops->write_fw_block(data->client,
|
error = data->ops->write_fw_block(data->client, page_size,
|
||||||
page, checksum, idx);
|
page, checksum, idx);
|
||||||
if (!error)
|
if (!error)
|
||||||
return 0;
|
return 0;
|
||||||
@ -460,21 +496,23 @@ static int __elan_update_firmware(struct elan_tp_data *data,
|
|||||||
u16 boot_page_count;
|
u16 boot_page_count;
|
||||||
u16 sw_checksum = 0, fw_checksum = 0;
|
u16 sw_checksum = 0, fw_checksum = 0;
|
||||||
|
|
||||||
error = data->ops->prepare_fw_update(client);
|
error = data->ops->prepare_fw_update(client, data->ic_type,
|
||||||
|
data->iap_version);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
|
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
|
||||||
|
|
||||||
boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
|
boot_page_count = (iap_start_addr * 2) / data->fw_page_size;
|
||||||
for (i = boot_page_count; i < data->fw_validpage_count; i++) {
|
for (i = boot_page_count; i < data->fw_validpage_count; i++) {
|
||||||
u16 checksum = 0;
|
u16 checksum = 0;
|
||||||
const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
|
const u8 *page = &fw->data[i * data->fw_page_size];
|
||||||
|
|
||||||
for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
|
for (j = 0; j < data->fw_page_size; j += 2)
|
||||||
checksum += ((page[j + 1] << 8) | page[j]);
|
checksum += ((page[j + 1] << 8) | page[j]);
|
||||||
|
|
||||||
error = elan_write_fw_block(data, page, checksum, i);
|
error = elan_write_fw_block(data, data->fw_page_size,
|
||||||
|
page, checksum, i);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "write page %d fail: %d\n", i, error);
|
dev_err(dev, "write page %d fail: %d\n", i, error);
|
||||||
return error;
|
return error;
|
||||||
@ -886,24 +924,22 @@ static const struct attribute_group *elan_sysfs_groups[] = {
|
|||||||
* Elan isr functions
|
* Elan isr functions
|
||||||
******************************************************************
|
******************************************************************
|
||||||
*/
|
*/
|
||||||
static void elan_report_contact(struct elan_tp_data *data,
|
static void elan_report_contact(struct elan_tp_data *data, int contact_num,
|
||||||
int contact_num, bool contact_valid,
|
bool contact_valid, bool high_precision,
|
||||||
u8 *finger_data)
|
u8 *packet, u8 *finger_data)
|
||||||
{
|
{
|
||||||
struct input_dev *input = data->input;
|
struct input_dev *input = data->input;
|
||||||
unsigned int pos_x, pos_y;
|
unsigned int pos_x, pos_y;
|
||||||
unsigned int pressure, mk_x, mk_y;
|
unsigned int pressure, scaled_pressure;
|
||||||
unsigned int area_x, area_y, major, minor;
|
|
||||||
unsigned int scaled_pressure;
|
|
||||||
|
|
||||||
if (contact_valid) {
|
if (contact_valid) {
|
||||||
pos_x = ((finger_data[0] & 0xf0) << 4) |
|
if (high_precision) {
|
||||||
finger_data[1];
|
pos_x = get_unaligned_be16(&finger_data[0]);
|
||||||
pos_y = ((finger_data[0] & 0x0f) << 8) |
|
pos_y = get_unaligned_be16(&finger_data[2]);
|
||||||
finger_data[2];
|
} else {
|
||||||
mk_x = (finger_data[3] & 0x0f);
|
pos_x = ((finger_data[0] & 0xf0) << 4) | finger_data[1];
|
||||||
mk_y = (finger_data[3] >> 4);
|
pos_y = ((finger_data[0] & 0x0f) << 8) | finger_data[2];
|
||||||
pressure = finger_data[4];
|
}
|
||||||
|
|
||||||
if (pos_x > data->max_x || pos_y > data->max_y) {
|
if (pos_x > data->max_x || pos_y > data->max_y) {
|
||||||
dev_dbg(input->dev.parent,
|
dev_dbg(input->dev.parent,
|
||||||
@ -913,18 +949,8 @@ static void elan_report_contact(struct elan_tp_data *data,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
pressure = finger_data[4];
|
||||||
* To avoid treating large finger as palm, let's reduce the
|
|
||||||
* width x and y per trace.
|
|
||||||
*/
|
|
||||||
area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
|
|
||||||
area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
|
|
||||||
|
|
||||||
major = max(area_x, area_y);
|
|
||||||
minor = min(area_x, area_y);
|
|
||||||
|
|
||||||
scaled_pressure = pressure + data->pressure_adjustment;
|
scaled_pressure = pressure + data->pressure_adjustment;
|
||||||
|
|
||||||
if (scaled_pressure > ETP_MAX_PRESSURE)
|
if (scaled_pressure > ETP_MAX_PRESSURE)
|
||||||
scaled_pressure = ETP_MAX_PRESSURE;
|
scaled_pressure = ETP_MAX_PRESSURE;
|
||||||
|
|
||||||
@ -933,16 +959,37 @@ static void elan_report_contact(struct elan_tp_data *data,
|
|||||||
input_report_abs(input, ABS_MT_POSITION_X, pos_x);
|
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_POSITION_Y, data->max_y - pos_y);
|
||||||
input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure);
|
input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure);
|
||||||
input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
|
|
||||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
|
if (data->report_features & ETP_FEATURE_REPORT_MK) {
|
||||||
input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
|
unsigned int mk_x, mk_y, area_x, area_y;
|
||||||
|
u8 mk_data = high_precision ?
|
||||||
|
packet[ETP_MK_DATA_OFFSET + contact_num] :
|
||||||
|
finger_data[3];
|
||||||
|
|
||||||
|
mk_x = mk_data & 0x0f;
|
||||||
|
mk_y = mk_data >> 4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To avoid treating large finger as palm, let's reduce
|
||||||
|
* the width x and y per trace.
|
||||||
|
*/
|
||||||
|
area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
|
||||||
|
area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
|
||||||
|
|
||||||
|
input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
|
||||||
|
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
|
||||||
|
max(area_x, area_y));
|
||||||
|
input_report_abs(input, ABS_MT_TOUCH_MINOR,
|
||||||
|
min(area_x, area_y));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
input_mt_slot(input, contact_num);
|
input_mt_slot(input, contact_num);
|
||||||
input_mt_report_slot_inactive(input);
|
input_mt_report_slot_inactive(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
|
static void elan_report_absolute(struct elan_tp_data *data, u8 *packet,
|
||||||
|
bool high_precision)
|
||||||
{
|
{
|
||||||
struct input_dev *input = data->input;
|
struct input_dev *input = data->input;
|
||||||
u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
|
u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
|
||||||
@ -953,11 +1000,12 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
|
|||||||
|
|
||||||
pm_wakeup_event(&data->client->dev, 0);
|
pm_wakeup_event(&data->client->dev, 0);
|
||||||
|
|
||||||
hover_event = hover_info & 0x40;
|
hover_event = hover_info & BIT(6);
|
||||||
for (i = 0; i < ETP_MAX_FINGERS; i++) {
|
|
||||||
contact_valid = tp_info & (1U << (3 + i));
|
|
||||||
elan_report_contact(data, i, contact_valid, finger_data);
|
|
||||||
|
|
||||||
|
for (i = 0; i < ETP_MAX_FINGERS; i++) {
|
||||||
|
contact_valid = tp_info & BIT(3 + i);
|
||||||
|
elan_report_contact(data, i, contact_valid, high_precision,
|
||||||
|
packet, finger_data);
|
||||||
if (contact_valid)
|
if (contact_valid)
|
||||||
finger_data += ETP_FINGER_DATA_LEN;
|
finger_data += ETP_FINGER_DATA_LEN;
|
||||||
}
|
}
|
||||||
@ -1015,13 +1063,16 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = data->ops->get_report(data->client, report);
|
error = data->ops->get_report(data->client, report, data->report_len);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
switch (report[ETP_REPORT_ID_OFFSET]) {
|
switch (report[ETP_REPORT_ID_OFFSET]) {
|
||||||
case ETP_REPORT_ID:
|
case ETP_REPORT_ID:
|
||||||
elan_report_absolute(data, report);
|
elan_report_absolute(data, report, false);
|
||||||
|
break;
|
||||||
|
case ETP_REPORT_ID2:
|
||||||
|
elan_report_absolute(data, report, true);
|
||||||
break;
|
break;
|
||||||
case ETP_TP_REPORT_ID:
|
case ETP_TP_REPORT_ID:
|
||||||
elan_report_trackpoint(data, report);
|
elan_report_trackpoint(data, report);
|
||||||
@ -1112,7 +1163,9 @@ static int elan_setup_input_device(struct elan_tp_data *data)
|
|||||||
input_abs_set_res(input, ABS_X, data->x_res);
|
input_abs_set_res(input, ABS_X, data->x_res);
|
||||||
input_abs_set_res(input, ABS_Y, data->y_res);
|
input_abs_set_res(input, ABS_Y, data->y_res);
|
||||||
input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
|
input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
|
||||||
input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
|
if (data->report_features & ETP_FEATURE_REPORT_MK)
|
||||||
|
input_set_abs_params(input, ABS_TOOL_WIDTH,
|
||||||
|
0, ETP_FINGER_WIDTH, 0, 0);
|
||||||
input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
|
input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
|
||||||
|
|
||||||
/* And MT parameters */
|
/* And MT parameters */
|
||||||
@ -1122,10 +1175,12 @@ static int elan_setup_input_device(struct elan_tp_data *data)
|
|||||||
input_abs_set_res(input, ABS_MT_POSITION_Y, data->y_res);
|
input_abs_set_res(input, ABS_MT_POSITION_Y, data->y_res);
|
||||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0,
|
input_set_abs_params(input, ABS_MT_PRESSURE, 0,
|
||||||
ETP_MAX_PRESSURE, 0, 0);
|
ETP_MAX_PRESSURE, 0, 0);
|
||||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
|
if (data->report_features & ETP_FEATURE_REPORT_MK) {
|
||||||
ETP_FINGER_WIDTH * max_width, 0, 0);
|
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
|
||||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
|
0, ETP_FINGER_WIDTH * max_width, 0, 0);
|
||||||
ETP_FINGER_WIDTH * min_width, 0, 0);
|
input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
|
||||||
|
0, ETP_FINGER_WIDTH * min_width, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
data->input = input;
|
data->input = input;
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
@ -43,6 +44,8 @@
|
|||||||
#define ETP_I2C_RESOLUTION_CMD 0x0108
|
#define ETP_I2C_RESOLUTION_CMD 0x0108
|
||||||
#define ETP_I2C_PRESSURE_CMD 0x010A
|
#define ETP_I2C_PRESSURE_CMD 0x010A
|
||||||
#define ETP_I2C_IAP_VERSION_CMD 0x0110
|
#define ETP_I2C_IAP_VERSION_CMD 0x0110
|
||||||
|
#define ETP_I2C_IC_TYPE_P0_CMD 0x0110
|
||||||
|
#define ETP_I2C_IAP_VERSION_P0_CMD 0x0111
|
||||||
#define ETP_I2C_SET_CMD 0x0300
|
#define ETP_I2C_SET_CMD 0x0300
|
||||||
#define ETP_I2C_POWER_CMD 0x0307
|
#define ETP_I2C_POWER_CMD 0x0307
|
||||||
#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
|
#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
|
||||||
@ -53,8 +56,12 @@
|
|||||||
#define ETP_I2C_CALIBRATE_CMD 0x0316
|
#define ETP_I2C_CALIBRATE_CMD 0x0316
|
||||||
#define ETP_I2C_MAX_BASELINE_CMD 0x0317
|
#define ETP_I2C_MAX_BASELINE_CMD 0x0317
|
||||||
#define ETP_I2C_MIN_BASELINE_CMD 0x0318
|
#define ETP_I2C_MIN_BASELINE_CMD 0x0318
|
||||||
|
#define ETP_I2C_IAP_TYPE_REG 0x0040
|
||||||
|
#define ETP_I2C_IAP_TYPE_CMD 0x0304
|
||||||
|
|
||||||
#define ETP_I2C_REPORT_LEN 34
|
#define ETP_I2C_REPORT_LEN 34
|
||||||
|
#define ETP_I2C_REPORT_LEN_ID2 39
|
||||||
|
#define ETP_I2C_REPORT_MAX_LEN 39
|
||||||
#define ETP_I2C_DESC_LENGTH 30
|
#define ETP_I2C_DESC_LENGTH 30
|
||||||
#define ETP_I2C_REPORT_DESC_LENGTH 158
|
#define ETP_I2C_REPORT_DESC_LENGTH 158
|
||||||
#define ETP_I2C_INF_LENGTH 2
|
#define ETP_I2C_INF_LENGTH 2
|
||||||
@ -249,56 +256,52 @@ static int elan_i2c_get_pattern(struct i2c_client *client, u8 *pattern)
|
|||||||
dev_err(&client->dev, "failed to get pattern: %d\n", error);
|
dev_err(&client->dev, "failed to get pattern: %d\n", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
*pattern = val[1];
|
|
||||||
|
/*
|
||||||
|
* Not all versions of firmware implement "get pattern" command.
|
||||||
|
* When this command is not implemented the device will respond
|
||||||
|
* with 0xFF 0xFF, which we will treat as "old" pattern 0.
|
||||||
|
*/
|
||||||
|
*pattern = val[0] == 0xFF && val[1] == 0xFF ? 0 : val[1];
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_i2c_get_version(struct i2c_client *client,
|
static int elan_i2c_get_version(struct i2c_client *client,
|
||||||
bool iap, u8 *version)
|
u8 pattern, bool iap, u8 *version)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
u8 pattern_ver;
|
u16 cmd;
|
||||||
u8 val[3];
|
u8 val[3];
|
||||||
|
|
||||||
error = elan_i2c_get_pattern(client, &pattern_ver);
|
if (!iap)
|
||||||
if (error) {
|
cmd = ETP_I2C_FW_VERSION_CMD;
|
||||||
dev_err(&client->dev, "failed to get pattern version\n");
|
else if (pattern == 0)
|
||||||
return error;
|
cmd = ETP_I2C_IAP_VERSION_P0_CMD;
|
||||||
}
|
else
|
||||||
|
cmd = ETP_I2C_IAP_VERSION_CMD;
|
||||||
|
|
||||||
error = elan_i2c_read_cmd(client,
|
error = elan_i2c_read_cmd(client, cmd, val);
|
||||||
iap ? ETP_I2C_IAP_VERSION_CMD :
|
|
||||||
ETP_I2C_FW_VERSION_CMD,
|
|
||||||
val);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&client->dev, "failed to get %s version: %d\n",
|
dev_err(&client->dev, "failed to get %s version: %d\n",
|
||||||
iap ? "IAP" : "FW", error);
|
iap ? "IAP" : "FW", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pattern_ver == 0x01)
|
if (pattern >= 0x01)
|
||||||
*version = iap ? val[1] : val[0];
|
*version = iap ? val[1] : val[0];
|
||||||
else
|
else
|
||||||
*version = val[0];
|
*version = val[0];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_i2c_get_sm_version(struct i2c_client *client,
|
static int elan_i2c_get_sm_version(struct i2c_client *client, u8 pattern,
|
||||||
u16 *ic_type, u8 *version,
|
u16 *ic_type, u8 *version, u8 *clickpad)
|
||||||
u8 *clickpad)
|
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
u8 pattern_ver;
|
|
||||||
u8 val[3];
|
u8 val[3];
|
||||||
|
|
||||||
error = elan_i2c_get_pattern(client, &pattern_ver);
|
if (pattern >= 0x01) {
|
||||||
if (error) {
|
|
||||||
dev_err(&client->dev, "failed to get pattern version\n");
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pattern_ver == 0x01) {
|
|
||||||
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_CMD, val);
|
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_CMD, val);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&client->dev, "failed to get ic type: %d\n",
|
dev_err(&client->dev, "failed to get ic type: %d\n",
|
||||||
@ -324,7 +327,14 @@ static int elan_i2c_get_sm_version(struct i2c_client *client,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
*version = val[0];
|
*version = val[0];
|
||||||
*ic_type = val[1];
|
|
||||||
|
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_P0_CMD, val);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&client->dev, "failed to get ic type: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
*ic_type = val[0];
|
||||||
|
|
||||||
error = elan_i2c_read_cmd(client, ETP_I2C_NSM_VERSION_CMD,
|
error = elan_i2c_read_cmd(client, ETP_I2C_NSM_VERSION_CMD,
|
||||||
val);
|
val);
|
||||||
@ -386,7 +396,7 @@ static int elan_i2c_get_max(struct i2c_client *client,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
*max_x = le16_to_cpup((__le16 *)val) & 0x0fff;
|
*max_x = le16_to_cpup((__le16 *)val);
|
||||||
|
|
||||||
error = elan_i2c_read_cmd(client, ETP_I2C_MAX_Y_AXIS_CMD, val);
|
error = elan_i2c_read_cmd(client, ETP_I2C_MAX_Y_AXIS_CMD, val);
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -394,7 +404,7 @@ static int elan_i2c_get_max(struct i2c_client *client,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
*max_y = le16_to_cpup((__le16 *)val) & 0x0fff;
|
*max_y = le16_to_cpup((__le16 *)val);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -507,7 +517,43 @@ static int elan_i2c_set_flash_key(struct i2c_client *client)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_i2c_prepare_fw_update(struct i2c_client *client)
|
static int elan_read_write_iap_type(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
u16 constant;
|
||||||
|
u8 val[3];
|
||||||
|
int retry = 3;
|
||||||
|
|
||||||
|
do {
|
||||||
|
error = elan_i2c_write_cmd(client, ETP_I2C_IAP_TYPE_CMD,
|
||||||
|
ETP_I2C_IAP_TYPE_REG);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"cannot write iap type: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_TYPE_CMD, val);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"failed to read iap type register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
constant = le16_to_cpup((__le16 *)val);
|
||||||
|
dev_dbg(&client->dev, "iap type reg: 0x%04x\n", constant);
|
||||||
|
|
||||||
|
if (constant == ETP_I2C_IAP_TYPE_REG)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} while (--retry > 0);
|
||||||
|
|
||||||
|
dev_err(&client->dev, "cannot set iap type\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elan_i2c_prepare_fw_update(struct i2c_client *client, u16 ic_type,
|
||||||
|
u8 iap_version)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
int error;
|
int error;
|
||||||
@ -547,6 +593,12 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client)
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ic_type >= 0x0D && iap_version >= 1) {
|
||||||
|
error = elan_read_write_iap_type(client);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set flash key again */
|
/* Set flash key again */
|
||||||
error = elan_i2c_set_flash_key(client);
|
error = elan_i2c_set_flash_key(client);
|
||||||
if (error)
|
if (error)
|
||||||
@ -572,57 +624,64 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_i2c_write_fw_block(struct i2c_client *client,
|
static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size,
|
||||||
const u8 *page, u16 checksum, int idx)
|
const u8 *page, u16 checksum, int idx)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
u8 page_store[ETP_FW_PAGE_SIZE + 4];
|
u8 *page_store;
|
||||||
u8 val[3];
|
u8 val[3];
|
||||||
u16 result;
|
u16 result;
|
||||||
int ret, error;
|
int ret, error;
|
||||||
|
|
||||||
|
page_store = kmalloc(fw_page_size + 4, GFP_KERNEL);
|
||||||
|
if (!page_store)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
page_store[0] = ETP_I2C_IAP_REG_L;
|
page_store[0] = ETP_I2C_IAP_REG_L;
|
||||||
page_store[1] = ETP_I2C_IAP_REG_H;
|
page_store[1] = ETP_I2C_IAP_REG_H;
|
||||||
memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
|
memcpy(&page_store[2], page, fw_page_size);
|
||||||
/* recode checksum at last two bytes */
|
/* recode checksum at last two bytes */
|
||||||
put_unaligned_le16(checksum, &page_store[ETP_FW_PAGE_SIZE + 2]);
|
put_unaligned_le16(checksum, &page_store[fw_page_size + 2]);
|
||||||
|
|
||||||
ret = i2c_master_send(client, page_store, sizeof(page_store));
|
ret = i2c_master_send(client, page_store, fw_page_size + 4);
|
||||||
if (ret != sizeof(page_store)) {
|
if (ret != fw_page_size + 4) {
|
||||||
error = ret < 0 ? ret : -EIO;
|
error = ret < 0 ? ret : -EIO;
|
||||||
dev_err(dev, "Failed to write page %d: %d\n", idx, error);
|
dev_err(dev, "Failed to write page %d: %d\n", idx, error);
|
||||||
return error;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for F/W to update one page ROM data. */
|
/* Wait for F/W to update one page ROM data. */
|
||||||
msleep(35);
|
msleep(fw_page_size == ETP_FW_PAGE_SIZE_512 ? 50 : 35);
|
||||||
|
|
||||||
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
|
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "Failed to read IAP write result: %d\n", error);
|
dev_err(dev, "Failed to read IAP write result: %d\n", error);
|
||||||
return error;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = le16_to_cpup((__le16 *)val);
|
result = le16_to_cpup((__le16 *)val);
|
||||||
if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
|
if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
|
||||||
dev_err(dev, "IAP reports failed write: %04hx\n",
|
dev_err(dev, "IAP reports failed write: %04hx\n",
|
||||||
result);
|
result);
|
||||||
return -EIO;
|
error = -EIO;
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
exit:
|
||||||
|
kfree(page_store);
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_i2c_finish_fw_update(struct i2c_client *client,
|
static int elan_i2c_finish_fw_update(struct i2c_client *client,
|
||||||
struct completion *completion)
|
struct completion *completion)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
int error;
|
int error = 0;
|
||||||
int len;
|
int len;
|
||||||
u8 buffer[ETP_I2C_REPORT_LEN];
|
u8 buffer[ETP_I2C_REPORT_MAX_LEN];
|
||||||
|
|
||||||
len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_LEN);
|
len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_MAX_LEN);
|
||||||
if (len != ETP_I2C_REPORT_LEN) {
|
if (len <= 0) {
|
||||||
error = len < 0 ? len : -EIO;
|
error = len < 0 ? len : -EIO;
|
||||||
dev_warn(dev, "failed to read I2C data after FW WDT reset: %d (%d)\n",
|
dev_warn(dev, "failed to read I2C data after FW WDT reset: %d (%d)\n",
|
||||||
error, len);
|
error, len);
|
||||||
@ -656,20 +715,31 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_i2c_get_report(struct i2c_client *client, u8 *report)
|
static int elan_i2c_get_report_features(struct i2c_client *client, u8 pattern,
|
||||||
|
unsigned int *features,
|
||||||
|
unsigned int *report_len)
|
||||||
|
{
|
||||||
|
*features = ETP_FEATURE_REPORT_MK;
|
||||||
|
*report_len = pattern <= 0x01 ?
|
||||||
|
ETP_I2C_REPORT_LEN : ETP_I2C_REPORT_LEN_ID2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elan_i2c_get_report(struct i2c_client *client,
|
||||||
|
u8 *report, unsigned int report_len)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = i2c_master_recv(client, report, ETP_I2C_REPORT_LEN);
|
len = i2c_master_recv(client, report, report_len);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
dev_err(&client->dev, "failed to read report data: %d\n", len);
|
dev_err(&client->dev, "failed to read report data: %d\n", len);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len != ETP_I2C_REPORT_LEN) {
|
if (len != report_len) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"wrong report length (%d vs %d expected)\n",
|
"wrong report length (%d vs %d expected)\n",
|
||||||
len, ETP_I2C_REPORT_LEN);
|
len, report_len);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -706,5 +776,6 @@ const struct elan_transport_ops elan_i2c_ops = {
|
|||||||
|
|
||||||
.get_pattern = elan_i2c_get_pattern,
|
.get_pattern = elan_i2c_get_pattern,
|
||||||
|
|
||||||
|
.get_report_features = elan_i2c_get_report_features,
|
||||||
.get_report = elan_i2c_get_report,
|
.get_report = elan_i2c_get_report,
|
||||||
};
|
};
|
||||||
|
@ -147,7 +147,7 @@ static int elan_smbus_get_baseline_data(struct i2c_client *client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int elan_smbus_get_version(struct i2c_client *client,
|
static int elan_smbus_get_version(struct i2c_client *client,
|
||||||
bool iap, u8 *version)
|
u8 pattern, bool iap, u8 *version)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||||
@ -166,9 +166,8 @@ static int elan_smbus_get_version(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_smbus_get_sm_version(struct i2c_client *client,
|
static int elan_smbus_get_sm_version(struct i2c_client *client, u8 pattern,
|
||||||
u16 *ic_type, u8 *version,
|
u16 *ic_type, u8 *version, u8 *clickpad)
|
||||||
u8 *clickpad)
|
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||||
@ -340,7 +339,8 @@ static int elan_smbus_set_flash_key(struct i2c_client *client)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_smbus_prepare_fw_update(struct i2c_client *client)
|
static int elan_smbus_prepare_fw_update(struct i2c_client *client, u16 ic_type,
|
||||||
|
u8 iap_version)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
int len;
|
int len;
|
||||||
@ -414,7 +414,7 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int elan_smbus_write_fw_block(struct i2c_client *client,
|
static int elan_smbus_write_fw_block(struct i2c_client *client, u16 fw_page_size,
|
||||||
const u8 *page, u16 checksum, int idx)
|
const u8 *page, u16 checksum, int idx)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
@ -429,7 +429,7 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
|
|||||||
*/
|
*/
|
||||||
error = i2c_smbus_write_block_data(client,
|
error = i2c_smbus_write_block_data(client,
|
||||||
ETP_SMBUS_WRITE_FW_BLOCK,
|
ETP_SMBUS_WRITE_FW_BLOCK,
|
||||||
ETP_FW_PAGE_SIZE / 2,
|
fw_page_size / 2,
|
||||||
page);
|
page);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "Failed to write page %d (part %d): %d\n",
|
dev_err(dev, "Failed to write page %d (part %d): %d\n",
|
||||||
@ -439,8 +439,8 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
|
|||||||
|
|
||||||
error = i2c_smbus_write_block_data(client,
|
error = i2c_smbus_write_block_data(client,
|
||||||
ETP_SMBUS_WRITE_FW_BLOCK,
|
ETP_SMBUS_WRITE_FW_BLOCK,
|
||||||
ETP_FW_PAGE_SIZE / 2,
|
fw_page_size / 2,
|
||||||
page + ETP_FW_PAGE_SIZE / 2);
|
page + fw_page_size / 2);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "Failed to write page %d (part %d): %d\n",
|
dev_err(dev, "Failed to write page %d (part %d): %d\n",
|
||||||
idx, 2, error);
|
idx, 2, error);
|
||||||
@ -469,7 +469,21 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_smbus_get_report(struct i2c_client *client, u8 *report)
|
static int elan_smbus_get_report_features(struct i2c_client *client, u8 pattern,
|
||||||
|
unsigned int *features,
|
||||||
|
unsigned int *report_len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* SMBus controllers with pattern 2 lack area info, as newer
|
||||||
|
* high-precision packets use that space for coordinates.
|
||||||
|
*/
|
||||||
|
*features = pattern <= 0x01 ? ETP_FEATURE_REPORT_MK : 0;
|
||||||
|
*report_len = ETP_SMBUS_REPORT_LEN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elan_smbus_get_report(struct i2c_client *client,
|
||||||
|
u8 *report, unsigned int report_len)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
@ -534,6 +548,7 @@ const struct elan_transport_ops elan_smbus_ops = {
|
|||||||
.write_fw_block = elan_smbus_write_fw_block,
|
.write_fw_block = elan_smbus_write_fw_block,
|
||||||
.finish_fw_update = elan_smbus_finish_fw_update,
|
.finish_fw_update = elan_smbus_finish_fw_update,
|
||||||
|
|
||||||
|
.get_report_features = elan_smbus_get_report_features,
|
||||||
.get_report = elan_smbus_get_report,
|
.get_report = elan_smbus_get_report,
|
||||||
.get_pattern = elan_smbus_get_pattern,
|
.get_pattern = elan_smbus_get_pattern,
|
||||||
};
|
};
|
||||||
|
@ -383,7 +383,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
|
|||||||
*/
|
*/
|
||||||
if (packet[3] & 0x80)
|
if (packet[3] & 0x80)
|
||||||
fingers = 4;
|
fingers = 4;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case 1:
|
case 1:
|
||||||
/*
|
/*
|
||||||
* byte 1: . . . . x11 x10 x9 x8
|
* byte 1: . . . . x11 x10 x9 x8
|
||||||
@ -1146,7 +1146,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
|||||||
case 2:
|
case 2:
|
||||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||||
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
case 3:
|
case 3:
|
||||||
if (info->hw_version == 3)
|
if (info->hw_version == 3)
|
||||||
elantech_set_buttonpad_prop(psmouse);
|
elantech_set_buttonpad_prop(psmouse);
|
||||||
@ -1877,12 +1877,10 @@ static bool elantech_use_host_notify(struct psmouse *psmouse,
|
|||||||
/* expected case */
|
/* expected case */
|
||||||
break;
|
break;
|
||||||
case ETP_BUS_SMB_ALERT_ONLY:
|
case ETP_BUS_SMB_ALERT_ONLY:
|
||||||
/* fall-through */
|
|
||||||
case ETP_BUS_PS2_SMB_ALERT:
|
case ETP_BUS_PS2_SMB_ALERT:
|
||||||
psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
|
psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
|
||||||
break;
|
break;
|
||||||
case ETP_BUS_SMB_HST_NTFY_ONLY:
|
case ETP_BUS_SMB_HST_NTFY_ONLY:
|
||||||
/* fall-through */
|
|
||||||
case ETP_BUS_PS2_SMB_HST_NTFY:
|
case ETP_BUS_PS2_SMB_HST_NTFY:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
@ -1897,7 +1895,7 @@ static bool elantech_use_host_notify(struct psmouse *psmouse,
|
|||||||
int elantech_init_smbus(struct psmouse *psmouse)
|
int elantech_init_smbus(struct psmouse *psmouse)
|
||||||
{
|
{
|
||||||
struct elantech_device_info info;
|
struct elantech_device_info info;
|
||||||
int error = -EINVAL;
|
int error;
|
||||||
|
|
||||||
psmouse_reset(psmouse);
|
psmouse_reset(psmouse);
|
||||||
|
|
||||||
@ -2015,7 +2013,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
|
|||||||
int elantech_init_ps2(struct psmouse *psmouse)
|
int elantech_init_ps2(struct psmouse *psmouse)
|
||||||
{
|
{
|
||||||
struct elantech_device_info info;
|
struct elantech_device_info info;
|
||||||
int error = -EINVAL;
|
int error;
|
||||||
|
|
||||||
psmouse_reset(psmouse);
|
psmouse_reset(psmouse);
|
||||||
|
|
||||||
@ -2036,7 +2034,7 @@ int elantech_init_ps2(struct psmouse *psmouse)
|
|||||||
int elantech_init(struct psmouse *psmouse)
|
int elantech_init(struct psmouse *psmouse)
|
||||||
{
|
{
|
||||||
struct elantech_device_info info;
|
struct elantech_device_info info;
|
||||||
int error = -EINVAL;
|
int error;
|
||||||
|
|
||||||
psmouse_reset(psmouse);
|
psmouse_reset(psmouse);
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
|
|||||||
/* we're not spewing, but this packet might be the start */
|
/* we're not spewing, but this packet might be the start */
|
||||||
priv->spew_flag = MAYBE_SPEWING;
|
priv->spew_flag = MAYBE_SPEWING;
|
||||||
|
|
||||||
/* fall-through */
|
fallthrough;
|
||||||
|
|
||||||
case MAYBE_SPEWING:
|
case MAYBE_SPEWING:
|
||||||
priv->spew_count++;
|
priv->spew_count++;
|
||||||
@ -249,7 +249,7 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
|
|||||||
/* excessive spew detected, request recalibration */
|
/* excessive spew detected, request recalibration */
|
||||||
priv->spew_flag = SPEW_DETECTED;
|
priv->spew_flag = SPEW_DETECTED;
|
||||||
|
|
||||||
/* fall-through */
|
fallthrough;
|
||||||
|
|
||||||
case SPEW_DETECTED:
|
case SPEW_DETECTED:
|
||||||
/* only recalibrate when the overall delta to the cursor
|
/* only recalibrate when the overall delta to the cursor
|
||||||
|
@ -105,7 +105,7 @@ static void navpoint_packet(struct navpoint *navpoint)
|
|||||||
case 0x19: /* Module 0, Hello packet */
|
case 0x19: /* Module 0, Hello packet */
|
||||||
if ((navpoint->data[1] & 0xf0) == 0x10)
|
if ((navpoint->data[1] & 0xf0) == 0x10)
|
||||||
break;
|
break;
|
||||||
/* FALLTHROUGH */
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
dev_warn(navpoint->dev,
|
dev_warn(navpoint->dev,
|
||||||
"spurious packet: data=0x%02x,0x%02x,...\n",
|
"spurious packet: data=0x%02x,0x%02x,...\n",
|
||||||
|
@ -2042,7 +2042,7 @@ static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp)
|
|||||||
{
|
{
|
||||||
int type = *((unsigned int *)kp->arg);
|
int type = *((unsigned int *)kp->arg);
|
||||||
|
|
||||||
return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name);
|
return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init psmouse_init(void)
|
static int __init psmouse_init(void)
|
||||||
|
@ -441,7 +441,7 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
|
|||||||
|
|
||||||
fsp_reg_write_enable(psmouse, false);
|
fsp_reg_write_enable(psmouse, false);
|
||||||
|
|
||||||
return count;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg);
|
PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg);
|
||||||
@ -794,7 +794,7 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
|
|||||||
/* on-pad click, filter it if necessary */
|
/* on-pad click, filter it if necessary */
|
||||||
if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
|
if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
|
||||||
packet[0] &= ~FSP_PB0_LBTN;
|
packet[0] &= ~FSP_PB0_LBTN;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case FSP_PKT_TYPE_NORMAL:
|
case FSP_PKT_TYPE_NORMAL:
|
||||||
/* normal packet */
|
/* normal packet */
|
||||||
|
@ -128,7 +128,7 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
|
|||||||
|
|
||||||
case SERIO_MS:
|
case SERIO_MS:
|
||||||
sermouse->type = SERIO_MP;
|
sermouse->type = SERIO_MP;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case SERIO_MP:
|
case SERIO_MP:
|
||||||
if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */
|
if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */
|
||||||
@ -139,7 +139,7 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
|
|||||||
case SERIO_MZP:
|
case SERIO_MZP:
|
||||||
case SERIO_MZPP:
|
case SERIO_MZPP:
|
||||||
input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
|
input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case SERIO_MZ:
|
case SERIO_MZ:
|
||||||
input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
|
input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
|
||||||
|
@ -562,7 +562,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
|||||||
str = last_str;
|
str = last_str;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* fall through - report timeout */
|
fallthrough; /* report timeout */
|
||||||
case 0xfc:
|
case 0xfc:
|
||||||
case 0xfd:
|
case 0xfd:
|
||||||
case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
|
case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
|
||||||
|
@ -418,7 +418,7 @@ bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
|
|||||||
ps2dev->nak = 0;
|
ps2dev->nak = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Fall through */
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
* Do not signal errors if we get unexpected reply while
|
* Do not signal errors if we get unexpected reply while
|
||||||
|
@ -247,7 +247,7 @@ void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *k
|
|||||||
|
|
||||||
case KE_SW:
|
case KE_SW:
|
||||||
value = ke->sw.value;
|
value = ke->sw.value;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case KE_VSW:
|
case KE_VSW:
|
||||||
input_report_switch(dev, ke->sw.code, value);
|
input_report_switch(dev, ke->sw.code, value);
|
||||||
|
@ -676,8 +676,8 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
|||||||
|
|
||||||
/* Mask out the Y tilt value used for pressure */
|
/* Mask out the Y tilt value used for pressure */
|
||||||
device->buffer[7] = (u8)((device->buffer[7]) & 0x7F);
|
device->buffer[7] = (u8)((device->buffer[7]) & 0x7F);
|
||||||
|
fallthrough;
|
||||||
|
|
||||||
/* Fall thru */
|
|
||||||
case 4:
|
case 4:
|
||||||
/* Tilt */
|
/* Tilt */
|
||||||
input_report_abs(inputdev, ABS_TILT_X,
|
input_report_abs(inputdev, ABS_TILT_X,
|
||||||
@ -685,8 +685,8 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
|||||||
|
|
||||||
input_report_abs(inputdev, ABS_TILT_Y,
|
input_report_abs(inputdev, ABS_TILT_Y,
|
||||||
sign_extend32(device->buffer[7], 6));
|
sign_extend32(device->buffer[7], 6));
|
||||||
|
fallthrough;
|
||||||
|
|
||||||
/* Fall thru */
|
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
/* Convert buttons, only 5 bits possible */
|
/* Convert buttons, only 5 bits possible */
|
||||||
@ -695,8 +695,8 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
|||||||
/* We don't apply any meaning to the bitmask,
|
/* We don't apply any meaning to the bitmask,
|
||||||
just report */
|
just report */
|
||||||
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
|
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
|
||||||
|
fallthrough;
|
||||||
|
|
||||||
/* Fall thru */
|
|
||||||
case 1:
|
case 1:
|
||||||
/* All reports have X and Y coords in the same place */
|
/* All reports have X and Y coords in the same place */
|
||||||
val = get_unaligned_le16(&device->buffer[1]);
|
val = get_unaligned_le16(&device->buffer[1]);
|
||||||
|
@ -146,7 +146,7 @@ static void pegasus_parse_packet(struct pegasus *pegasus)
|
|||||||
/* xy data */
|
/* xy data */
|
||||||
case BATTERY_LOW:
|
case BATTERY_LOW:
|
||||||
dev_warn_once(&dev->dev, "Pen battery low\n");
|
dev_warn_once(&dev->dev, "Pen battery low\n");
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case BATTERY_NO_REPORT:
|
case BATTERY_NO_REPORT:
|
||||||
case BATTERY_GOOD:
|
case BATTERY_GOOD:
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/input/mt.h>
|
#include <linux/input/mt.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -129,6 +130,7 @@ struct t9_range {
|
|||||||
/* MXT_SPT_COMMSCONFIG_T18 */
|
/* MXT_SPT_COMMSCONFIG_T18 */
|
||||||
#define MXT_COMMS_CTRL 0
|
#define MXT_COMMS_CTRL 0
|
||||||
#define MXT_COMMS_CMD 1
|
#define MXT_COMMS_CMD 1
|
||||||
|
#define MXT_COMMS_RETRIGEN BIT(6)
|
||||||
|
|
||||||
/* MXT_DEBUG_DIAGNOSTIC_T37 */
|
/* MXT_DEBUG_DIAGNOSTIC_T37 */
|
||||||
#define MXT_DIAGNOSTIC_PAGEUP 0x01
|
#define MXT_DIAGNOSTIC_PAGEUP 0x01
|
||||||
@ -308,6 +310,7 @@ struct mxt_data {
|
|||||||
struct t7_config t7_cfg;
|
struct t7_config t7_cfg;
|
||||||
struct mxt_dbg dbg;
|
struct mxt_dbg dbg;
|
||||||
struct gpio_desc *reset_gpio;
|
struct gpio_desc *reset_gpio;
|
||||||
|
bool use_retrigen_workaround;
|
||||||
|
|
||||||
/* Cached parameters from object table */
|
/* Cached parameters from object table */
|
||||||
u16 T5_address;
|
u16 T5_address;
|
||||||
@ -318,6 +321,7 @@ struct mxt_data {
|
|||||||
u16 T71_address;
|
u16 T71_address;
|
||||||
u8 T9_reportid_min;
|
u8 T9_reportid_min;
|
||||||
u8 T9_reportid_max;
|
u8 T9_reportid_max;
|
||||||
|
u16 T18_address;
|
||||||
u8 T19_reportid;
|
u8 T19_reportid;
|
||||||
u16 T44_address;
|
u16 T44_address;
|
||||||
u8 T100_reportid_min;
|
u8 T100_reportid_min;
|
||||||
@ -1190,9 +1194,11 @@ static int mxt_acquire_irq(struct mxt_data *data)
|
|||||||
|
|
||||||
enable_irq(data->irq);
|
enable_irq(data->irq);
|
||||||
|
|
||||||
error = mxt_process_messages_until_invalid(data);
|
if (data->use_retrigen_workaround) {
|
||||||
if (error)
|
error = mxt_process_messages_until_invalid(data);
|
||||||
return error;
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1282,6 +1288,38 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
|
|||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mxt_check_retrigen(struct mxt_data *data)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
int error;
|
||||||
|
int val;
|
||||||
|
struct irq_data *irqd;
|
||||||
|
|
||||||
|
data->use_retrigen_workaround = false;
|
||||||
|
|
||||||
|
irqd = irq_get_irq_data(data->irq);
|
||||||
|
if (!irqd)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (irqd_is_level_type(irqd))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (data->T18_address) {
|
||||||
|
error = __mxt_read_reg(client,
|
||||||
|
data->T18_address + MXT_COMMS_CTRL,
|
||||||
|
1, &val);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (val & MXT_COMMS_RETRIGEN)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_warn(&client->dev, "Enabling RETRIGEN workaround\n");
|
||||||
|
data->use_retrigen_workaround = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
|
static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
|
||||||
{
|
{
|
||||||
struct device *dev = &data->client->dev;
|
struct device *dev = &data->client->dev;
|
||||||
@ -1561,6 +1599,10 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw)
|
|||||||
|
|
||||||
mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
|
mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
|
||||||
|
|
||||||
|
ret = mxt_check_retrigen(data);
|
||||||
|
if (ret)
|
||||||
|
goto release_mem;
|
||||||
|
|
||||||
ret = mxt_soft_reset(data);
|
ret = mxt_soft_reset(data);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto release_mem;
|
goto release_mem;
|
||||||
@ -1604,6 +1646,7 @@ static void mxt_free_object_table(struct mxt_data *data)
|
|||||||
data->T71_address = 0;
|
data->T71_address = 0;
|
||||||
data->T9_reportid_min = 0;
|
data->T9_reportid_min = 0;
|
||||||
data->T9_reportid_max = 0;
|
data->T9_reportid_max = 0;
|
||||||
|
data->T18_address = 0;
|
||||||
data->T19_reportid = 0;
|
data->T19_reportid = 0;
|
||||||
data->T44_address = 0;
|
data->T44_address = 0;
|
||||||
data->T100_reportid_min = 0;
|
data->T100_reportid_min = 0;
|
||||||
@ -1678,6 +1721,9 @@ static int mxt_parse_object_table(struct mxt_data *data,
|
|||||||
object->num_report_ids - 1;
|
object->num_report_ids - 1;
|
||||||
data->num_touchids = object->num_report_ids;
|
data->num_touchids = object->num_report_ids;
|
||||||
break;
|
break;
|
||||||
|
case MXT_SPT_COMMSCONFIG_T18:
|
||||||
|
data->T18_address = object->start_address;
|
||||||
|
break;
|
||||||
case MXT_SPT_MESSAGECOUNT_T44:
|
case MXT_SPT_MESSAGECOUNT_T44:
|
||||||
data->T44_address = object->start_address;
|
data->T44_address = object->start_address;
|
||||||
break;
|
break;
|
||||||
@ -2141,6 +2187,10 @@ static int mxt_initialize(struct mxt_data *data)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
error = mxt_check_retrigen(data);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
|
error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
|
||||||
&client->dev, GFP_KERNEL, data,
|
&client->dev, GFP_KERNEL, data,
|
||||||
mxt_config_cb);
|
mxt_config_cb);
|
||||||
|
@ -288,7 +288,7 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
|
|||||||
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
|
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
|
||||||
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
|
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
|
||||||
wrbuf, 0, NULL);
|
wrbuf, 0, NULL);
|
||||||
/* fallthrough */
|
|
||||||
case EDT_M09:
|
case EDT_M09:
|
||||||
case EDT_M12:
|
case EDT_M12:
|
||||||
case EV_FT:
|
case EV_FT:
|
||||||
@ -330,7 +330,6 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* fallthrough */
|
|
||||||
case EDT_M09:
|
case EDT_M09:
|
||||||
case EDT_M12:
|
case EDT_M12:
|
||||||
case EV_FT:
|
case EV_FT:
|
||||||
|
@ -955,7 +955,7 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
ts->state = ELAN_STATE_NORMAL;
|
ts->state = ELAN_STATE_NORMAL;
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case ELAN_STATE_NORMAL:
|
case ELAN_STATE_NORMAL:
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
|
|||||||
|
|
||||||
case 1: /* 6-byte protocol */
|
case 1: /* 6-byte protocol */
|
||||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
|
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case 2: /* 4-byte protocol */
|
case 2: /* 4-byte protocol */
|
||||||
input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
|
input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/input/mt.h>
|
#include <linux/input/mt.h>
|
||||||
@ -16,6 +18,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
@ -23,15 +26,59 @@
|
|||||||
#define EXC3000_SLOTS_PER_FRAME 5
|
#define EXC3000_SLOTS_PER_FRAME 5
|
||||||
#define EXC3000_LEN_FRAME 66
|
#define EXC3000_LEN_FRAME 66
|
||||||
#define EXC3000_LEN_POINT 10
|
#define EXC3000_LEN_POINT 10
|
||||||
#define EXC3000_MT_EVENT 6
|
|
||||||
|
#define EXC3000_LEN_MODEL_NAME 16
|
||||||
|
#define EXC3000_LEN_FW_VERSION 16
|
||||||
|
|
||||||
|
#define EXC3000_MT1_EVENT 0x06
|
||||||
|
#define EXC3000_MT2_EVENT 0x18
|
||||||
|
|
||||||
#define EXC3000_TIMEOUT_MS 100
|
#define EXC3000_TIMEOUT_MS 100
|
||||||
|
|
||||||
|
#define EXC3000_RESET_MS 10
|
||||||
|
#define EXC3000_READY_MS 100
|
||||||
|
|
||||||
|
static const struct i2c_device_id exc3000_id[];
|
||||||
|
|
||||||
|
struct eeti_dev_info {
|
||||||
|
const char *name;
|
||||||
|
int max_xy;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum eeti_dev_id {
|
||||||
|
EETI_EXC3000,
|
||||||
|
EETI_EXC80H60,
|
||||||
|
EETI_EXC80H84,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct eeti_dev_info exc3000_info[] = {
|
||||||
|
[EETI_EXC3000] = {
|
||||||
|
.name = "EETI EXC3000 Touch Screen",
|
||||||
|
.max_xy = SZ_4K - 1,
|
||||||
|
},
|
||||||
|
[EETI_EXC80H60] = {
|
||||||
|
.name = "EETI EXC80H60 Touch Screen",
|
||||||
|
.max_xy = SZ_16K - 1,
|
||||||
|
},
|
||||||
|
[EETI_EXC80H84] = {
|
||||||
|
.name = "EETI EXC80H84 Touch Screen",
|
||||||
|
.max_xy = SZ_16K - 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
struct exc3000_data {
|
struct exc3000_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
const struct eeti_dev_info *info;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
struct touchscreen_properties prop;
|
struct touchscreen_properties prop;
|
||||||
|
struct gpio_desc *reset;
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
u8 buf[2 * EXC3000_LEN_FRAME];
|
u8 buf[2 * EXC3000_LEN_FRAME];
|
||||||
|
struct completion wait_event;
|
||||||
|
struct mutex query_lock;
|
||||||
|
int query_result;
|
||||||
|
char model[EXC3000_LEN_MODEL_NAME];
|
||||||
|
char fw_version[EXC3000_LEN_FW_VERSION];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void exc3000_report_slots(struct input_dev *input,
|
static void exc3000_report_slots(struct input_dev *input,
|
||||||
@ -58,10 +105,15 @@ static void exc3000_timer(struct timer_list *t)
|
|||||||
input_sync(data->input);
|
input_sync(data->input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
|
static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
|
||||||
{
|
{
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
u8 expected_event = EXC3000_MT1_EVENT;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (data->info->max_xy == SZ_16K - 1)
|
||||||
|
expected_event = EXC3000_MT2_EVENT;
|
||||||
|
|
||||||
ret = i2c_master_send(client, "'", 2);
|
ret = i2c_master_send(client, "'", 2);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -76,19 +128,21 @@ static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
|
|||||||
if (ret != EXC3000_LEN_FRAME)
|
if (ret != EXC3000_LEN_FRAME)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME ||
|
if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
|
||||||
buf[2] != EXC3000_MT_EVENT)
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (buf[2] != expected_event)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exc3000_read_data(struct i2c_client *client,
|
static int exc3000_read_data(struct exc3000_data *data,
|
||||||
u8 *buf, int *n_slots)
|
u8 *buf, int *n_slots)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = exc3000_read_frame(client, buf);
|
error = exc3000_read_frame(data, buf);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -98,7 +152,7 @@ static int exc3000_read_data(struct i2c_client *client,
|
|||||||
|
|
||||||
if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
|
if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
|
||||||
/* Read 2nd frame to get the rest of the contacts. */
|
/* Read 2nd frame to get the rest of the contacts. */
|
||||||
error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME);
|
error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -110,6 +164,28 @@ static int exc3000_read_data(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int exc3000_query_interrupt(struct exc3000_data *data)
|
||||||
|
{
|
||||||
|
u8 *buf = data->buf;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME);
|
||||||
|
if (error < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (buf[0] != 'B')
|
||||||
|
return -EPROTO;
|
||||||
|
|
||||||
|
if (buf[4] == 'E')
|
||||||
|
strlcpy(data->model, buf + 5, sizeof(data->model));
|
||||||
|
else if (buf[4] == 'D')
|
||||||
|
strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version));
|
||||||
|
else
|
||||||
|
return -EPROTO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
|
static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct exc3000_data *data = dev_id;
|
struct exc3000_data *data = dev_id;
|
||||||
@ -118,7 +194,13 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
|
|||||||
int slots, total_slots;
|
int slots, total_slots;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = exc3000_read_data(data->client, buf, &total_slots);
|
if (mutex_is_locked(&data->query_lock)) {
|
||||||
|
data->query_result = exc3000_query_interrupt(data);
|
||||||
|
complete(&data->wait_event);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = exc3000_read_data(data, buf, &total_slots);
|
||||||
if (error) {
|
if (error) {
|
||||||
/* Schedule a timer to release "stuck" contacts */
|
/* Schedule a timer to release "stuck" contacts */
|
||||||
mod_timer(&data->timer,
|
mod_timer(&data->timer,
|
||||||
@ -145,31 +227,132 @@ out:
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exc3000_probe(struct i2c_client *client,
|
static ssize_t fw_version_show(struct device *dev,
|
||||||
const struct i2c_device_id *id)
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct exc3000_data *data = i2c_get_clientdata(client);
|
||||||
|
static const u8 request[68] = {
|
||||||
|
0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00
|
||||||
|
};
|
||||||
|
int error;
|
||||||
|
|
||||||
|
mutex_lock(&data->query_lock);
|
||||||
|
|
||||||
|
data->query_result = -ETIMEDOUT;
|
||||||
|
reinit_completion(&data->wait_event);
|
||||||
|
|
||||||
|
error = i2c_master_send(client, request, sizeof(request));
|
||||||
|
if (error < 0) {
|
||||||
|
mutex_unlock(&data->query_lock);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
|
||||||
|
mutex_unlock(&data->query_lock);
|
||||||
|
|
||||||
|
if (data->query_result < 0)
|
||||||
|
return data->query_result;
|
||||||
|
|
||||||
|
return sprintf(buf, "%s\n", data->fw_version);
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RO(fw_version);
|
||||||
|
|
||||||
|
static ssize_t exc3000_get_model(struct exc3000_data *data)
|
||||||
|
{
|
||||||
|
static const u8 request[68] = {
|
||||||
|
0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00
|
||||||
|
};
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
mutex_lock(&data->query_lock);
|
||||||
|
data->query_result = -ETIMEDOUT;
|
||||||
|
reinit_completion(&data->wait_event);
|
||||||
|
|
||||||
|
error = i2c_master_send(client, request, sizeof(request));
|
||||||
|
if (error < 0) {
|
||||||
|
mutex_unlock(&data->query_lock);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
|
||||||
|
mutex_unlock(&data->query_lock);
|
||||||
|
|
||||||
|
return data->query_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t model_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct exc3000_data *data = i2c_get_clientdata(client);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = exc3000_get_model(data);
|
||||||
|
if (error < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return sprintf(buf, "%s\n", data->model);
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RO(model);
|
||||||
|
|
||||||
|
static struct attribute *sysfs_attrs[] = {
|
||||||
|
&dev_attr_fw_version.attr,
|
||||||
|
&dev_attr_model.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute_group exc3000_attribute_group = {
|
||||||
|
.attrs = sysfs_attrs
|
||||||
|
};
|
||||||
|
|
||||||
|
static int exc3000_probe(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct exc3000_data *data;
|
struct exc3000_data *data;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
int error;
|
int error, max_xy, retry;
|
||||||
|
|
||||||
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
data->client = client;
|
data->client = client;
|
||||||
|
data->info = device_get_match_data(&client->dev);
|
||||||
|
if (!data->info) {
|
||||||
|
enum eeti_dev_id eeti_dev_id =
|
||||||
|
i2c_match_id(exc3000_id, client)->driver_data;
|
||||||
|
data->info = &exc3000_info[eeti_dev_id];
|
||||||
|
}
|
||||||
timer_setup(&data->timer, exc3000_timer, 0);
|
timer_setup(&data->timer, exc3000_timer, 0);
|
||||||
|
init_completion(&data->wait_event);
|
||||||
|
mutex_init(&data->query_lock);
|
||||||
|
|
||||||
|
data->reset = devm_gpiod_get_optional(&client->dev, "reset",
|
||||||
|
GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(data->reset))
|
||||||
|
return PTR_ERR(data->reset);
|
||||||
|
|
||||||
|
if (data->reset) {
|
||||||
|
msleep(EXC3000_RESET_MS);
|
||||||
|
gpiod_set_value_cansleep(data->reset, 0);
|
||||||
|
msleep(EXC3000_READY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
input = devm_input_allocate_device(&client->dev);
|
input = devm_input_allocate_device(&client->dev);
|
||||||
if (!input)
|
if (!input)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
data->input = input;
|
data->input = input;
|
||||||
|
input_set_drvdata(input, data);
|
||||||
|
|
||||||
input->name = "EETI EXC3000 Touch Screen";
|
input->name = data->info->name;
|
||||||
input->id.bustype = BUS_I2C;
|
input->id.bustype = BUS_I2C;
|
||||||
|
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0);
|
max_xy = data->info->max_xy;
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0);
|
input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0);
|
||||||
|
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
|
||||||
|
|
||||||
touchscreen_parse_properties(input, true, &data->prop);
|
touchscreen_parse_properties(input, true, &data->prop);
|
||||||
|
|
||||||
error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
|
error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
|
||||||
@ -187,18 +370,49 @@ static int exc3000_probe(struct i2c_client *client,
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I²C does not have built-in recovery, so retry on failure. This
|
||||||
|
* ensures, that the device probe will not fail for temporary issues
|
||||||
|
* on the bus. This is not needed for the sysfs calls (userspace
|
||||||
|
* will receive the error code and can start another query) and
|
||||||
|
* cannot be done for touch events (but that only means loosing one
|
||||||
|
* or two touch events anyways).
|
||||||
|
*/
|
||||||
|
for (retry = 0; retry < 3; retry++) {
|
||||||
|
error = exc3000_get_model(data);
|
||||||
|
if (!error)
|
||||||
|
break;
|
||||||
|
dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n",
|
||||||
|
retry + 1, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "TS Model: %s", data->model);
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, data);
|
||||||
|
|
||||||
|
error = devm_device_add_group(&client->dev, &exc3000_attribute_group);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id exc3000_id[] = {
|
static const struct i2c_device_id exc3000_id[] = {
|
||||||
{ "exc3000", 0 },
|
{ "exc3000", EETI_EXC3000 },
|
||||||
|
{ "exc80h60", EETI_EXC80H60 },
|
||||||
|
{ "exc80h84", EETI_EXC80H84 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, exc3000_id);
|
MODULE_DEVICE_TABLE(i2c, exc3000_id);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id exc3000_of_match[] = {
|
static const struct of_device_id exc3000_of_match[] = {
|
||||||
{ .compatible = "eeti,exc3000" },
|
{ .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] },
|
||||||
|
{ .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] },
|
||||||
|
{ .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, exc3000_of_match);
|
MODULE_DEVICE_TABLE(of, exc3000_of_match);
|
||||||
@ -210,7 +424,7 @@ static struct i2c_driver exc3000_driver = {
|
|||||||
.of_match_table = of_match_ptr(exc3000_of_match),
|
.of_match_table = of_match_ptr(exc3000_of_match),
|
||||||
},
|
},
|
||||||
.id_table = exc3000_id,
|
.id_table = exc3000_id,
|
||||||
.probe = exc3000_probe,
|
.probe_new = exc3000_probe,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_i2c_driver(exc3000_driver);
|
module_i2c_driver(exc3000_driver);
|
||||||
|
@ -289,7 +289,7 @@ static int iqs5xx_bl_cmd(struct i2c_client *client, u8 bl_cmd, u16 bl_addr)
|
|||||||
break;
|
break;
|
||||||
case IQS5XX_BL_CMD_EXEC:
|
case IQS5XX_BL_CMD_EXEC:
|
||||||
usleep_range(10000, 10100);
|
usleep_range(10000, 10100);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,6 @@ static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
|
|||||||
|
|
||||||
switch (buf[1] & EVENT_TAG_MASK) {
|
switch (buf[1] & EVENT_TAG_MASK) {
|
||||||
case EVENT_INIT:
|
case EVENT_INIT:
|
||||||
/* fall through */
|
|
||||||
case EVENT_MIDDLE:
|
case EVENT_MIDDLE:
|
||||||
input_report_abs(data->input_dev, ABS_X, x);
|
input_report_abs(data->input_dev, ABS_X, x);
|
||||||
input_report_abs(data->input_dev, ABS_Y, y);
|
input_report_abs(data->input_dev, ABS_Y, y);
|
||||||
|
@ -255,7 +255,7 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
|
|||||||
case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
|
case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
|
||||||
case STMFTS_EV_STATUS:
|
case STMFTS_EV_STATUS:
|
||||||
complete(&sdata->cmd_done);
|
complete(&sdata->cmd_done);
|
||||||
/* fall through */
|
fallthrough;
|
||||||
|
|
||||||
case STMFTS_EV_NO_EVENT:
|
case STMFTS_EV_NO_EVENT:
|
||||||
case STMFTS_EV_DEBUG:
|
case STMFTS_EV_DEBUG:
|
||||||
|
@ -206,7 +206,7 @@ struct rmi_device_platform_data_spi {
|
|||||||
*
|
*
|
||||||
* @reset_delay_ms - after issuing a reset command to the touch sensor, the
|
* @reset_delay_ms - after issuing a reset command to the touch sensor, the
|
||||||
* driver waits a few milliseconds to give the firmware a chance to
|
* driver waits a few milliseconds to give the firmware a chance to
|
||||||
* to re-initialize. You can override the default wait period here.
|
* re-initialize. You can override the default wait period here.
|
||||||
* @irq: irq associated with the attn gpio line, or negative
|
* @irq: irq associated with the attn gpio line, or negative
|
||||||
*/
|
*/
|
||||||
struct rmi_device_platform_data {
|
struct rmi_device_platform_data {
|
||||||
|
Loading…
Reference in New Issue
Block a user