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:
Linus Torvalds 2020-08-10 16:35:57 -07:00
commit 4bcf69e570
47 changed files with 718 additions and 265 deletions

View 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

View File

@ -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;
};
};

View File

@ -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;
};

View File

@ -99,7 +99,7 @@ the sake of simplicity.
/*
* Give userspace some time to read the events before we destroy the
* device with UI_DEV_DESTOY.
* device with UI_DEV_DESTROY.
*/
sleep(1);
@ -164,7 +164,7 @@ mouse.
/*
* Give userspace some time to read the events before we destroy the
* device with UI_DEV_DESTOY.
* device with UI_DEV_DESTROY.
*/
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
* device with UI_DEV_DESTOY.
* device with UI_DEV_DESTROY.
*/
sleep(1);

View File

@ -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)) {
dev->absinfo[dst] = dev->absinfo[src];
dev->absinfo[dst].fuzz = 0;
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
__set_bit(dst, dev->absbit);
}
}

View File

@ -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);
return data[0] = 0xe3;
}
/* fall through */
fallthrough;
default:
return data[0];
}
@ -267,14 +267,14 @@ static int db9_saturn_report(unsigned char id, unsigned char data[60], struct in
switch (data[j]) {
case 0x16: /* multi controller (analog 4 axis) */
input_report_abs(dev, db9_abs[5], data[j + 6]);
/* fall through */
fallthrough;
case 0x15: /* mission stick (analog 3 axis) */
input_report_abs(dev, db9_abs[3], data[j + 4]);
input_report_abs(dev, db9_abs[4], data[j + 5]);
/* fall through */
fallthrough;
case 0x13: /* racing controller (analog 1 axis) */
input_report_abs(dev, db9_abs[2], data[j + 3]);
/* fall through */
fallthrough;
case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
case 0x02: /* digital pad (digital 2 axis + buttons) */
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_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1);
/* fall through */
fallthrough;
case DB9_MULTI_0802:

View File

@ -485,7 +485,7 @@ static void gc_multi_process_packet(struct gc *gc)
switch (pad->type) {
case GC_MULTI2:
input_report_key(dev, BTN_THUMB, s & data[5]);
/* fall through */
fallthrough;
case GC_MULTI:
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_THUMBR, ~data[0] & 0x02);
/* fall through */
fallthrough;
case GC_PSX_NEGCON:
case GC_PSX_ANALOG:
@ -872,7 +872,8 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
case GC_SNES:
for (i = 4; i < 8; i++)
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
/* fall through */
fallthrough;
case GC_NES:
for (i = 0; i < 4; 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:
input_set_capability(input_dev, EV_KEY, BTN_THUMB);
/* fall through */
fallthrough;
case GC_MULTI:
input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
/* fall through */

View File

@ -656,16 +656,19 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
switch (i * m) {
case 60:
sw->number++; /* fall through */
sw->number++;
fallthrough;
case 45: /* Ambiguous packet length */
if (j <= 40) { /* ID length less or eq 40 -> FSP */
case 43:
sw->type = SW_ID_FSP;
break;
}
sw->number++; /* fall through */
sw->number++;
fallthrough;
case 30:
sw->number++; /* fall through */
sw->number++;
fallthrough;
case 15:
sw->type = SW_ID_GP;
break;
@ -681,9 +684,11 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
sw->type = SW_ID_PP;
break;
case 66:
sw->bits = 3; /* fall through */
sw->bits = 3;
fallthrough;
case 198:
sw->length = 22; /* fall through */
sw->length = 22;
fallthrough;
case 64:
sw->type = SW_ID_3DP;
if (j == 160)

View File

@ -146,7 +146,7 @@ static irqreturn_t spaceball_interrupt(struct serio *serio,
break;
}
spaceball->escape = 0;
/* fall through */
fallthrough;
case 'M':
case 'Q':
case 'S':
@ -154,7 +154,7 @@ static irqreturn_t spaceball_interrupt(struct serio *serio,
spaceball->escape = 0;
data &= 0x1f;
}
/* fall through */
fallthrough;
default:
if (spaceball->escape)
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) |
BIT_MASK(BTN_B) | BIT_MASK(BTN_C) |
BIT_MASK(BTN_MODE);
/* fall through */
fallthrough;
default:
input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) |
BIT_MASK(BTN_3) | BIT_MASK(BTN_4) |
BIT_MASK(BTN_5) | BIT_MASK(BTN_6) |
BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
/* fall through */
fallthrough;
case SPACEBALL_3003C:
input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) |
BIT_MASK(BTN_8);

View File

@ -1016,7 +1016,7 @@ static int adp5589_probe(struct i2c_client *client,
switch (id->driver_data) {
case ADP5585_02:
kpad->support_row5 = true;
/* fall through */
fallthrough;
case ADP5585_01:
kpad->is_adp5585 = true;
kpad->var = &const_adp5585;

View File

@ -1241,7 +1241,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
case SERIO_8042_XL:
atkbd->translated = true;
/* Fall through */
fallthrough;
case SERIO_8042:
if (serio->write)

View File

@ -574,7 +574,6 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
break;
case EV_ACT_ANY:
/* fall through */
default:
/*
* For other cases, we are OK letting suspend/resume

View File

@ -68,7 +68,7 @@ static int ati_remote2_get_channel_mask(char *buffer,
{
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,
@ -84,7 +84,7 @@ static int ati_remote2_get_mode_mask(char *buffer,
{
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;

View File

@ -663,12 +663,8 @@ static const struct usb_device_id cm109_usb_table[] = {
static void cm109_usb_cleanup(struct cm109_dev *dev)
{
kfree(dev->ctl_req);
if (dev->ctl_data)
usb_free_coherent(dev->udev, USB_PKT_LEN,
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_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
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_ctl); /* parameter validation in core/urb */

View File

@ -335,7 +335,7 @@ static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
err_free_mem:
input_free_device(input);
kfree(gamepad);
return -ENOMEM;
return error;
}
static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)

View File

@ -575,8 +575,7 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269,
case IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5:
engine_a |= IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
/* fall through */
fallthrough;
case IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY:
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].enabled = true;
}
/* fall through */
fallthrough;
case IQS269_CHx_HALL_INACTIVE:
if (iqs269->hall_enable)
break;
/* fall through */
fallthrough;
default:
iqs269->keycode[i * IQS269_NUM_CH + reg] = val;
@ -1143,14 +1140,12 @@ static int iqs269_input_init(struct iqs269_private *iqs269)
sw_code,
state & BIT(j));
}
/* fall through */
fallthrough;
case IQS269_CHx_HALL_INACTIVE:
if (iqs269->hall_enable)
continue;
/* fall through */
fallthrough;
default:
if (keycode != KEY_RESERVED)
@ -1273,14 +1268,12 @@ static int iqs269_report(struct iqs269_private *iqs269)
input_report_switch(iqs269->keypad,
sw_code,
state & BIT(j));
/* fall through */
fallthrough;
case IQS269_CHx_HALL_INACTIVE:
if (iqs269->hall_enable)
continue;
/* fall through */
fallthrough;
default:
input_report_key(iqs269->keypad, keycode,

View File

@ -190,7 +190,7 @@ static int pwm_vibrator_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
/* Fall through */
fallthrough;
case -EPROBE_DEFER:
return err;

View File

@ -124,7 +124,7 @@ static void xenkbd_handle_mt_event(struct xenkbd_info *info,
switch (mtouch->event_type) {
case XENKBD_MT_EV_DOWN:
input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
/* fall through */
fallthrough;
case XENKBD_MT_EV_MOTION:
input_report_abs(info->mtouch, ABS_MT_POSITION_X,
@ -524,7 +524,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
case XenbusStateClosed:
if (dev->state == XenbusStateClosed)
break;
/* fall through - Missed the backend's CLOSING state */
fallthrough; /* Missed the backend's CLOSING state */
case XenbusStateClosing:
xenbus_frontend_closed(dev);
break;

View File

@ -1929,7 +1929,7 @@ static int alps_monitor_mode(struct psmouse *psmouse, bool enable)
static int alps_absolute_mode_v6(struct psmouse *psmouse)
{
u16 reg_val = 0x181;
int ret = -1;
int ret;
/* enter monitor mode, to write the register */
if (alps_monitor_mode(psmouse, true))

View File

@ -458,7 +458,7 @@ static int atp_status_check(struct urb *urb)
dev->info->datalen, dev->urb->actual_length);
dev->overflow_warned = true;
}
/* fall through */
fallthrough;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:

View File

@ -1067,7 +1067,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
return error;
}
/* Fall through */
fallthrough;
case CYAPA_STATE_BL_IDLE:
/* Try to get firmware version in bootloader mode. */
cyapa_gen3_bl_query_data(cyapa);
@ -1078,7 +1078,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
return error;
}
/* Fall through */
fallthrough;
case CYAPA_STATE_OP:
/*
* Reading query data before going back to the full mode

View File

@ -2554,7 +2554,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
}
cyapa->state = CYAPA_STATE_GEN5_APP;
/* fall through */
fallthrough;
case CYAPA_STATE_GEN5_APP:
/*

View File

@ -680,7 +680,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa)
}
cyapa->state = CYAPA_STATE_GEN6_APP;
/* fall through */
fallthrough;
case CYAPA_STATE_GEN6_APP:
/*

View File

@ -26,6 +26,8 @@
#define ETP_CALIBRATE_MAX_LEN 3
#define ETP_FEATURE_REPORT_MK BIT(0)
/* IAP Firmware handling */
#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0"
#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_INTF_ERR (1 << 4)
#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
struct i2c_client;
@ -55,8 +59,9 @@ struct elan_transport_ops {
int (*get_baseline_data)(struct i2c_client *client,
bool max_baseliune, u8 *value);
int (*get_version)(struct i2c_client *client, bool iap, u8 *version);
int (*get_sm_version)(struct i2c_client *client,
int (*get_version)(struct i2c_client *client, u8 pattern, bool iap,
u8 *version);
int (*get_sm_version)(struct i2c_client *client, u8 pattern,
u16 *ic_type, u8 *version, u8 *clickpad);
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
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_reset)(struct i2c_client *client);
int (*prepare_fw_update)(struct i2c_client *client);
int (*write_fw_block)(struct i2c_client *client,
int (*prepare_fw_update)(struct i2c_client *client, u16 ic_type,
u8 iap_version);
int (*write_fw_block)(struct i2c_client *client, u16 fw_page_size,
const u8 *page, u16 checksum, int idx);
int (*finish_fw_update)(struct i2c_client *client,
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 *adjustment);
int (*get_pattern)(struct i2c_client *client, u8 *pattern);

View File

@ -50,12 +50,14 @@
#define ETP_MAX_FINGERS 5
#define ETP_FINGER_DATA_LEN 5
#define ETP_REPORT_ID 0x5D
#define ETP_REPORT_ID2 0x60 /* High precision report */
#define ETP_TP_REPORT_ID 0x5E
#define ETP_REPORT_ID_OFFSET 2
#define ETP_TOUCH_INFO_OFFSET 3
#define ETP_FINGER_DATA_OFFSET 4
#define ETP_HOVER_INFO_OFFSET 30
#define ETP_MAX_REPORT_LEN 34
#define ETP_MK_DATA_OFFSET 33 /* For high precision reports */
#define ETP_MAX_REPORT_LEN 39
/* The main device structure */
struct elan_tp_data {
@ -85,11 +87,14 @@ struct elan_tp_data {
u8 sm_version;
u8 iap_version;
u16 fw_checksum;
unsigned int report_features;
unsigned int report_len;
int pressure_adjustment;
u8 mode;
u16 ic_type;
u16 fw_validpage_count;
u16 fw_signature_address;
u16 fw_page_size;
u32 fw_signature_address;
bool irq_wake;
@ -100,8 +105,8 @@ struct elan_tp_data {
bool middle_button;
};
static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
u16 *signature_address)
static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count,
u32 *signature_address, u16 *page_size)
{
switch (ic_type) {
case 0x00:
@ -126,16 +131,37 @@ static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
case 0x10:
*validpage_count = 1024;
break;
case 0x11:
*validpage_count = 1280;
break;
case 0x13:
*validpage_count = 2048;
break;
case 0x14:
case 0x15:
*validpage_count = 1024;
break;
default:
/* unknown ic type clear value */
*validpage_count = 0;
*signature_address = 0;
*page_size = 0;
return -ENXIO;
}
*signature_address =
(*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;
}
@ -215,8 +241,13 @@ static int elan_query_product(struct elan_tp_data *data)
if (error)
return error;
error = data->ops->get_sm_version(data->client, &data->ic_type,
&data->sm_version, &data->clickpad);
error = data->ops->get_pattern(data->client, &data->pattern);
if (error)
return error;
error = data->ops->get_sm_version(data->client, data->pattern,
&data->ic_type, &data->sm_version,
&data->clickpad);
if (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)
{
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)
return error;
@ -323,7 +354,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
if (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)
return error;
@ -332,17 +364,16 @@ static int elan_query_device_info(struct elan_tp_data *data)
if (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)
return error;
if (data->pattern == 0x01)
ic_type = data->ic_type;
else
ic_type = data->iap_version;
error = elan_get_fwinfo(ic_type, &data->fw_validpage_count,
&data->fw_signature_address);
error = elan_get_fwinfo(data->ic_type, data->iap_version,
&data->fw_validpage_count,
&data->fw_signature_address,
&data->fw_page_size);
if (error)
dev_warn(&data->client->dev,
"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;
}
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
* point).
*/
return ((int)(char)val * 10 + 790) * 10 / 254;
return res * 10 / 254;
}
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)
return error;
data->x_res = elan_convert_resolution(hw_x_res);
data->y_res = elan_convert_resolution(hw_y_res);
data->x_res = elan_convert_resolution(hw_x_res, data->pattern);
data->y_res = elan_convert_resolution(hw_y_res, data->pattern);
} else {
data->x_res = (data->max_x + 1) / x_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
**********************************************************
*/
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)
{
int retry = ETP_RETRY_COUNT;
int error;
do {
error = data->ops->write_fw_block(data->client,
error = data->ops->write_fw_block(data->client, page_size,
page, checksum, idx);
if (!error)
return 0;
@ -460,21 +496,23 @@ static int __elan_update_firmware(struct elan_tp_data *data,
u16 boot_page_count;
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)
return error;
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++) {
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]);
error = elan_write_fw_block(data, page, checksum, i);
error = elan_write_fw_block(data, data->fw_page_size,
page, checksum, i);
if (error) {
dev_err(dev, "write page %d fail: %d\n", i, error);
return error;
@ -886,24 +924,22 @@ static const struct attribute_group *elan_sysfs_groups[] = {
* Elan isr functions
******************************************************************
*/
static void elan_report_contact(struct elan_tp_data *data,
int contact_num, bool contact_valid,
u8 *finger_data)
static void elan_report_contact(struct elan_tp_data *data, int contact_num,
bool contact_valid, bool high_precision,
u8 *packet, u8 *finger_data)
{
struct input_dev *input = data->input;
unsigned int pos_x, pos_y;
unsigned int pressure, mk_x, mk_y;
unsigned int area_x, area_y, major, minor;
unsigned int scaled_pressure;
unsigned int pressure, scaled_pressure;
if (contact_valid) {
pos_x = ((finger_data[0] & 0xf0) << 4) |
finger_data[1];
pos_y = ((finger_data[0] & 0x0f) << 8) |
finger_data[2];
mk_x = (finger_data[3] & 0x0f);
mk_y = (finger_data[3] >> 4);
pressure = finger_data[4];
if (high_precision) {
pos_x = get_unaligned_be16(&finger_data[0]);
pos_y = get_unaligned_be16(&finger_data[2]);
} else {
pos_x = ((finger_data[0] & 0xf0) << 4) | finger_data[1];
pos_y = ((finger_data[0] & 0x0f) << 8) | finger_data[2];
}
if (pos_x > data->max_x || pos_y > data->max_y) {
dev_dbg(input->dev.parent,
@ -913,18 +949,8 @@ static void elan_report_contact(struct elan_tp_data *data,
return;
}
/*
* 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);
pressure = finger_data[4];
scaled_pressure = pressure + data->pressure_adjustment;
if (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_Y, data->max_y - pos_y);
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);
input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
if (data->report_features & ETP_FEATURE_REPORT_MK) {
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 {
input_mt_slot(input, contact_num);
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;
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);
hover_event = hover_info & 0x40;
for (i = 0; i < ETP_MAX_FINGERS; i++) {
contact_valid = tp_info & (1U << (3 + i));
elan_report_contact(data, i, contact_valid, finger_data);
hover_event = hover_info & BIT(6);
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)
finger_data += ETP_FINGER_DATA_LEN;
}
@ -1015,13 +1063,16 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
goto out;
}
error = data->ops->get_report(data->client, report);
error = data->ops->get_report(data->client, report, data->report_len);
if (error)
goto out;
switch (report[ETP_REPORT_ID_OFFSET]) {
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;
case ETP_TP_REPORT_ID:
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_Y, data->y_res);
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);
/* 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_set_abs_params(input, ABS_MT_PRESSURE, 0,
ETP_MAX_PRESSURE, 0, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
ETP_FINGER_WIDTH * max_width, 0, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
ETP_FINGER_WIDTH * min_width, 0, 0);
if (data->report_features & ETP_FEATURE_REPORT_MK) {
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
0, ETP_FINGER_WIDTH * max_width, 0, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
0, ETP_FINGER_WIDTH * min_width, 0, 0);
}
data->input = input;

View File

@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <asm/unaligned.h>
@ -43,6 +44,8 @@
#define ETP_I2C_RESOLUTION_CMD 0x0108
#define ETP_I2C_PRESSURE_CMD 0x010A
#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_POWER_CMD 0x0307
#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
@ -53,8 +56,12 @@
#define ETP_I2C_CALIBRATE_CMD 0x0316
#define ETP_I2C_MAX_BASELINE_CMD 0x0317
#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_ID2 39
#define ETP_I2C_REPORT_MAX_LEN 39
#define ETP_I2C_DESC_LENGTH 30
#define ETP_I2C_REPORT_DESC_LENGTH 158
#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);
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;
}
static int elan_i2c_get_version(struct i2c_client *client,
bool iap, u8 *version)
u8 pattern, bool iap, u8 *version)
{
int error;
u8 pattern_ver;
u16 cmd;
u8 val[3];
error = elan_i2c_get_pattern(client, &pattern_ver);
if (error) {
dev_err(&client->dev, "failed to get pattern version\n");
return error;
}
if (!iap)
cmd = ETP_I2C_FW_VERSION_CMD;
else if (pattern == 0)
cmd = ETP_I2C_IAP_VERSION_P0_CMD;
else
cmd = ETP_I2C_IAP_VERSION_CMD;
error = elan_i2c_read_cmd(client,
iap ? ETP_I2C_IAP_VERSION_CMD :
ETP_I2C_FW_VERSION_CMD,
val);
error = elan_i2c_read_cmd(client, cmd, val);
if (error) {
dev_err(&client->dev, "failed to get %s version: %d\n",
iap ? "IAP" : "FW", error);
return error;
}
if (pattern_ver == 0x01)
if (pattern >= 0x01)
*version = iap ? val[1] : val[0];
else
*version = val[0];
return 0;
}
static int elan_i2c_get_sm_version(struct i2c_client *client,
u16 *ic_type, u8 *version,
u8 *clickpad)
static int elan_i2c_get_sm_version(struct i2c_client *client, u8 pattern,
u16 *ic_type, u8 *version, u8 *clickpad)
{
int error;
u8 pattern_ver;
u8 val[3];
error = elan_i2c_get_pattern(client, &pattern_ver);
if (error) {
dev_err(&client->dev, "failed to get pattern version\n");
return error;
}
if (pattern_ver == 0x01) {
if (pattern >= 0x01) {
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_CMD, val);
if (error) {
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;
}
*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,
val);
@ -386,7 +396,7 @@ static int elan_i2c_get_max(struct i2c_client *client,
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);
if (error) {
@ -394,7 +404,7 @@ static int elan_i2c_get_max(struct i2c_client *client,
return error;
}
*max_y = le16_to_cpup((__le16 *)val) & 0x0fff;
*max_y = le16_to_cpup((__le16 *)val);
return 0;
}
@ -507,7 +517,43 @@ static int elan_i2c_set_flash_key(struct i2c_client *client)
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;
int error;
@ -547,6 +593,12 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client)
return -EIO;
}
if (ic_type >= 0x0D && iap_version >= 1) {
error = elan_read_write_iap_type(client);
if (error)
return error;
}
/* Set flash key again */
error = elan_i2c_set_flash_key(client);
if (error)
@ -572,57 +624,64 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client)
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)
{
struct device *dev = &client->dev;
u8 page_store[ETP_FW_PAGE_SIZE + 4];
u8 *page_store;
u8 val[3];
u16 result;
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[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 */
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));
if (ret != sizeof(page_store)) {
ret = i2c_master_send(client, page_store, fw_page_size + 4);
if (ret != fw_page_size + 4) {
error = ret < 0 ? ret : -EIO;
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. */
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);
if (error) {
dev_err(dev, "Failed to read IAP write result: %d\n", error);
return error;
goto exit;
}
result = le16_to_cpup((__le16 *)val);
if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
dev_err(dev, "IAP reports failed write: %04hx\n",
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,
struct completion *completion)
{
struct device *dev = &client->dev;
int error;
int error = 0;
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);
if (len != ETP_I2C_REPORT_LEN) {
len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_MAX_LEN);
if (len <= 0) {
error = len < 0 ? len : -EIO;
dev_warn(dev, "failed to read I2C data after FW WDT reset: %d (%d)\n",
error, len);
@ -656,20 +715,31 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client,
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;
len = i2c_master_recv(client, report, ETP_I2C_REPORT_LEN);
len = i2c_master_recv(client, report, report_len);
if (len < 0) {
dev_err(&client->dev, "failed to read report data: %d\n", len);
return len;
}
if (len != ETP_I2C_REPORT_LEN) {
if (len != report_len) {
dev_err(&client->dev,
"wrong report length (%d vs %d expected)\n",
len, ETP_I2C_REPORT_LEN);
len, report_len);
return -EIO;
}
@ -706,5 +776,6 @@ const struct elan_transport_ops elan_i2c_ops = {
.get_pattern = elan_i2c_get_pattern,
.get_report_features = elan_i2c_get_report_features,
.get_report = elan_i2c_get_report,
};

View File

@ -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,
bool iap, u8 *version)
u8 pattern, bool iap, u8 *version)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
@ -166,9 +166,8 @@ static int elan_smbus_get_version(struct i2c_client *client,
return 0;
}
static int elan_smbus_get_sm_version(struct i2c_client *client,
u16 *ic_type, u8 *version,
u8 *clickpad)
static int elan_smbus_get_sm_version(struct i2c_client *client, u8 pattern,
u16 *ic_type, u8 *version, u8 *clickpad)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
@ -340,7 +339,8 @@ static int elan_smbus_set_flash_key(struct i2c_client *client)
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;
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)
{
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,
ETP_SMBUS_WRITE_FW_BLOCK,
ETP_FW_PAGE_SIZE / 2,
fw_page_size / 2,
page);
if (error) {
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,
ETP_SMBUS_WRITE_FW_BLOCK,
ETP_FW_PAGE_SIZE / 2,
page + ETP_FW_PAGE_SIZE / 2);
fw_page_size / 2,
page + fw_page_size / 2);
if (error) {
dev_err(dev, "Failed to write page %d (part %d): %d\n",
idx, 2, error);
@ -469,7 +469,21 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
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;
@ -534,6 +548,7 @@ const struct elan_transport_ops elan_smbus_ops = {
.write_fw_block = elan_smbus_write_fw_block,
.finish_fw_update = elan_smbus_finish_fw_update,
.get_report_features = elan_smbus_get_report_features,
.get_report = elan_smbus_get_report,
.get_pattern = elan_smbus_get_pattern,
};

View File

@ -383,7 +383,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
*/
if (packet[3] & 0x80)
fingers = 4;
/* fall through */
fallthrough;
case 1:
/*
* byte 1: . . . . x11 x10 x9 x8
@ -1146,7 +1146,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
case 2:
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
/* fall through */
fallthrough;
case 3:
if (info->hw_version == 3)
elantech_set_buttonpad_prop(psmouse);
@ -1877,12 +1877,10 @@ static bool elantech_use_host_notify(struct psmouse *psmouse,
/* expected case */
break;
case ETP_BUS_SMB_ALERT_ONLY:
/* fall-through */
case ETP_BUS_PS2_SMB_ALERT:
psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
break;
case ETP_BUS_SMB_HST_NTFY_ONLY:
/* fall-through */
case ETP_BUS_PS2_SMB_HST_NTFY:
return true;
default:
@ -1897,7 +1895,7 @@ static bool elantech_use_host_notify(struct psmouse *psmouse,
int elantech_init_smbus(struct psmouse *psmouse)
{
struct elantech_device_info info;
int error = -EINVAL;
int error;
psmouse_reset(psmouse);
@ -2015,7 +2013,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
int elantech_init_ps2(struct psmouse *psmouse)
{
struct elantech_device_info info;
int error = -EINVAL;
int error;
psmouse_reset(psmouse);
@ -2036,7 +2034,7 @@ int elantech_init_ps2(struct psmouse *psmouse)
int elantech_init(struct psmouse *psmouse)
{
struct elantech_device_info info;
int error = -EINVAL;
int error;
psmouse_reset(psmouse);

View File

@ -238,7 +238,7 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
/* we're not spewing, but this packet might be the start */
priv->spew_flag = MAYBE_SPEWING;
/* fall-through */
fallthrough;
case MAYBE_SPEWING:
priv->spew_count++;
@ -249,7 +249,7 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
/* excessive spew detected, request recalibration */
priv->spew_flag = SPEW_DETECTED;
/* fall-through */
fallthrough;
case SPEW_DETECTED:
/* only recalibrate when the overall delta to the cursor

View File

@ -105,7 +105,7 @@ static void navpoint_packet(struct navpoint *navpoint)
case 0x19: /* Module 0, Hello packet */
if ((navpoint->data[1] & 0xf0) == 0x10)
break;
/* FALLTHROUGH */
fallthrough;
default:
dev_warn(navpoint->dev,
"spurious packet: data=0x%02x,0x%02x,...\n",

View File

@ -2042,7 +2042,7 @@ static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp)
{
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)

View File

@ -441,7 +441,7 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
fsp_reg_write_enable(psmouse, false);
return count;
return retval;
}
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 */
if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
packet[0] &= ~FSP_PB0_LBTN;
/* fall through */
fallthrough;
case FSP_PKT_TYPE_NORMAL:
/* normal packet */

View File

@ -128,7 +128,7 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
case SERIO_MS:
sermouse->type = SERIO_MP;
/* fall through */
fallthrough;
case SERIO_MP:
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_MZPP:
input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
/* fall through */
fallthrough;
case SERIO_MZ:
input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);

View File

@ -562,7 +562,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
str = last_str;
break;
}
/* fall through - report timeout */
fallthrough; /* report timeout */
case 0xfc:
case 0xfd:
case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;

View File

@ -418,7 +418,7 @@ bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
ps2dev->nak = 0;
break;
}
/* Fall through */
fallthrough;
default:
/*
* Do not signal errors if we get unexpected reply while

View File

@ -247,7 +247,7 @@ void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *k
case KE_SW:
value = ke->sw.value;
/* fall through */
fallthrough;
case KE_VSW:
input_report_switch(dev, ke->sw.code, value);

View File

@ -676,8 +676,8 @@ static void gtco_urb_callback(struct urb *urbinfo)
/* Mask out the Y tilt value used for pressure */
device->buffer[7] = (u8)((device->buffer[7]) & 0x7F);
fallthrough;
/* Fall thru */
case 4:
/* Tilt */
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,
sign_extend32(device->buffer[7], 6));
fallthrough;
/* Fall thru */
case 2:
case 3:
/* 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,
just report */
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
fallthrough;
/* Fall thru */
case 1:
/* All reports have X and Y coords in the same place */
val = get_unaligned_le16(&device->buffer[1]);

View File

@ -146,7 +146,7 @@ static void pegasus_parse_packet(struct pegasus *pegasus)
/* xy data */
case BATTERY_LOW:
dev_warn_once(&dev->dev, "Pen battery low\n");
/* fall through */
fallthrough;
case BATTERY_NO_REPORT:
case BATTERY_GOOD:

View File

@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/slab.h>
@ -129,6 +130,7 @@ struct t9_range {
/* MXT_SPT_COMMSCONFIG_T18 */
#define MXT_COMMS_CTRL 0
#define MXT_COMMS_CMD 1
#define MXT_COMMS_RETRIGEN BIT(6)
/* MXT_DEBUG_DIAGNOSTIC_T37 */
#define MXT_DIAGNOSTIC_PAGEUP 0x01
@ -308,6 +310,7 @@ struct mxt_data {
struct t7_config t7_cfg;
struct mxt_dbg dbg;
struct gpio_desc *reset_gpio;
bool use_retrigen_workaround;
/* Cached parameters from object table */
u16 T5_address;
@ -318,6 +321,7 @@ struct mxt_data {
u16 T71_address;
u8 T9_reportid_min;
u8 T9_reportid_max;
u16 T18_address;
u8 T19_reportid;
u16 T44_address;
u8 T100_reportid_min;
@ -1190,9 +1194,11 @@ static int mxt_acquire_irq(struct mxt_data *data)
enable_irq(data->irq);
error = mxt_process_messages_until_invalid(data);
if (error)
return error;
if (data->use_retrigen_workaround) {
error = mxt_process_messages_until_invalid(data);
if (error)
return error;
}
return 0;
}
@ -1282,6 +1288,38 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
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)
{
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);
ret = mxt_check_retrigen(data);
if (ret)
goto release_mem;
ret = mxt_soft_reset(data);
if (ret)
goto release_mem;
@ -1604,6 +1646,7 @@ static void mxt_free_object_table(struct mxt_data *data)
data->T71_address = 0;
data->T9_reportid_min = 0;
data->T9_reportid_max = 0;
data->T18_address = 0;
data->T19_reportid = 0;
data->T44_address = 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;
data->num_touchids = object->num_report_ids;
break;
case MXT_SPT_COMMSCONFIG_T18:
data->T18_address = object->start_address;
break;
case MXT_SPT_MESSAGECOUNT_T44:
data->T44_address = object->start_address;
break;
@ -2141,6 +2187,10 @@ static int mxt_initialize(struct mxt_data *data)
if (error)
return error;
error = mxt_check_retrigen(data);
if (error)
return error;
error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
&client->dev, GFP_KERNEL, data,
mxt_config_cb);

View File

@ -288,7 +288,7 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
wrbuf, 0, NULL);
/* fallthrough */
case EDT_M09:
case EDT_M12:
case EV_FT:
@ -330,7 +330,6 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
}
break;
/* fallthrough */
case EDT_M09:
case EDT_M12:
case EV_FT:

View File

@ -955,7 +955,7 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
break;
ts->state = ELAN_STATE_NORMAL;
/* fall through */
fallthrough;
case ELAN_STATE_NORMAL:

View File

@ -348,7 +348,7 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
case 1: /* 6-byte protocol */
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
/* fall through */
fallthrough;
case 2: /* 4-byte protocol */
input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);

View File

@ -8,7 +8,9 @@
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
@ -16,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/sizes.h>
#include <linux/timer.h>
#include <asm/unaligned.h>
@ -23,15 +26,59 @@
#define EXC3000_SLOTS_PER_FRAME 5
#define EXC3000_LEN_FRAME 66
#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_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 i2c_client *client;
const struct eeti_dev_info *info;
struct input_dev *input;
struct touchscreen_properties prop;
struct gpio_desc *reset;
struct timer_list timer;
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,
@ -58,10 +105,15 @@ static void exc3000_timer(struct timer_list *t)
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;
if (data->info->max_xy == SZ_16K - 1)
expected_event = EXC3000_MT2_EVENT;
ret = i2c_master_send(client, "'", 2);
if (ret < 0)
return ret;
@ -76,19 +128,21 @@ static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
if (ret != EXC3000_LEN_FRAME)
return -EIO;
if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME ||
buf[2] != EXC3000_MT_EVENT)
if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
return -EINVAL;
if (buf[2] != expected_event)
return -EINVAL;
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)
{
int error;
error = exc3000_read_frame(client, buf);
error = exc3000_read_frame(data, buf);
if (error)
return error;
@ -98,7 +152,7 @@ static int exc3000_read_data(struct i2c_client *client,
if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
/* 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)
return error;
@ -110,6 +164,28 @@ static int exc3000_read_data(struct i2c_client *client,
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)
{
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 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) {
/* Schedule a timer to release "stuck" contacts */
mod_timer(&data->timer,
@ -145,31 +227,132 @@ out:
return IRQ_HANDLED;
}
static int exc3000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static ssize_t fw_version_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);
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 input_dev *input;
int error;
int error, max_xy, retry;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
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);
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);
if (!input)
return -ENOMEM;
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_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0);
max_xy = data->info->max_xy;
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);
error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
@ -187,18 +370,49 @@ static int exc3000_probe(struct i2c_client *client,
if (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;
}
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);
#ifdef CONFIG_OF
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);
@ -210,7 +424,7 @@ static struct i2c_driver exc3000_driver = {
.of_match_table = of_match_ptr(exc3000_of_match),
},
.id_table = exc3000_id,
.probe = exc3000_probe,
.probe_new = exc3000_probe,
};
module_i2c_driver(exc3000_driver);

View File

@ -289,7 +289,7 @@ static int iqs5xx_bl_cmd(struct i2c_client *client, u8 bl_cmd, u16 bl_addr)
break;
case IQS5XX_BL_CMD_EXEC:
usleep_range(10000, 10100);
/* fall through */
fallthrough;
default:
return 0;
}

View File

@ -130,7 +130,6 @@ static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
switch (buf[1] & EVENT_TAG_MASK) {
case EVENT_INIT:
/* fall through */
case EVENT_MIDDLE:
input_report_abs(data->input_dev, ABS_X, x);
input_report_abs(data->input_dev, ABS_Y, y);

View File

@ -255,7 +255,7 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
case STMFTS_EV_STATUS:
complete(&sdata->cmd_done);
/* fall through */
fallthrough;
case STMFTS_EV_NO_EVENT:
case STMFTS_EV_DEBUG:

View File

@ -206,7 +206,7 @@ struct rmi_device_platform_data_spi {
*
* @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
* 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
*/
struct rmi_device_platform_data {