diff options
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 13 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 352 | ||||
-rw-r--r-- | drivers/input/touchscreen/chipone_icn8505.c | 520 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix.c | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/mk712.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/ti_am335x_tsc.c | 7 | ||||
-rw-r--r-- | drivers/input/touchscreen/usbtouchscreen.c | 94 |
8 files changed, 770 insertions, 220 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4f15496fec8b..32267c1afebc 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -164,6 +164,17 @@ config TOUCHSCREEN_CHIPONE_ICN8318 To compile this driver as a module, choose M here: the module will be called chipone_icn8318. +config TOUCHSCREEN_CHIPONE_ICN8505 + tristate "chipone icn8505 touchscreen controller" + depends on I2C && ACPI + help + Say Y here if you have a ChipOne icn8505 based I2C touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called chipone_icn8505. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C @@ -362,7 +373,7 @@ config TOUCHSCREEN_HIDEEP If unsure, say N. - To compile this driver as a moudle, choose M here : the + To compile this driver as a module, choose M here : the module will be called hideep_ts. config TOUCHSCREEN_ILI210X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index dddae7973436..fd4fd32fb73f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o +obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 5d9699fe1b55..54fe190fd4bc 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -194,6 +194,8 @@ enum t100_type { /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_GPIO_TIME 20 /* msec */ +#define MXT_RESET_INVALID_CHG 100 /* msec */ #define MXT_RESET_TIME 200 /* msec */ #define MXT_RESET_TIMEOUT 3000 /* msec */ #define MXT_CRC_TIMEOUT 1000 /* msec */ @@ -280,7 +282,8 @@ struct mxt_data { struct input_dev *input_dev; char phys[64]; /* device physical location */ struct mxt_object *object_table; - struct mxt_info info; + struct mxt_info *info; + void *raw_info_block; unsigned int irq; unsigned int max_x; unsigned int max_y; @@ -460,12 +463,13 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) { u8 appmode = data->client->addr; u8 bootloader; + u8 family_id = data->info ? data->info->family_id : 0; switch (appmode) { case 0x4a: case 0x4b: /* Chips after 1664S use different scheme */ - if (retry || data->info.family_id >= 0xa2) { + if (retry || family_id >= 0xa2) { bootloader = appmode - 0x24; break; } @@ -692,7 +696,7 @@ mxt_get_object(struct mxt_data *data, u8 type) struct mxt_object *object; int i; - for (i = 0; i < data->info.object_num; i++) { + for (i = 0; i < data->info->object_num; i++) { object = data->object_table + i; if (object->type == type) return object; @@ -1206,7 +1210,7 @@ static int mxt_soft_reset(struct mxt_data *data) return ret; /* Ignore CHG line for 100ms after reset */ - msleep(100); + msleep(MXT_RESET_INVALID_CHG); mxt_acquire_irq(data); @@ -1462,12 +1466,12 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) data_pos += offset; } - if (cfg_info.family_id != data->info.family_id) { + if (cfg_info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); return -EINVAL; } - if (cfg_info.variant_id != data->info.variant_id) { + if (cfg_info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); return -EINVAL; } @@ -1512,7 +1516,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) /* Malloc memory to store configuration */ cfg_start_ofs = MXT_OBJECT_START + - data->info.object_num * sizeof(struct mxt_object) + + data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; config_mem_size = data->mem_size - cfg_start_ofs; config_mem = kzalloc(config_mem_size, GFP_KERNEL); @@ -1563,20 +1567,6 @@ release_mem: return ret; } -static int mxt_get_info(struct mxt_data *data) -{ - struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; - int error; - - /* Read 7-byte info block starting at address 0 */ - error = __mxt_read_reg(client, 0, sizeof(*info), info); - if (error) - return error; - - return 0; -} - static void mxt_free_input_device(struct mxt_data *data) { if (data->input_dev) { @@ -1591,9 +1581,10 @@ static void mxt_free_object_table(struct mxt_data *data) video_unregister_device(&data->dbg.vdev); v4l2_device_unregister(&data->dbg.v4l2); #endif - - kfree(data->object_table); data->object_table = NULL; + data->info = NULL; + kfree(data->raw_info_block); + data->raw_info_block = NULL; kfree(data->msg_buf); data->msg_buf = NULL; data->T5_address = 0; @@ -1609,34 +1600,18 @@ static void mxt_free_object_table(struct mxt_data *data) data->max_reportid = 0; } -static int mxt_get_object_table(struct mxt_data *data) +static int mxt_parse_object_table(struct mxt_data *data, + struct mxt_object *object_table) { struct i2c_client *client = data->client; - size_t table_size; - struct mxt_object *object_table; - int error; int i; u8 reportid; u16 end_address; - table_size = data->info.object_num * sizeof(struct mxt_object); - object_table = kzalloc(table_size, GFP_KERNEL); - if (!object_table) { - dev_err(&data->client->dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, - object_table); - if (error) { - kfree(object_table); - return error; - } - /* Valid Report IDs start counting from 1 */ reportid = 1; data->mem_size = 0; - for (i = 0; i < data->info.object_num; i++) { + for (i = 0; i < data->info->object_num; i++) { struct mxt_object *object = object_table + i; u8 min_id, max_id; @@ -1660,8 +1635,8 @@ static int mxt_get_object_table(struct mxt_data *data) switch (object->type) { case MXT_GEN_MESSAGE_T5: - if (data->info.family_id == 0x80 && - data->info.version < 0x20) { + if (data->info->family_id == 0x80 && + data->info->version < 0x20) { /* * On mXT224 firmware versions prior to V2.0 * read and discard unused CRC byte otherwise @@ -1716,24 +1691,102 @@ static int mxt_get_object_table(struct mxt_data *data) /* If T44 exists, T5 position has to be directly after */ if (data->T44_address && (data->T5_address != data->T44_address + 1)) { dev_err(&client->dev, "Invalid T44 position\n"); - error = -EINVAL; - goto free_object_table; + return -EINVAL; } data->msg_buf = kcalloc(data->max_reportid, data->T5_msg_size, GFP_KERNEL); - if (!data->msg_buf) { - dev_err(&client->dev, "Failed to allocate message buffer\n"); + if (!data->msg_buf) + return -ENOMEM; + + return 0; +} + +static int mxt_read_info_block(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + size_t size; + void *id_buf, *buf; + uint8_t num_objects; + u32 calculated_crc; + u8 *crc_ptr; + + /* If info block already allocated, free it */ + if (data->raw_info_block) + mxt_free_object_table(data); + + /* Read 7-byte ID information block starting at address 0 */ + size = sizeof(struct mxt_info); + id_buf = kzalloc(size, GFP_KERNEL); + if (!id_buf) + return -ENOMEM; + + error = __mxt_read_reg(client, 0, size, id_buf); + if (error) + goto err_free_mem; + + /* Resize buffer to give space for rest of info block */ + num_objects = ((struct mxt_info *)id_buf)->object_num; + size += (num_objects * sizeof(struct mxt_object)) + + MXT_INFO_CHECKSUM_SIZE; + + buf = krealloc(id_buf, size, GFP_KERNEL); + if (!buf) { error = -ENOMEM; - goto free_object_table; + goto err_free_mem; + } + id_buf = buf; + + /* Read rest of info block */ + error = __mxt_read_reg(client, MXT_OBJECT_START, + size - MXT_OBJECT_START, + id_buf + MXT_OBJECT_START); + if (error) + goto err_free_mem; + + /* Extract & calculate checksum */ + crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE; + data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); + + calculated_crc = mxt_calculate_crc(id_buf, 0, + size - MXT_INFO_CHECKSUM_SIZE); + + /* + * CRC mismatch can be caused by data corruption due to I2C comms + * issue or else device is not using Object Based Protocol (eg i2c-hid) + */ + if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { + dev_err(&client->dev, + "Info Block CRC error calculated=0x%06X read=0x%06X\n", + calculated_crc, data->info_crc); + error = -EIO; + goto err_free_mem; + } + + data->raw_info_block = id_buf; + data->info = (struct mxt_info *)id_buf; + + dev_info(&client->dev, + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", + data->info->family_id, data->info->variant_id, + data->info->version >> 4, data->info->version & 0xf, + data->info->build, data->info->object_num); + + /* Parse object table information */ + error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START); + if (error) { + dev_err(&client->dev, "Error %d parsing object table\n", error); + mxt_free_object_table(data); + goto err_free_mem; } - data->object_table = object_table; + data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START); return 0; -free_object_table: - mxt_free_object_table(data); +err_free_mem: + kfree(id_buf); return error; } @@ -2046,7 +2099,7 @@ static int mxt_initialize(struct mxt_data *data) int error; while (1) { - error = mxt_get_info(data); + error = mxt_read_info_block(data); if (!error) break; @@ -2077,16 +2130,9 @@ static int mxt_initialize(struct mxt_data *data) msleep(MXT_FW_RESET_TIME); } - /* Get object table information */ - error = mxt_get_object_table(data); - if (error) { - dev_err(&client->dev, "Error %d reading object table\n", error); - return error; - } - error = mxt_acquire_irq(data); if (error) - goto err_free_object_table; + return error; error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, &client->dev, GFP_KERNEL, data, @@ -2094,14 +2140,10 @@ static int mxt_initialize(struct mxt_data *data) if (error) { dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", error); - goto err_free_object_table; + return error; } return 0; - -err_free_object_table: - mxt_free_object_table(data); - return error; } static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) @@ -2162,7 +2204,7 @@ recheck: static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, unsigned int y) { - struct mxt_info *info = &data->info; + struct mxt_info *info = data->info; struct mxt_dbg *dbg = &data->dbg; unsigned int ofs, page; unsigned int col = 0; @@ -2490,7 +2532,7 @@ static const struct video_device mxt_video_device = { static void mxt_debug_init(struct mxt_data *data) { - struct mxt_info *info = &data->info; + struct mxt_info *info = data->info; struct mxt_dbg *dbg = &data->dbg; struct mxt_object *object; int error; @@ -2576,7 +2618,6 @@ static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { struct device *dev = &data->client->dev; - struct mxt_info *info = &data->info; int error; error = mxt_init_t7_power_cfg(data); @@ -2601,11 +2642,6 @@ static int mxt_configure_objects(struct mxt_data *data, mxt_debug_init(data); - dev_info(dev, - "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", - info->family_id, info->variant_id, info->version >> 4, - info->version & 0xf, info->build, info->object_num); - return 0; } @@ -2614,7 +2650,7 @@ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_info *info = &data->info; + struct mxt_info *info = data->info; return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", info->version >> 4, info->version & 0xf, info->build); } @@ -2624,7 +2660,7 @@ static ssize_t mxt_hw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_info *info = &data->info; + struct mxt_info *info = data->info; return scnprintf(buf, PAGE_SIZE, "%u.%u\n", info->family_id, info->variant_id); } @@ -2663,7 +2699,7 @@ static ssize_t mxt_object_show(struct device *dev, return -ENOMEM; error = 0; - for (i = 0; i < data->info.object_num; i++) { + for (i = 0; i < data->info->object_num; i++) { object = data->object_table + i; if (!mxt_object_readable(object->type)) @@ -2965,133 +3001,6 @@ static int mxt_parse_device_properties(struct mxt_data *data) return 0; } -#ifdef CONFIG_ACPI - -struct mxt_acpi_platform_data { - const char *hid; - const struct property_entry *props; -}; - -static unsigned int samus_touchpad_buttons[] = { - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT -}; - -static const struct property_entry samus_touchpad_props[] = { - PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons), - { } -}; - -static struct mxt_acpi_platform_data samus_platform_data[] = { - { - /* Touchpad */ - .hid = "ATML0000", - .props = samus_touchpad_props, - }, - { - /* Touchscreen */ - .hid = "ATML0001", - }, - { } -}; - -static unsigned int chromebook_tp_buttons[] = { - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT -}; - -static const struct property_entry chromebook_tp_props[] = { - PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_tp_buttons), - { } -}; - -static struct mxt_acpi_platform_data chromebook_platform_data[] = { - { - /* Touchpad */ - .hid = "ATML0000", - .props = chromebook_tp_props, - }, - { - /* Touchscreen */ - .hid = "ATML0001", - }, - { } -}; - -static const struct dmi_system_id mxt_dmi_table[] = { - { - /* 2015 Google Pixel */ - .ident = "Chromebook Pixel 2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), - }, - .driver_data = samus_platform_data, - }, - { - /* Other Google Chromebooks */ - .ident = "Chromebook", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), - }, - .driver_data = chromebook_platform_data, - }, - { } -}; - -static int mxt_prepare_acpi_properties(struct i2c_client *client) -{ - struct acpi_device *adev; - const struct dmi_system_id *system_id; - const struct mxt_acpi_platform_data *acpi_pdata; - - adev = ACPI_COMPANION(&client->dev); - if (!adev) - return -ENOENT; - - system_id = dmi_first_match(mxt_dmi_table); - if (!system_id) - return -ENOENT; - - acpi_pdata = system_id->driver_data; - if (!acpi_pdata) - return -ENOENT; - - while (acpi_pdata->hid) { - if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid)) { - /* - * Remove previously installed properties if we - * are probing this device not for the very first - * time. - */ - device_remove_properties(&client->dev); - - /* - * Now install the platform-specific properties - * that are missing from ACPI. - */ - device_add_properties(&client->dev, acpi_pdata->props); - break; - } - - acpi_pdata++; - } - - return 0; -} -#else -static int mxt_prepare_acpi_properties(struct i2c_client *client) -{ - return -ENOENT; -} -#endif - static const struct dmi_system_id chromebook_T9_suspend_dmi[] = { { .matches = { @@ -3113,6 +3022,18 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) int error; /* + * Ignore devices that do not have device properties attached to + * them, as we need help determining whether we are dealing with + * touch screen or touchpad. + * + * So far on x86 the only users of Atmel touch controllers are + * Chromebooks, and chromeos_laptop driver will ensure that + * necessary properties are provided (if firmware does not do that). + */ + if (!device_property_present(&client->dev, "compatible")) + return -ENXIO; + + /* * Ignore ACPI devices representing bootloader mode. * * This is a bit of a hack: Google Chromebook BIOS creates ACPI @@ -3143,10 +3064,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ? MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP; - error = mxt_prepare_acpi_properties(client); - if (error && error != -ENOENT) - return error; - error = mxt_parse_device_properties(data); if (error) return error; @@ -3167,20 +3084,14 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) return error; } + disable_irq(client->irq); + if (data->reset_gpio) { - data->in_bootloader = true; - msleep(MXT_RESET_TIME); - reinit_completion(&data->bl_completion); + msleep(MXT_RESET_GPIO_TIME); gpiod_set_value(data->reset_gpio, 1); - error = mxt_wait_for_completion(data, &data->bl_completion, - MXT_RESET_TIMEOUT); - if (error) - return error; - data->in_bootloader = false; + msleep(MXT_RESET_INVALID_CHG); } - disable_irq(client->irq); - error = mxt_initialize(data); if (error) return error; @@ -3254,6 +3165,11 @@ static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); static const struct of_device_id mxt_of_match[] = { { .compatible = "atmel,maxtouch", }, + /* Compatibles listed below are deprecated */ + { .compatible = "atmel,qt602240_ts", }, + { .compatible = "atmel,atmel_mxt_ts", }, + { .compatible = "atmel,atmel_mxt_tp", }, + { .compatible = "atmel,mXT224", }, {}, }; MODULE_DEVICE_TABLE(of, mxt_of_match); diff --git a/drivers/input/touchscreen/chipone_icn8505.c b/drivers/input/touchscreen/chipone_icn8505.c new file mode 100644 index 000000000000..c768186ce856 --- /dev/null +++ b/drivers/input/touchscreen/chipone_icn8505.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for ChipOne icn8505 i2c touchscreen controller + * + * Copyright (c) 2015-2018 Red Hat Inc. + * + * Red Hat authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/module.h> + +/* Normal operation mode defines */ +#define ICN8505_REG_ADDR_WIDTH 16 + +#define ICN8505_REG_POWER 0x0004 +#define ICN8505_REG_TOUCHDATA 0x1000 +#define ICN8505_REG_CONFIGDATA 0x8000 + +/* ICN8505_REG_POWER commands */ +#define ICN8505_POWER_ACTIVE 0x00 +#define ICN8505_POWER_MONITOR 0x01 +#define ICN8505_POWER_HIBERNATE 0x02 +/* + * The Android driver uses these to turn on/off the charger filter, but the + * filter is way too aggressive making e.g. onscreen keyboards unusable. + */ +#define ICN8505_POWER_ENA_CHARGER_MODE 0x55 +#define ICN8505_POWER_DIS_CHARGER_MODE 0x66 + +#define ICN8505_MAX_TOUCHES 10 + +/* Programming mode defines */ +#define ICN8505_PROG_I2C_ADDR 0x30 +#define ICN8505_PROG_REG_ADDR_WIDTH 24 + +#define MAX_FW_UPLOAD_TRIES 3 + +struct icn8505_touch { + u8 slot; + u8 x[2]; + u8 y[2]; + u8 pressure; /* Seems more like finger width then pressure really */ + u8 event; +/* The difference between 2 and 3 is unclear */ +#define ICN8505_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */ +#define ICN8505_EVENT_UPDATE1 2 /* New or updated coordinates */ +#define ICN8505_EVENT_UPDATE2 3 /* New or updated coordinates */ +#define ICN8505_EVENT_END 4 /* Finger lifted */ +} __packed; + +struct icn8505_touch_data { + u8 softbutton; + u8 touch_count; + struct icn8505_touch touches[ICN8505_MAX_TOUCHES]; +} __packed; + +struct icn8505_data { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *wake_gpio; + struct touchscreen_properties prop; + char firmware_name[32]; +}; + +static int icn8505_read_xfer(struct i2c_client *client, u16 i2c_addr, + int reg_addr, int reg_addr_width, + void *data, int len, bool silent) +{ + u8 buf[3]; + int i, ret; + struct i2c_msg msg[2] = { + { + .addr = i2c_addr, + .buf = buf, + .len = reg_addr_width / 8, + }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = data, + .len = len, + } + }; + + for (i = 0; i < (reg_addr_width / 8); i++) + buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != ARRAY_SIZE(msg)) { + if (ret >= 0) + ret = -EIO; + if (!silent) + dev_err(&client->dev, + "Error reading addr %#x reg %#x: %d\n", + i2c_addr, reg_addr, ret); + return ret; + } + + return 0; +} + +static int icn8505_write_xfer(struct i2c_client *client, u16 i2c_addr, + int reg_addr, int reg_addr_width, + const void *data, int len, bool silent) +{ + u8 buf[3 + 32]; /* 3 bytes for 24 bit reg-addr + 32 bytes max len */ + int i, ret; + struct i2c_msg msg = { + .addr = i2c_addr, + .buf = buf, + .len = reg_addr_width / 8 + len, + }; + + if (WARN_ON(len > 32)) + return -EINVAL; + + for (i = 0; i < (reg_addr_width / 8); i++) + buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff; + + memcpy(buf + reg_addr_width / 8, data, len); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + if (!silent) + dev_err(&client->dev, + "Error writing addr %#x reg %#x: %d\n", + i2c_addr, reg_addr, ret); + return ret; + } + + return 0; +} + +static int icn8505_read_data(struct icn8505_data *icn8505, int reg, + void *buf, int len) +{ + return icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg, + ICN8505_REG_ADDR_WIDTH, buf, len, false); +} + +static int icn8505_read_reg_silent(struct icn8505_data *icn8505, int reg) +{ + u8 buf; + int error; + + error = icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg, + ICN8505_REG_ADDR_WIDTH, &buf, 1, true); + if (error) + return error; + + return buf; +} + +static int icn8505_write_reg(struct icn8505_data *icn8505, int reg, u8 val) +{ + return icn8505_write_xfer(icn8505->client, icn8505->client->addr, reg, + ICN8505_REG_ADDR_WIDTH, &val, 1, false); +} + +static int icn8505_read_prog_data(struct icn8505_data *icn8505, int reg, + void *buf, int len) +{ + return icn8505_read_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg, + ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false); +} + +static int icn8505_write_prog_data(struct icn8505_data *icn8505, int reg, + const void *buf, int len) +{ + return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg, + ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false); +} + +static int icn8505_write_prog_reg(struct icn8505_data *icn8505, int reg, u8 val) +{ + return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg, + ICN8505_PROG_REG_ADDR_WIDTH, &val, 1, false); +} + +/* + * Note this function uses a number of magic register addresses and values, + * there are deliberately no defines for these because the algorithm is taken + * from the icn85xx Android driver and I do not want to make up possibly wrong + * names for the addresses and/or values. + */ +static int icn8505_try_fw_upload(struct icn8505_data *icn8505, + const struct firmware *fw) +{ + struct device *dev = &icn8505->client->dev; + size_t offset, count; + int error; + u8 buf[4]; + u32 crc; + + /* Put the controller in programming mode */ + error = icn8505_write_prog_reg(icn8505, 0xcc3355, 0x5a); + if (error) + return error; + + usleep_range(2000, 5000); + + error = icn8505_write_prog_reg(icn8505, 0x040400, 0x01); + if (error) + return error; + + usleep_range(2000, 5000); + + error = icn8505_read_prog_data(icn8505, 0x040002, buf, 1); + if (error) + return error; + + if (buf[0] != 0x85) { + dev_err(dev, "Failed to enter programming mode\n"); + return -ENODEV; + } + + usleep_range(1000, 5000); + + /* Enable CRC mode */ + error = icn8505_write_prog_reg(icn8505, 0x40028, 1); + if (error) + return error; + + /* Send the firmware to SRAM */ + for (offset = 0; offset < fw->size; offset += count) { + count = min_t(size_t, fw->size - offset, 32); + error = icn8505_write_prog_data(icn8505, offset, + fw->data + offset, count); + if (error) + return error; + } + + /* Disable CRC mode */ + error = icn8505_write_prog_reg(icn8505, 0x40028, 0); + if (error) + return error; + + /* Get and check length and CRC */ + error = icn8505_read_prog_data(icn8505, 0x40034, buf, 2); + if (error) + return error; + + if (get_unaligned_le16(buf) != fw->size) { + dev_warn(dev, "Length mismatch after uploading fw\n"); + return -EIO; + } + + error = icn8505_read_prog_data(icn8505, 0x4002c, buf, 4); + if (error) + return error; + + crc = crc32_be(0, fw->data, fw->size); + if (get_unaligned_le32(buf) != crc) { + dev_warn(dev, "CRC mismatch after uploading fw\n"); + return -EIO; + } + + /* Boot controller from SRAM */ + error = icn8505_write_prog_reg(icn8505, 0x40400, 0x03); + if (error) + return error; + + usleep_range(2000, 5000); + return 0; +} + +static int icn8505_upload_fw(struct icn8505_data *icn8505) +{ + struct device *dev = &icn8505->client->dev; + const struct firmware *fw; + int i, error; + + /* + * Always load the firmware, even if we don't need it at boot, we + * we may need it at resume. Having loaded it once will make the + * firmware class code cache it at suspend/resume. + */ + error = request_firmware(&fw, icn8505->firmware_name, dev); + if (error) { + dev_err(dev, "Firmware request error %d\n", error); + return error; + } + + /* Check if the controller is not already up and running */ + if (icn8505_read_reg_silent(icn8505, 0x000a) == 0x85) + goto success; + + for (i = 1; i <= MAX_FW_UPLOAD_TRIES; i++) { + error = icn8505_try_fw_upload(icn8505, fw); + if (!error) + goto success; + + dev_err(dev, "Failed to upload firmware: %d (attempt %d/%d)\n", + error, i, MAX_FW_UPLOAD_TRIES); + usleep_range(2000, 5000); + } + +success: + release_firmware(fw); + return error; +} + +static bool icn8505_touch_active(u8 event) +{ + return event == ICN8505_EVENT_UPDATE1 || + event == ICN8505_EVENT_UPDATE2; +} + +static irqreturn_t icn8505_irq(int irq, void *dev_id) +{ + struct icn8505_data *icn8505 = dev_id; + struct device *dev = &icn8505->client->dev; + struct icn8505_touch_data touch_data; + int i, error; + + error = icn8505_read_data(icn8505, ICN8505_REG_TOUCHDATA, + &touch_data, sizeof(touch_data)); + if (error) { + dev_err(dev, "Error reading touch data: %d\n", error); + return IRQ_HANDLED; + } + + if (touch_data.touch_count > ICN8505_MAX_TOUCHES) { + dev_warn(dev, "Too many touches %d > %d\n", + touch_data.touch_count, ICN8505_MAX_TOUCHES); + touch_data.touch_count = ICN8505_MAX_TOUCHES; + } + + for (i = 0; i < touch_data.touch_count; i++) { + struct icn8505_touch *touch = &touch_data.touches[i]; + bool act = icn8505_touch_active(touch->event); + + input_mt_slot(icn8505->input, touch->slot); + input_mt_report_slot_state(icn8505->input, MT_TOOL_FINGER, act); + if (!act) + continue; + + touchscreen_report_pos(icn8505->input, &icn8505->prop, + get_unaligned_le16(touch->x), + get_unaligned_le16(touch->y), + true); + } + + input_mt_sync_frame(icn8505->input); + input_report_key(icn8505->input, KEY_LEFTMETA, + touch_data.softbutton == 1); + input_sync(icn8505->input); + + return IRQ_HANDLED; +} + +static int icn8505_probe_acpi(struct icn8505_data *icn8505, struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + const char *subsys = "unknown"; + struct acpi_device *adev; + union acpi_object *obj; + acpi_status status; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + status = acpi_evaluate_object(adev->handle, "_SUB", NULL, &buffer); + if (ACPI_SUCCESS(status)) { + obj = buffer.pointer; + if (obj->type == ACPI_TYPE_STRING) + subsys = obj->string.pointer; + else + dev_warn(dev, "Warning ACPI _SUB did not return a string\n"); + } else { + dev_warn(dev, "Warning ACPI _SUB failed: %#x\n", status); + buffer.pointer = NULL; + } + + snprintf(icn8505->firmware_name, sizeof(icn8505->firmware_name), + "chipone/icn8505-%s.fw", subsys); + + kfree(buffer.pointer); + return 0; +} + +static int icn8505_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct icn8505_data *icn8505; + struct input_dev *input; + __le16 resolution[2]; + int error; + + if (!client->irq) { + dev_err(dev, "No irq specified\n"); + return -EINVAL; + } + + icn8505 = devm_kzalloc(dev, sizeof(*icn8505), GFP_KERNEL); + if (!icn8505) + return -ENOMEM; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = client->name; + input->id.bustype = BUS_I2C; + + input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); + input_set_capability(input, EV_KEY, KEY_LEFTMETA); + + icn8505->client = client; + icn8505->input = input; + input_set_drvdata(input, icn8505); + + error = icn8505_probe_acpi(icn8505, dev); + if (error) + return error; + + error = icn8505_upload_fw(icn8505); + if (error) + return error; + + error = icn8505_read_data(icn8505, ICN8505_REG_CONFIGDATA, + resolution, sizeof(resolution)); + if (error) { + dev_err(dev, "Error reading resolution: %d\n", error); + return error; + } + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + le16_to_cpu(resolution[0]) - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + le16_to_cpu(resolution[1]) - 1, 0, 0); + + touchscreen_parse_properties(input, true, &icn8505->prop); + if (!input_abs_get_max(input, ABS_MT_POSITION_X) || + !input_abs_get_max(input, ABS_MT_POSITION_Y)) { + dev_err(dev, "Error touchscreen-size-x and/or -y missing\n"); + return -EINVAL; + } + + error = input_mt_init_slots(input, ICN8505_MAX_TOUCHES, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, NULL, icn8505_irq, + IRQF_ONESHOT, client->name, icn8505); + if (error) { + dev_err(dev, "Error requesting irq: %d\n", error); + return error; + } + + error = input_register_device(input); + if (error) + return error; + + i2c_set_clientdata(client, icn8505); + return 0; +} + +static int __maybe_unused icn8505_suspend(struct device *dev) +{ + struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev)); + + disable_irq(icn8505->client->irq); + + icn8505_write_reg(icn8505, ICN8505_REG_POWER, ICN8505_POWER_HIBERNATE); + + return 0; +} + +static int __maybe_unused icn8505_resume(struct device *dev) +{ + struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev)); + int error; + + error = icn8505_upload_fw(icn8505); + if (error) + return error; + + enable_irq(icn8505->client->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(icn8505_pm_ops, icn8505_suspend, icn8505_resume); + +static const struct acpi_device_id icn8505_acpi_match[] = { + { "CHPN0001" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, icn8505_acpi_match); + +static struct i2c_driver icn8505_driver = { + .driver = { + .name = "chipone_icn8505", + .pm = &icn8505_pm_ops, + .acpi_match_table = icn8505_acpi_match, + }, + .probe_new = icn8505_probe, +}; + +module_i2c_driver(icn8505_driver); + +MODULE_DESCRIPTION("ChipOne icn8505 I2C Touchscreen Driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 9736c83dd418..f2d9c2c41885 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -933,6 +933,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id); #ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, + { "GDIX1002", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c index bd5352824f77..c179060525ae 100644 --- a/drivers/input/touchscreen/mk712.c +++ b/drivers/input/touchscreen/mk712.c @@ -17,7 +17,7 @@ * found in Gateway AOL Connected Touchpad computers. * * Documentation for ICS MK712 can be found at: - * http://www.idt.com/products/getDoc.cfm?docID=18713923 + * https://www.idt.com/general-parts/mk712-touch-screen-controller */ /* diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index f1043ae71dcc..b86c1e5fbc11 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -34,6 +34,8 @@ #define SEQ_SETTLE 275 #define MAX_12BIT ((1 << 12) - 1) +#define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN) + static const int config_pins[] = { STEPCONFIG_XPP, STEPCONFIG_XNN, @@ -274,6 +276,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) if (status & IRQENB_HW_PEN) { ts_dev->pen_down = true; irqclr |= IRQENB_HW_PEN; + pm_stay_awake(ts_dev->mfd_tscadc->dev); } if (status & IRQENB_PENUP) { @@ -283,6 +286,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) input_report_key(input_dev, BTN_TOUCH, 0); input_report_abs(input_dev, ABS_PRESSURE, 0); input_sync(input_dev); + pm_relax(ts_dev->mfd_tscadc->dev); } else { ts_dev->pen_down = true; } @@ -432,6 +436,7 @@ static int titsc_probe(struct platform_device *pdev) goto err_free_mem; } + titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS); err = titsc_config_wires(ts_dev); @@ -495,6 +500,7 @@ static int __maybe_unused titsc_suspend(struct device *dev) tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (device_may_wakeup(tscadc_dev->dev)) { + titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); idle = titsc_readl(ts_dev, REG_IRQENABLE); titsc_writel(ts_dev, REG_IRQENABLE, (idle | IRQENB_HW_PEN)); @@ -513,6 +519,7 @@ static int __maybe_unused titsc_resume(struct device *dev) titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); + pm_relax(ts_dev->mfd_tscadc->dev); } titsc_step_config(ts_dev); titsc_writel(ts_dev, REG_FIFO0THR, diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index c6cf90868503..d61570d64ee7 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -440,6 +440,8 @@ static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt) #define MTOUCHUSB_RESET 7 #define MTOUCHUSB_REQ_CTRLLR_ID 10 +#define MTOUCHUSB_REQ_CTRLLR_ID_LEN 16 + static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) { if (hwcalib_xy) { @@ -454,11 +456,93 @@ static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) return 1; } +struct mtouch_priv { + u8 fw_rev_major; + u8 fw_rev_minor; +}; + +static ssize_t mtouch_firmware_rev_show(struct device *dev, + struct device_attribute *attr, char *output) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct mtouch_priv *priv = usbtouch->priv; + + return scnprintf(output, PAGE_SIZE, "%1x.%1x\n", + priv->fw_rev_major, priv->fw_rev_minor); +} +static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL); + +static struct attribute *mtouch_attrs[] = { + &dev_attr_firmware_rev.attr, + NULL +}; + +static const struct attribute_group mtouch_attr_group = { + .attrs = mtouch_attrs, +}; + +static int mtouch_get_fw_revision(struct usbtouch_usb *usbtouch) +{ + struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + struct mtouch_priv *priv = usbtouch->priv; + u8 *buf; + int ret; + + buf = kzalloc(MTOUCHUSB_REQ_CTRLLR_ID_LEN, GFP_NOIO); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_REQ_CTRLLR_ID, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, MTOUCHUSB_REQ_CTRLLR_ID_LEN, + USB_CTRL_SET_TIMEOUT); + if (ret != MTOUCHUSB_REQ_CTRLLR_ID_LEN) { + dev_warn(&usbtouch->interface->dev, + "Failed to read FW rev: %d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto free; + } + + priv->fw_rev_major = buf[3]; + priv->fw_rev_minor = buf[4]; + + ret = 0; + +free: + kfree(buf); + return ret; +} + +static int mtouch_alloc(struct usbtouch_usb *usbtouch) +{ + int ret; + + usbtouch->priv = kmalloc(sizeof(struct mtouch_priv), GFP_KERNEL); + if (!usbtouch->priv) + return -ENOMEM; + + ret = sysfs_create_group(&usbtouch->interface->dev.kobj, + &mtouch_attr_group); + if (ret) { + kfree(usbtouch->priv); + usbtouch->priv = NULL; + return ret; + } + + return 0; +} + static int mtouch_init(struct usbtouch_usb *usbtouch) { int ret, i; struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + ret = mtouch_get_fw_revision(usbtouch); + if (ret) + return ret; + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), MTOUCHUSB_RESET, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, @@ -492,6 +576,14 @@ static int mtouch_init(struct usbtouch_usb *usbtouch) return 0; } + +static void mtouch_exit(struct usbtouch_usb *usbtouch) +{ + struct mtouch_priv *priv = usbtouch->priv; + + sysfs_remove_group(&usbtouch->interface->dev.kobj, &mtouch_attr_group); + kfree(priv); +} #endif @@ -1119,7 +1211,9 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .max_yc = 0x4000, .rept_size = 11, .read_data = mtouch_read_data, + .alloc = mtouch_alloc, .init = mtouch_init, + .exit = mtouch_exit, }, #endif |