linux/drivers/input/mouse/elan_i2c_core.c
jingle.wu d198b8273e Input: elan_i2c - reduce the resume time for controller in Whitebox
Similar to controllers found Voxel, Delbin, Magpie and Bobba, the one found
in Whitebox does not need to be reset after issuing power-on command, and
skipping reset saves resume time.

Signed-off-by: Jingle Wu <jingle.wu@emc.com.tw>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20210907012924.11391-1-jingle.wu@emc.com.tw
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2021-09-06 22:30:34 -07:00

1473 lines
36 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Elan I2C/SMBus Touchpad driver
*
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
* Author: KT Liao <kt.liao@emc.com.tw>
* Version: 1.6.3
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
* copyright (c) 2011-2012 Google, Inc.
*
* Trademarks are the property of their respective owners.
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <asm/unaligned.h>
#include "elan_i2c.h"
#define DRIVER_NAME "elan_i2c"
#define ELAN_VENDOR_ID 0x04f3
#define ETP_MAX_PRESSURE 255
#define ETP_FWIDTH_REDUCE 90
#define ETP_FINGER_WIDTH 15
#define ETP_RETRY_COUNT 3
/* quirks to control the device */
#define ETP_QUIRK_QUICK_WAKEUP BIT(0)
/* The main device structure */
struct elan_tp_data {
struct i2c_client *client;
struct input_dev *input;
struct input_dev *tp_input; /* trackpoint input node */
struct regulator *vcc;
const struct elan_transport_ops *ops;
/* for fw update */
struct completion fw_completion;
bool in_fw_update;
struct mutex sysfs_mutex;
unsigned int max_x;
unsigned int max_y;
unsigned int width_x;
unsigned int width_y;
unsigned int x_res;
unsigned int y_res;
u8 pattern;
u16 product_id;
u8 fw_version;
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_page_size;
u32 fw_signature_address;
bool irq_wake;
u8 min_baseline;
u8 max_baseline;
bool baseline_ready;
u8 clickpad;
bool middle_button;
u32 quirks; /* Various quirks */
};
static u32 elan_i2c_lookup_quirks(u16 ic_type, u16 product_id)
{
static const struct {
u16 ic_type;
u16 product_id;
u32 quirks;
} elan_i2c_quirks[] = {
{ 0x0D, ETP_PRODUCT_ID_DELBIN, ETP_QUIRK_QUICK_WAKEUP },
{ 0x0D, ETP_PRODUCT_ID_WHITEBOX, ETP_QUIRK_QUICK_WAKEUP },
{ 0x10, ETP_PRODUCT_ID_VOXEL, ETP_QUIRK_QUICK_WAKEUP },
{ 0x14, ETP_PRODUCT_ID_MAGPIE, ETP_QUIRK_QUICK_WAKEUP },
{ 0x14, ETP_PRODUCT_ID_BOBBA, ETP_QUIRK_QUICK_WAKEUP },
};
u32 quirks = 0;
int i;
for (i = 0; i < ARRAY_SIZE(elan_i2c_quirks); i++) {
if (elan_i2c_quirks[i].ic_type == ic_type &&
elan_i2c_quirks[i].product_id == product_id) {
quirks = elan_i2c_quirks[i].quirks;
}
}
if (ic_type >= 0x0D && product_id >= 0x123)
quirks |= ETP_QUIRK_QUICK_WAKEUP;
return quirks;
}
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:
case 0x06:
case 0x08:
*validpage_count = 512;
break;
case 0x03:
case 0x07:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
*validpage_count = 768;
break;
case 0x0D:
*validpage_count = 896;
break;
case 0x0E:
*validpage_count = 640;
break;
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;
}
static int elan_enable_power(struct elan_tp_data *data)
{
int repeat = ETP_RETRY_COUNT;
int error;
error = regulator_enable(data->vcc);
if (error) {
dev_err(&data->client->dev,
"failed to enable regulator: %d\n", error);
return error;
}
do {
error = data->ops->power_control(data->client, true);
if (error >= 0)
return 0;
msleep(30);
} while (--repeat > 0);
dev_err(&data->client->dev, "failed to enable power: %d\n", error);
return error;
}
static int elan_disable_power(struct elan_tp_data *data)
{
int repeat = ETP_RETRY_COUNT;
int error;
do {
error = data->ops->power_control(data->client, false);
if (!error) {
error = regulator_disable(data->vcc);
if (error) {
dev_err(&data->client->dev,
"failed to disable regulator: %d\n",
error);
/* Attempt to power the chip back up */
data->ops->power_control(data->client, true);
break;
}
return 0;
}
msleep(30);
} while (--repeat > 0);
dev_err(&data->client->dev, "failed to disable power: %d\n", error);
return error;
}
static int elan_sleep(struct elan_tp_data *data)
{
int repeat = ETP_RETRY_COUNT;
int error;
do {
error = data->ops->sleep_control(data->client, true);
if (!error)
return 0;
msleep(30);
} while (--repeat > 0);
return error;
}
static int elan_query_product(struct elan_tp_data *data)
{
int error;
error = data->ops->get_product_id(data->client, &data->product_id);
if (error)
return error;
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;
return 0;
}
static int elan_check_ASUS_special_fw(struct elan_tp_data *data)
{
if (data->ic_type == 0x0E) {
switch (data->product_id) {
case 0x05 ... 0x07:
case 0x09:
case 0x13:
return true;
}
} else if (data->ic_type == 0x08 && data->product_id == 0x26) {
/* ASUS EeeBook X205TA */
return true;
}
return false;
}
static int __elan_initialize(struct elan_tp_data *data, bool skip_reset)
{
struct i2c_client *client = data->client;
bool woken_up = false;
int error;
if (!skip_reset) {
error = data->ops->initialize(client);
if (error) {
dev_err(&client->dev, "device initialize failed: %d\n", error);
return error;
}
}
error = elan_query_product(data);
if (error)
return error;
/*
* Some ASUS devices were shipped with firmware that requires
* touchpads to be woken up first, before attempting to switch
* them into absolute reporting mode.
*/
if (elan_check_ASUS_special_fw(data)) {
error = data->ops->sleep_control(client, false);
if (error) {
dev_err(&client->dev,
"failed to wake device up: %d\n", error);
return error;
}
msleep(200);
woken_up = true;
}
data->mode |= ETP_ENABLE_ABS;
error = data->ops->set_mode(client, data->mode);
if (error) {
dev_err(&client->dev,
"failed to switch to absolute mode: %d\n", error);
return error;
}
if (!woken_up) {
error = data->ops->sleep_control(client, false);
if (error) {
dev_err(&client->dev,
"failed to wake device up: %d\n", error);
return error;
}
}
return 0;
}
static int elan_initialize(struct elan_tp_data *data, bool skip_reset)
{
int repeat = ETP_RETRY_COUNT;
int error;
do {
error = __elan_initialize(data, skip_reset);
if (!error)
return 0;
skip_reset = false;
msleep(30);
} while (--repeat > 0);
return error;
}
static int elan_query_device_info(struct elan_tp_data *data)
{
int error;
error = data->ops->get_version(data->client, data->pattern, false,
&data->fw_version);
if (error)
return error;
error = data->ops->get_checksum(data->client, false,
&data->fw_checksum);
if (error)
return error;
error = data->ops->get_version(data->client, data->pattern,
true, &data->iap_version);
if (error)
return error;
error = data->ops->get_pressure_adjustment(data->client,
&data->pressure_adjustment);
if (error)
return error;
error = data->ops->get_report_features(data->client, data->pattern,
&data->report_features,
&data->report_len);
if (error)
return error;
data->quirks = elan_i2c_lookup_quirks(data->ic_type, data->product_id);
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",
data->iap_version, data->ic_type);
return 0;
}
static unsigned int elan_convert_resolution(u8 val, u8 pattern)
{
/*
* 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 res * 10 / 254;
}
static int elan_query_device_parameters(struct elan_tp_data *data)
{
struct i2c_client *client = data->client;
unsigned int x_traces, y_traces;
u32 x_mm, y_mm;
u8 hw_x_res, hw_y_res;
int error;
if (device_property_read_u32(&client->dev,
"touchscreen-size-x", &data->max_x) ||
device_property_read_u32(&client->dev,
"touchscreen-size-y", &data->max_y)) {
error = data->ops->get_max(data->client,
&data->max_x,
&data->max_y);
if (error)
return error;
} else {
/* size is the maximum + 1 */
--data->max_x;
--data->max_y;
}
if (device_property_read_u32(&client->dev,
"elan,x_traces",
&x_traces) ||
device_property_read_u32(&client->dev,
"elan,y_traces",
&y_traces)) {
error = data->ops->get_num_traces(data->client,
&x_traces, &y_traces);
if (error)
return error;
}
data->width_x = data->max_x / x_traces;
data->width_y = data->max_y / y_traces;
if (device_property_read_u32(&client->dev,
"touchscreen-x-mm", &x_mm) ||
device_property_read_u32(&client->dev,
"touchscreen-y-mm", &y_mm)) {
error = data->ops->get_resolution(data->client,
&hw_x_res, &hw_y_res);
if (error)
return error;
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;
}
if (device_property_read_bool(&client->dev, "elan,clickpad"))
data->clickpad = 1;
if (device_property_read_bool(&client->dev, "elan,middle-button"))
data->middle_button = true;
return 0;
}
/*
**********************************************************
* IAP firmware updater related routines
**********************************************************
*/
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, page_size,
page, checksum, idx);
if (!error)
return 0;
dev_dbg(&data->client->dev,
"IAP retrying page %d (error: %d)\n", idx, error);
} while (--retry > 0);
return error;
}
static int __elan_update_firmware(struct elan_tp_data *data,
const struct firmware *fw)
{
struct i2c_client *client = data->client;
struct device *dev = &client->dev;
int i, j;
int error;
u16 iap_start_addr;
u16 boot_page_count;
u16 sw_checksum = 0, fw_checksum = 0;
error = data->ops->prepare_fw_update(client, data->ic_type,
data->iap_version,
data->fw_page_size);
if (error)
return error;
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
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 * data->fw_page_size];
for (j = 0; j < data->fw_page_size; j += 2)
checksum += ((page[j + 1] << 8) | page[j]);
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;
}
sw_checksum += checksum;
}
/* Wait WDT reset and power on reset */
msleep(600);
error = data->ops->finish_fw_update(client, &data->fw_completion);
if (error)
return error;
error = data->ops->get_checksum(client, true, &fw_checksum);
if (error)
return error;
if (sw_checksum != fw_checksum) {
dev_err(dev, "checksum diff sw=[%04X], fw=[%04X]\n",
sw_checksum, fw_checksum);
return -EIO;
}
return 0;
}
static int elan_update_firmware(struct elan_tp_data *data,
const struct firmware *fw)
{
struct i2c_client *client = data->client;
int retval;
dev_dbg(&client->dev, "Starting firmware update....\n");
disable_irq(client->irq);
data->in_fw_update = true;
retval = __elan_update_firmware(data, fw);
if (retval) {
dev_err(&client->dev, "firmware update failed: %d\n", retval);
data->ops->iap_reset(client);
} else {
/* Reinitialize TP after fw is updated */
elan_initialize(data, false);
elan_query_device_info(data);
}
data->in_fw_update = false;
enable_irq(client->irq);
return retval;
}
/*
*******************************************************************
* SYSFS attributes
*******************************************************************
*/
static ssize_t elan_sysfs_read_fw_checksum(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
return sprintf(buf, "0x%04x\n", data->fw_checksum);
}
static ssize_t elan_sysfs_read_product_id(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n",
data->product_id);
}
static ssize_t elan_sysfs_read_fw_ver(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%d.0\n", data->fw_version);
}
static ssize_t elan_sysfs_read_sm_ver(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%d.0\n", data->sm_version);
}
static ssize_t elan_sysfs_read_iap_ver(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%d.0\n", data->iap_version);
}
static ssize_t elan_sysfs_update_fw(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct elan_tp_data *data = dev_get_drvdata(dev);
const struct firmware *fw;
char *fw_name;
int error;
const u8 *fw_signature;
static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
if (data->fw_validpage_count == 0)
return -EINVAL;
/* Look for a firmware with the product id appended. */
fw_name = kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id);
if (!fw_name) {
dev_err(dev, "failed to allocate memory for firmware name\n");
return -ENOMEM;
}
dev_info(dev, "requesting fw '%s'\n", fw_name);
error = request_firmware(&fw, fw_name, dev);
kfree(fw_name);
if (error) {
dev_err(dev, "failed to request firmware: %d\n", error);
return error;
}
/* Firmware file must match signature data */
fw_signature = &fw->data[data->fw_signature_address];
if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n",
(int)sizeof(signature), signature,
(int)sizeof(signature), fw_signature);
error = -EBADF;
goto out_release_fw;
}
error = mutex_lock_interruptible(&data->sysfs_mutex);
if (error)
goto out_release_fw;
error = elan_update_firmware(data, fw);
mutex_unlock(&data->sysfs_mutex);
out_release_fw:
release_firmware(fw);
return error ?: count;
}
static ssize_t calibrate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int tries = 20;
int retval;
int error;
u8 val[ETP_CALIBRATE_MAX_LEN];
retval = mutex_lock_interruptible(&data->sysfs_mutex);
if (retval)
return retval;
disable_irq(client->irq);
data->mode |= ETP_ENABLE_CALIBRATE;
retval = data->ops->set_mode(client, data->mode);
if (retval) {
dev_err(dev, "failed to enable calibration mode: %d\n",
retval);
goto out;
}
retval = data->ops->calibrate(client);
if (retval) {
dev_err(dev, "failed to start calibration: %d\n",
retval);
goto out_disable_calibrate;
}
val[0] = 0xff;
do {
/* Wait 250ms before checking if calibration has completed. */
msleep(250);
retval = data->ops->calibrate_result(client, val);
if (retval)
dev_err(dev, "failed to check calibration result: %d\n",
retval);
else if (val[0] == 0)
break; /* calibration done */
} while (--tries);
if (tries == 0) {
dev_err(dev, "failed to calibrate. Timeout.\n");
retval = -ETIMEDOUT;
}
out_disable_calibrate:
data->mode &= ~ETP_ENABLE_CALIBRATE;
error = data->ops->set_mode(data->client, data->mode);
if (error) {
dev_err(dev, "failed to disable calibration mode: %d\n",
error);
if (!retval)
retval = error;
}
out:
enable_irq(client->irq);
mutex_unlock(&data->sysfs_mutex);
return retval ?: count;
}
static ssize_t elan_sysfs_read_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int error;
enum tp_mode mode;
error = mutex_lock_interruptible(&data->sysfs_mutex);
if (error)
return error;
error = data->ops->iap_get_mode(data->client, &mode);
mutex_unlock(&data->sysfs_mutex);
if (error)
return error;
return sprintf(buf, "%d\n", (int)mode);
}
static DEVICE_ATTR(product_id, S_IRUGO, elan_sysfs_read_product_id, NULL);
static DEVICE_ATTR(firmware_version, S_IRUGO, elan_sysfs_read_fw_ver, NULL);
static DEVICE_ATTR(sample_version, S_IRUGO, elan_sysfs_read_sm_ver, NULL);
static DEVICE_ATTR(iap_version, S_IRUGO, elan_sysfs_read_iap_ver, NULL);
static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_sysfs_read_fw_checksum, NULL);
static DEVICE_ATTR(mode, S_IRUGO, elan_sysfs_read_mode, NULL);
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_sysfs_update_fw);
static DEVICE_ATTR_WO(calibrate);
static struct attribute *elan_sysfs_entries[] = {
&dev_attr_product_id.attr,
&dev_attr_firmware_version.attr,
&dev_attr_sample_version.attr,
&dev_attr_iap_version.attr,
&dev_attr_fw_checksum.attr,
&dev_attr_calibrate.attr,
&dev_attr_mode.attr,
&dev_attr_update_fw.attr,
NULL,
};
static const struct attribute_group elan_sysfs_group = {
.attrs = elan_sysfs_entries,
};
static ssize_t acquire_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int error;
int retval;
retval = mutex_lock_interruptible(&data->sysfs_mutex);
if (retval)
return retval;
disable_irq(client->irq);
data->baseline_ready = false;
data->mode |= ETP_ENABLE_CALIBRATE;
retval = data->ops->set_mode(data->client, data->mode);
if (retval) {
dev_err(dev, "Failed to enable calibration mode to get baseline: %d\n",
retval);
goto out;
}
msleep(250);
retval = data->ops->get_baseline_data(data->client, true,
&data->max_baseline);
if (retval) {
dev_err(dev, "Failed to read max baseline form device: %d\n",
retval);
goto out_disable_calibrate;
}
retval = data->ops->get_baseline_data(data->client, false,
&data->min_baseline);
if (retval) {
dev_err(dev, "Failed to read min baseline form device: %d\n",
retval);
goto out_disable_calibrate;
}
data->baseline_ready = true;
out_disable_calibrate:
data->mode &= ~ETP_ENABLE_CALIBRATE;
error = data->ops->set_mode(data->client, data->mode);
if (error) {
dev_err(dev, "Failed to disable calibration mode after acquiring baseline: %d\n",
error);
if (!retval)
retval = error;
}
out:
enable_irq(client->irq);
mutex_unlock(&data->sysfs_mutex);
return retval ?: count;
}
static ssize_t min_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int retval;
retval = mutex_lock_interruptible(&data->sysfs_mutex);
if (retval)
return retval;
if (!data->baseline_ready) {
retval = -ENODATA;
goto out;
}
retval = snprintf(buf, PAGE_SIZE, "%d", data->min_baseline);
out:
mutex_unlock(&data->sysfs_mutex);
return retval;
}
static ssize_t max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int retval;
retval = mutex_lock_interruptible(&data->sysfs_mutex);
if (retval)
return retval;
if (!data->baseline_ready) {
retval = -ENODATA;
goto out;
}
retval = snprintf(buf, PAGE_SIZE, "%d", data->max_baseline);
out:
mutex_unlock(&data->sysfs_mutex);
return retval;
}
static DEVICE_ATTR_WO(acquire);
static DEVICE_ATTR_RO(min);
static DEVICE_ATTR_RO(max);
static struct attribute *elan_baseline_sysfs_entries[] = {
&dev_attr_acquire.attr,
&dev_attr_min.attr,
&dev_attr_max.attr,
NULL,
};
static const struct attribute_group elan_baseline_sysfs_group = {
.name = "baseline",
.attrs = elan_baseline_sysfs_entries,
};
static const struct attribute_group *elan_sysfs_groups[] = {
&elan_sysfs_group,
&elan_baseline_sysfs_group,
NULL
};
/*
******************************************************************
* Elan isr functions
******************************************************************
*/
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, scaled_pressure;
if (contact_valid) {
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,
"[%d] x=%d y=%d over max (%d, %d)",
contact_num, pos_x, pos_y,
data->max_x, data->max_y);
return;
}
pressure = finger_data[4];
scaled_pressure = pressure + data->pressure_adjustment;
if (scaled_pressure > ETP_MAX_PRESSURE)
scaled_pressure = ETP_MAX_PRESSURE;
input_mt_slot(input, contact_num);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_POSITION_X, pos_x);
input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y);
input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure);
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,
bool high_precision)
{
struct input_dev *input = data->input;
u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
int i;
u8 tp_info = packet[ETP_TOUCH_INFO_OFFSET];
u8 hover_info = packet[ETP_HOVER_INFO_OFFSET];
bool contact_valid, hover_event;
pm_wakeup_event(&data->client->dev, 0);
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;
}
input_report_key(input, BTN_LEFT, tp_info & BIT(0));
input_report_key(input, BTN_MIDDLE, tp_info & BIT(2));
input_report_key(input, BTN_RIGHT, tp_info & BIT(1));
input_report_abs(input, ABS_DISTANCE, hover_event != 0);
input_mt_report_pointer_emulation(input, true);
input_sync(input);
}
static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
{
struct input_dev *input = data->tp_input;
u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
int x, y;
pm_wakeup_event(&data->client->dev, 0);
if (!data->tp_input) {
dev_warn_once(&data->client->dev,
"received a trackpoint report while no trackpoint device has been created. Please report upstream.\n");
return;
}
input_report_key(input, BTN_LEFT, packet[0] & 0x01);
input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
if ((packet[3] & 0x0F) == 0x06) {
x = packet[4] - (int)((packet[1] ^ 0x80) << 1);
y = (int)((packet[2] ^ 0x80) << 1) - packet[5];
input_report_rel(input, REL_X, x);
input_report_rel(input, REL_Y, y);
}
input_sync(input);
}
static irqreturn_t elan_isr(int irq, void *dev_id)
{
struct elan_tp_data *data = dev_id;
int error;
u8 report[ETP_MAX_REPORT_LEN];
/*
* When device is connected to i2c bus, when all IAP page writes
* complete, the driver will receive interrupt and must read
* 0000 to confirm that IAP is finished.
*/
if (data->in_fw_update) {
complete(&data->fw_completion);
goto out;
}
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, false);
break;
case ETP_REPORT_ID2:
elan_report_absolute(data, report, true);
break;
case ETP_TP_REPORT_ID:
case ETP_TP_REPORT_ID2:
elan_report_trackpoint(data, report);
break;
default:
dev_err(&data->client->dev, "invalid report id data (%x)\n",
report[ETP_REPORT_ID_OFFSET]);
}
out:
return IRQ_HANDLED;
}
/*
******************************************************************
* Elan initialization functions
******************************************************************
*/
static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
{
struct device *dev = &data->client->dev;
struct input_dev *input;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
input->name = "Elan TrackPoint";
input->id.bustype = BUS_I2C;
input->id.vendor = ELAN_VENDOR_ID;
input->id.product = data->product_id;
input_set_drvdata(input, data);
input_set_capability(input, EV_REL, REL_X);
input_set_capability(input, EV_REL, REL_Y);
input_set_capability(input, EV_KEY, BTN_LEFT);
input_set_capability(input, EV_KEY, BTN_RIGHT);
input_set_capability(input, EV_KEY, BTN_MIDDLE);
__set_bit(INPUT_PROP_POINTER, input->propbit);
__set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
data->tp_input = input;
return 0;
}
static int elan_setup_input_device(struct elan_tp_data *data)
{
struct device *dev = &data->client->dev;
struct input_dev *input;
unsigned int max_width = max(data->width_x, data->width_y);
unsigned int min_width = min(data->width_x, data->width_y);
int error;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
input->name = "Elan Touchpad";
input->id.bustype = BUS_I2C;
input->id.vendor = ELAN_VENDOR_ID;
input->id.product = data->product_id;
input_set_drvdata(input, data);
error = input_mt_init_slots(input, ETP_MAX_FINGERS,
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(dev, "failed to initialize MT slots: %d\n", error);
return error;
}
__set_bit(EV_ABS, input->evbit);
__set_bit(INPUT_PROP_POINTER, input->propbit);
if (data->clickpad) {
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
} else {
__set_bit(BTN_RIGHT, input->keybit);
if (data->middle_button)
__set_bit(BTN_MIDDLE, input->keybit);
}
__set_bit(BTN_LEFT, input->keybit);
/* Set up ST parameters */
input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
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);
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 */
input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
input_abs_set_res(input, ABS_MT_POSITION_X, data->x_res);
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);
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;
return 0;
}
static void elan_disable_regulator(void *_data)
{
struct elan_tp_data *data = _data;
regulator_disable(data->vcc);
}
static int elan_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
const struct elan_transport_ops *transport_ops;
struct device *dev = &client->dev;
struct elan_tp_data *data;
unsigned long irqflags;
int error;
if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_I2C) &&
i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
transport_ops = &elan_i2c_ops;
} else if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) &&
i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK)) {
transport_ops = &elan_smbus_ops;
} else {
dev_err(dev, "not a supported I2C/SMBus adapter\n");
return -EIO;
}
data = devm_kzalloc(dev, sizeof(struct elan_tp_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
data->ops = transport_ops;
data->client = client;
init_completion(&data->fw_completion);
mutex_init(&data->sysfs_mutex);
data->vcc = devm_regulator_get(dev, "vcc");
if (IS_ERR(data->vcc)) {
error = PTR_ERR(data->vcc);
if (error != -EPROBE_DEFER)
dev_err(dev, "Failed to get 'vcc' regulator: %d\n",
error);
return error;
}
error = regulator_enable(data->vcc);
if (error) {
dev_err(dev, "Failed to enable regulator: %d\n", error);
return error;
}
error = devm_add_action_or_reset(dev, elan_disable_regulator, data);
if (error) {
dev_err(dev, "Failed to add disable regulator action: %d\n",
error);
return error;
}
/* Make sure there is something at this address */
error = i2c_smbus_read_byte(client);
if (error < 0) {
dev_dbg(&client->dev, "nothing at this address: %d\n", error);
return -ENXIO;
}
/* Initialize the touchpad. */
error = elan_initialize(data, false);
if (error)
return error;
error = elan_query_device_info(data);
if (error)
return error;
error = elan_query_device_parameters(data);
if (error)
return error;
dev_info(dev,
"Elan Touchpad: Module ID: 0x%04x, Firmware: 0x%04x, Sample: 0x%04x, IAP: 0x%04x\n",
data->product_id,
data->fw_version,
data->sm_version,
data->iap_version);
dev_dbg(dev,
"Elan Touchpad Extra Information:\n"
" Max ABS X,Y: %d,%d\n"
" Width X,Y: %d,%d\n"
" Resolution X,Y: %d,%d (dots/mm)\n"
" ic type: 0x%x\n"
" info pattern: 0x%x\n",
data->max_x, data->max_y,
data->width_x, data->width_y,
data->x_res, data->y_res,
data->ic_type, data->pattern);
/* Set up input device properties based on queried parameters. */
error = elan_setup_input_device(data);
if (error)
return error;
if (device_property_read_bool(&client->dev, "elan,trackpoint")) {
error = elan_setup_trackpoint_input_device(data);
if (error)
return error;
}
/*
* Platform code (ACPI, DTS) should normally set up interrupt
* for us, but in case it did not let's fall back to using falling
* edge to be compatible with older Chromebooks.
*/
irqflags = irq_get_trigger_type(client->irq);
if (!irqflags)
irqflags = IRQF_TRIGGER_FALLING;
error = devm_request_threaded_irq(dev, client->irq, NULL, elan_isr,
irqflags | IRQF_ONESHOT,
client->name, data);
if (error) {
dev_err(dev, "cannot register irq=%d\n", client->irq);
return error;
}
error = devm_device_add_groups(dev, elan_sysfs_groups);
if (error) {
dev_err(dev, "failed to create sysfs attributes: %d\n", error);
return error;
}
error = input_register_device(data->input);
if (error) {
dev_err(dev, "failed to register input device: %d\n", error);
return error;
}
if (data->tp_input) {
error = input_register_device(data->tp_input);
if (error) {
dev_err(&client->dev,
"failed to register TrackPoint input device: %d\n",
error);
return error;
}
}
/*
* Systems using device tree should set up wakeup via DTS,
* the rest will configure device as wakeup source by default.
*/
if (!dev->of_node)
device_init_wakeup(dev, true);
return 0;
}
static int __maybe_unused elan_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int ret;
/*
* We are taking the mutex to make sure sysfs operations are
* complete before we attempt to bring the device into low[er]
* power mode.
*/
ret = mutex_lock_interruptible(&data->sysfs_mutex);
if (ret)
return ret;
disable_irq(client->irq);
if (device_may_wakeup(dev)) {
ret = elan_sleep(data);
/* Enable wake from IRQ */
data->irq_wake = (enable_irq_wake(client->irq) == 0);
} else {
ret = elan_disable_power(data);
}
mutex_unlock(&data->sysfs_mutex);
return ret;
}
static int __maybe_unused elan_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
int error;
if (device_may_wakeup(dev) && data->irq_wake) {
disable_irq_wake(client->irq);
data->irq_wake = false;
}
error = elan_enable_power(data);
if (error) {
dev_err(dev, "power up when resuming failed: %d\n", error);
goto err;
}
error = elan_initialize(data, data->quirks & ETP_QUIRK_QUICK_WAKEUP);
if (error)
dev_err(dev, "initialize when resuming failed: %d\n", error);
err:
enable_irq(data->client->irq);
return error;
}
static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
static const struct i2c_device_id elan_id[] = {
{ DRIVER_NAME, 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, elan_id);
#ifdef CONFIG_ACPI
#include <linux/input/elan-i2c-ids.h>
MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
#endif
#ifdef CONFIG_OF
static const struct of_device_id elan_of_match[] = {
{ .compatible = "elan,ekth3000" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, elan_of_match);
#endif
static struct i2c_driver elan_driver = {
.driver = {
.name = DRIVER_NAME,
.pm = &elan_pm_ops,
.acpi_match_table = ACPI_PTR(elan_acpi_id),
.of_match_table = of_match_ptr(elan_of_match),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = elan_probe,
.id_table = elan_id,
};
module_i2c_driver(elan_driver);
MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver");
MODULE_LICENSE("GPL");