diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2021-11-11 21:58:54 -0800 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2021-11-11 21:58:54 -0800 |
commit | efe6f16c6faf724882ddaba2eda8830a87bcc2b2 (patch) | |
tree | ab5a4ac8c5b917b07af95c866770659f330b7f2b /drivers/input/touchscreen | |
parent | a02dcde595f7cbd240ccd64de96034ad91cffc40 (diff) | |
parent | 744d0090a5f6dfa4c81b53402ccdf08313100429 (diff) | |
download | lwn-efe6f16c6faf724882ddaba2eda8830a87bcc2b2.tar.gz lwn-efe6f16c6faf724882ddaba2eda8830a87bcc2b2.zip |
Merge branch 'next' into for-linus
Prepare input updates for 5.16 merge window.
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 200 | ||||
-rw-r--r-- | drivers/input/touchscreen/elants_i2c.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix.c | 231 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix.h | 117 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix_fwupload.c | 427 | ||||
-rw-r--r-- | drivers/input/touchscreen/ili210x.c | 559 | ||||
-rw-r--r-- | drivers/input/touchscreen/raydium_i2c_ts.c | 54 | ||||
-rw-r--r-- | drivers/input/touchscreen/st1232.c | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2004.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2005.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc200x-core.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc200x-core.h | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/wacom_i2c.c | 22 |
15 files changed, 1350 insertions, 285 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d4e74738c5a8..2f6adfb7b938 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -425,6 +425,7 @@ config TOUCHSCREEN_HYCON_HY46XX config TOUCHSCREEN_ILI210X tristate "Ilitek ILI210X based touchscreen" depends on I2C + select CRC_CCITT help Say Y here if you have a ILI210X based touchscreen controller. This driver supports models ILI2102, diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 7d34100f7f22..39a8127cf6a5 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -6,6 +6,7 @@ # Each configuration option enables a list of files. wm97xx-ts-y := wm97xx-core.o +goodix_ts-y := goodix.o goodix_fwupload.o obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o @@ -44,7 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o -obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index f113a27aeb1e..a25a77dd9a32 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -101,10 +101,6 @@ struct ads7846 { struct spi_device *spi; struct regulator *reg; -#if IS_ENABLED(CONFIG_HWMON) - struct device *hwmon; -#endif - u16 model; u16 vref_mv; u16 vref_delay_usecs; @@ -142,13 +138,18 @@ struct ads7846 { int (*filter)(void *data, int data_idx, int *val); void *filter_data; - void (*filter_cleanup)(void *data); int (*get_pendown_state)(void); int gpio_pendown; void (*wait_for_sync)(void); }; +enum ads7846_filter { + ADS7846_FILTER_OK, + ADS7846_FILTER_REPEAT, + ADS7846_FILTER_IGNORE, +}; + /* leave chip selected when we're done, for quicker re-select? */ #if 0 #define CS_CHANGE(xfer) ((xfer).cs_change = 1) @@ -549,6 +550,8 @@ __ATTRIBUTE_GROUPS(ads7846_attr); static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) { + struct device *hwmon; + /* hwmon sensors need a reference voltage */ switch (ts->model) { case 7846: @@ -569,17 +572,11 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) break; } - ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias, - ts, ads7846_attr_groups); + hwmon = devm_hwmon_device_register_with_groups(&spi->dev, + spi->modalias, ts, + ads7846_attr_groups); - return PTR_ERR_OR_ZERO(ts->hwmon); -} - -static void ads784x_hwmon_unregister(struct spi_device *spi, - struct ads7846 *ts) -{ - if (ts->hwmon) - hwmon_device_unregister(ts->hwmon); + return PTR_ERR_OR_ZERO(hwmon); } #else @@ -588,11 +585,6 @@ static inline int ads784x_hwmon_register(struct spi_device *spi, { return 0; } - -static inline void ads784x_hwmon_unregister(struct spi_device *spi, - struct ads7846 *ts) -{ -} #endif static ssize_t ads7846_pen_down_show(struct device *dev, @@ -1014,8 +1006,8 @@ static int ads7846_setup_pendown(struct spi_device *spi, ts->get_pendown_state = pdata->get_pendown_state; } else if (gpio_is_valid(pdata->gpio_pendown)) { - err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN, - "ads7846_pendown"); + err = devm_gpio_request_one(&spi->dev, pdata->gpio_pendown, + GPIOF_IN, "ads7846_pendown"); if (err) { dev_err(&spi->dev, "failed to request/setup pendown GPIO%d: %d\n", @@ -1212,24 +1204,30 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) } #endif +static void ads7846_regulator_disable(void *regulator) +{ + regulator_disable(regulator); +} + static int ads7846_probe(struct spi_device *spi) { const struct ads7846_platform_data *pdata; struct ads7846 *ts; + struct device *dev = &spi->dev; struct ads7846_packet *packet; struct input_dev *input_dev; unsigned long irq_flags; int err; if (!spi->irq) { - dev_dbg(&spi->dev, "no IRQ?\n"); + dev_dbg(dev, "no IRQ?\n"); return -EINVAL; } /* don't exceed max specified sample rate */ if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { - dev_err(&spi->dev, "f(sample) %d KHz?\n", - (spi->max_speed_hz/SAMPLE_BITS)/1000); + dev_err(dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/SAMPLE_BITS)/1000); return -EINVAL; } @@ -1245,13 +1243,17 @@ static int ads7846_probe(struct spi_device *spi) if (err < 0) return err; - ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); - packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !packet || !input_dev) { - err = -ENOMEM; - goto err_free_mem; - } + ts = devm_kzalloc(dev, sizeof(struct ads7846), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + packet = devm_kzalloc(dev, sizeof(struct ads7846_packet), GFP_KERNEL); + if (!packet) + return -ENOMEM; + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return -ENOMEM; spi_set_drvdata(spi, ts); @@ -1262,13 +1264,11 @@ static int ads7846_probe(struct spi_device *spi) mutex_init(&ts->lock); init_waitqueue_head(&ts->wait); - pdata = dev_get_platdata(&spi->dev); + pdata = dev_get_platdata(dev); if (!pdata) { - pdata = ads7846_probe_dt(&spi->dev); - if (IS_ERR(pdata)) { - err = PTR_ERR(pdata); - goto err_free_mem; - } + pdata = ads7846_probe_dt(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); } ts->model = pdata->model ? : 7846; @@ -1276,15 +1276,7 @@ static int ads7846_probe(struct spi_device *spi) ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->vref_mv = pdata->vref_mv; - if (pdata->filter != NULL) { - if (pdata->filter_init != NULL) { - err = pdata->filter_init(pdata, &ts->filter_data); - if (err < 0) - goto err_free_mem; - } - ts->filter = pdata->filter; - ts->filter_cleanup = pdata->filter_cleanup; - } else if (pdata->debounce_max) { + if (pdata->debounce_max) { ts->debounce_max = pdata->debounce_max; if (ts->debounce_max < 2) ts->debounce_max = 2; @@ -1298,7 +1290,7 @@ static int ads7846_probe(struct spi_device *spi) err = ads7846_setup_pendown(spi, ts, pdata); if (err) - goto err_cleanup_filter; + return err; if (pdata->penirq_recheck_delay_usecs) ts->penirq_recheck_delay_usecs = @@ -1306,15 +1298,16 @@ static int ads7846_probe(struct spi_device *spi) ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); input_dev->name = ts->name; input_dev->phys = ts->phys; - input_dev->dev.parent = &spi->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_dev->id.bustype = BUS_SPI; + input_dev->id.product = pdata->model; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, pdata->x_min ? : 0, pdata->x_max ? : MAX_12BIT, @@ -1345,125 +1338,84 @@ static int ads7846_probe(struct spi_device *spi) ads7846_setup_spi_msg(ts, pdata); - ts->reg = regulator_get(&spi->dev, "vcc"); + ts->reg = devm_regulator_get(dev, "vcc"); if (IS_ERR(ts->reg)) { err = PTR_ERR(ts->reg); - dev_err(&spi->dev, "unable to get regulator: %d\n", err); - goto err_free_gpio; + dev_err(dev, "unable to get regulator: %d\n", err); + return err; } err = regulator_enable(ts->reg); if (err) { - dev_err(&spi->dev, "unable to enable regulator: %d\n", err); - goto err_put_regulator; + dev_err(dev, "unable to enable regulator: %d\n", err); + return err; } + err = devm_add_action_or_reset(dev, ads7846_regulator_disable, ts->reg); + if (err) + return err; + irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; irq_flags |= IRQF_ONESHOT; - err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq, - irq_flags, spi->dev.driver->name, ts); - if (err && !pdata->irq_flags) { - dev_info(&spi->dev, + err = devm_request_threaded_irq(dev, spi->irq, + ads7846_hard_irq, ads7846_irq, + irq_flags, dev->driver->name, ts); + if (err && err != -EPROBE_DEFER && !pdata->irq_flags) { + dev_info(dev, "trying pin change workaround on irq %d\n", spi->irq); irq_flags |= IRQF_TRIGGER_RISING; - err = request_threaded_irq(spi->irq, - ads7846_hard_irq, ads7846_irq, - irq_flags, spi->dev.driver->name, ts); + err = devm_request_threaded_irq(dev, spi->irq, + ads7846_hard_irq, ads7846_irq, + irq_flags, dev->driver->name, + ts); } if (err) { - dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); - goto err_disable_regulator; + dev_dbg(dev, "irq %d busy?\n", spi->irq); + return err; } err = ads784x_hwmon_register(spi, ts); if (err) - goto err_free_irq; + return err; - dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); + dev_info(dev, "touchscreen, irq %d\n", spi->irq); /* * Take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ if (ts->model == 7845) - ads7845_read12_ser(&spi->dev, PWRDOWN); + ads7845_read12_ser(dev, PWRDOWN); else - (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux)); + (void) ads7846_read12_ser(dev, READ_12BIT_SER(vaux)); - err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); + err = devm_device_add_group(dev, &ads784x_attr_group); if (err) - goto err_remove_hwmon; + return err; err = input_register_device(input_dev); if (err) - goto err_remove_attr_group; + return err; - device_init_wakeup(&spi->dev, pdata->wakeup); + device_init_wakeup(dev, pdata->wakeup); /* * If device does not carry platform data we must have allocated it * when parsing DT data. */ - if (!dev_get_platdata(&spi->dev)) - devm_kfree(&spi->dev, (void *)pdata); + if (!dev_get_platdata(dev)) + devm_kfree(dev, (void *)pdata); return 0; - - err_remove_attr_group: - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); - err_remove_hwmon: - ads784x_hwmon_unregister(spi, ts); - err_free_irq: - free_irq(spi->irq, ts); - err_disable_regulator: - regulator_disable(ts->reg); - err_put_regulator: - regulator_put(ts->reg); - err_free_gpio: - if (!ts->get_pendown_state) - gpio_free(ts->gpio_pendown); - err_cleanup_filter: - if (ts->filter_cleanup) - ts->filter_cleanup(ts->filter_data); - err_free_mem: - input_free_device(input_dev); - kfree(packet); - kfree(ts); - return err; } static int ads7846_remove(struct spi_device *spi) { struct ads7846 *ts = spi_get_drvdata(spi); - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); - - ads7846_disable(ts); - free_irq(ts->spi->irq, ts); - - input_unregister_device(ts->input); - - ads784x_hwmon_unregister(spi, ts); - - regulator_put(ts->reg); - - if (!ts->get_pendown_state) { - /* - * If we are not using specialized pendown method we must - * have been relying on gpio we set up ourselves. - */ - gpio_free(ts->gpio_pendown); - } - - if (ts->filter_cleanup) - ts->filter_cleanup(ts->filter_data); - - kfree(ts->packet); - kfree(ts); - - dev_dbg(&spi->dev, "unregistered touchscreen\n"); + ads7846_stop(ts); return 0; } diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 68f542bb809f..7e13a66a8a95 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -1439,11 +1439,11 @@ static int elants_i2c_probe(struct i2c_client *client) if (error) return error; - error = devm_add_action(&client->dev, elants_i2c_power_off, ts); + error = devm_add_action_or_reset(&client->dev, + elants_i2c_power_off, ts); if (error) { dev_err(&client->dev, "failed to install power off action: %d\n", error); - elants_i2c_power_off(ts); return error; } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index c682b028f0a2..906b5a6b52d1 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -14,20 +14,15 @@ #include <linux/kernel.h> #include <linux/dmi.h> #include <linux/firmware.h> -#include <linux/gpio/consumer.h> -#include <linux/i2c.h> -#include <linux/input.h> -#include <linux/input/mt.h> -#include <linux/input/touchscreen.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/irq.h> #include <linux/interrupt.h> -#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/acpi.h> #include <linux/of.h> #include <asm/unaligned.h> +#include "goodix.h" #define GOODIX_GPIO_INT_NAME "irq" #define GOODIX_GPIO_RST_NAME "reset" @@ -38,22 +33,11 @@ #define GOODIX_CONTACT_SIZE 8 #define GOODIX_MAX_CONTACT_SIZE 9 #define GOODIX_MAX_CONTACTS 10 -#define GOODIX_MAX_KEYS 7 #define GOODIX_CONFIG_MIN_LENGTH 186 #define GOODIX_CONFIG_911_LENGTH 186 #define GOODIX_CONFIG_967_LENGTH 228 #define GOODIX_CONFIG_GT9X_LENGTH 240 -#define GOODIX_CONFIG_MAX_LENGTH 240 - -/* Register defines */ -#define GOODIX_REG_COMMAND 0x8040 -#define GOODIX_CMD_SCREEN_OFF 0x05 - -#define GOODIX_READ_COOR_ADDR 0x814E -#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050 -#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047 -#define GOODIX_REG_ID 0x8140 #define GOODIX_BUFFER_STATUS_READY BIT(7) #define GOODIX_HAVE_KEY BIT(4) @@ -68,55 +52,11 @@ #define ACPI_GPIO_SUPPORT #endif -struct goodix_ts_data; - -enum goodix_irq_pin_access_method { - IRQ_PIN_ACCESS_NONE, - IRQ_PIN_ACCESS_GPIO, - IRQ_PIN_ACCESS_ACPI_GPIO, - IRQ_PIN_ACCESS_ACPI_METHOD, -}; - -struct goodix_chip_data { - u16 config_addr; - int config_len; - int (*check_config)(struct goodix_ts_data *ts, const u8 *cfg, int len); - void (*calc_config_checksum)(struct goodix_ts_data *ts); -}; - struct goodix_chip_id { const char *id; const struct goodix_chip_data *data; }; -#define GOODIX_ID_MAX_LEN 4 - -struct goodix_ts_data { - struct i2c_client *client; - struct input_dev *input_dev; - const struct goodix_chip_data *chip; - struct touchscreen_properties prop; - unsigned int max_touch_num; - unsigned int int_trigger_type; - struct regulator *avdd28; - struct regulator *vddio; - struct gpio_desc *gpiod_int; - struct gpio_desc *gpiod_rst; - int gpio_count; - int gpio_int_idx; - char id[GOODIX_ID_MAX_LEN + 1]; - u16 version; - const char *cfg_name; - bool reset_controller_at_probe; - bool load_cfg_from_disk; - struct completion firmware_loading_complete; - unsigned long irq_flags; - enum goodix_irq_pin_access_method irq_pin_access_method; - unsigned int contact_size; - u8 config[GOODIX_CONFIG_MAX_LENGTH]; - unsigned short keymap[GOODIX_MAX_KEYS]; -}; - static int goodix_check_cfg_8(struct goodix_ts_data *ts, const u8 *cfg, int len); static int goodix_check_cfg_16(struct goodix_ts_data *ts, @@ -260,8 +200,7 @@ static const struct dmi_system_id inverted_x_screen[] = { * @buf: raw write data buffer. * @len: length of the buffer to write */ -static int goodix_i2c_read(struct i2c_client *client, - u16 reg, u8 *buf, int len) +int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len) { struct i2c_msg msgs[2]; __be16 wbuf = cpu_to_be16(reg); @@ -278,7 +217,13 @@ static int goodix_i2c_read(struct i2c_client *client, msgs[1].buf = buf; ret = i2c_transfer(client->adapter, msgs, 2); - return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); + if (ret >= 0) + ret = (ret == ARRAY_SIZE(msgs) ? 0 : -EIO); + + if (ret) + dev_err(&client->dev, "Error reading %d bytes from 0x%04x: %d\n", + len, reg, ret); + return ret; } /** @@ -289,8 +234,7 @@ static int goodix_i2c_read(struct i2c_client *client, * @buf: raw data buffer to write. * @len: length of the buffer to write */ -static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, - unsigned len) +int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len) { u8 *addr_buf; struct i2c_msg msg; @@ -310,11 +254,18 @@ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, msg.len = len + 2; ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + ret = (ret == 1 ? 0 : -EIO); + kfree(addr_buf); - return ret < 0 ? ret : (ret != 1 ? -EIO : 0); + + if (ret) + dev_err(&client->dev, "Error writing %d bytes to 0x%04x: %d\n", + len, reg, ret); + return ret; } -static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) +int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) { return goodix_i2c_write(client, reg, &value, sizeof(value)); } @@ -353,11 +304,8 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) do { error = goodix_i2c_read(ts->client, addr, data, header_contact_keycode_size); - if (error) { - dev_err(&ts->client->dev, "I2C transfer error: %d\n", - error); + if (error) return error; - } if (data[0] & GOODIX_BUFFER_STATUS_READY) { touch_num = data[0] & 0x0f; @@ -378,6 +326,11 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) return touch_num; } + if (data[0] == 0 && ts->firmware_name) { + if (goodix_handle_fw_request(ts)) + return 0; + } + usleep_range(1000, 2000); /* Poll every 1 - 2 ms */ } while (time_before(jiffies, max_timeout)); @@ -480,9 +433,7 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) struct goodix_ts_data *ts = dev_id; goodix_process_events(ts); - - if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0) - dev_err(&ts->client->dev, "I2C write end_cmd error\n"); + goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0); return IRQ_HANDLED; } @@ -598,7 +549,7 @@ static int goodix_check_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len) * @cfg: config firmware to write to device * @len: config data length */ -static int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len) +int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len) { int error; @@ -607,11 +558,9 @@ static int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len) return error; error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg, len); - if (error) { - dev_err(&ts->client->dev, "Failed to write config data: %d", - error); + if (error) return error; - } + dev_dbg(&ts->client->dev, "Config sent successfully."); /* Let the firmware reconfigure itself, so sleep for 10ms */ @@ -696,62 +645,82 @@ static int goodix_irq_direction_input(struct goodix_ts_data *ts) return -EINVAL; /* Never reached */ } -static int goodix_int_sync(struct goodix_ts_data *ts) +int goodix_int_sync(struct goodix_ts_data *ts) { int error; error = goodix_irq_direction_output(ts, 0); if (error) - return error; + goto error; msleep(50); /* T5: 50ms */ error = goodix_irq_direction_input(ts); if (error) - return error; + goto error; return 0; + +error: + dev_err(&ts->client->dev, "Controller irq sync failed.\n"); + return error; } /** - * goodix_reset - Reset device during power on + * goodix_reset_no_int_sync - Reset device, leaving interrupt line in output mode * * @ts: goodix_ts_data pointer */ -static int goodix_reset(struct goodix_ts_data *ts) +int goodix_reset_no_int_sync(struct goodix_ts_data *ts) { int error; /* begin select I2C slave addr */ error = gpiod_direction_output(ts->gpiod_rst, 0); if (error) - return error; + goto error; msleep(20); /* T2: > 10ms */ /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ error = goodix_irq_direction_output(ts, ts->client->addr == 0x14); if (error) - return error; + goto error; usleep_range(100, 2000); /* T3: > 100us */ error = gpiod_direction_output(ts->gpiod_rst, 1); if (error) - return error; + goto error; usleep_range(6000, 10000); /* T4: > 5ms */ /* end select I2C slave addr */ error = gpiod_direction_input(ts->gpiod_rst); if (error) - return error; + goto error; - error = goodix_int_sync(ts); + return 0; + +error: + dev_err(&ts->client->dev, "Controller reset failed.\n"); + return error; +} + +/** + * goodix_reset - Reset device during power on + * + * @ts: goodix_ts_data pointer + */ +static int goodix_reset(struct goodix_ts_data *ts) +{ + int error; + + error = goodix_reset_no_int_sync(ts); if (error) return error; - return 0; + return goodix_int_sync(ts); } #ifdef ACPI_GPIO_SUPPORT @@ -976,14 +945,19 @@ static void goodix_read_config(struct goodix_ts_data *ts) int x_max, y_max; int error; - error = goodix_i2c_read(ts->client, ts->chip->config_addr, - ts->config, ts->chip->config_len); - if (error) { - dev_warn(&ts->client->dev, "Error reading config: %d\n", - error); - ts->int_trigger_type = GOODIX_INT_TRIGGER; - ts->max_touch_num = GOODIX_MAX_CONTACTS; - return; + /* + * On controllers where we need to upload the firmware + * (controllers without flash) ts->config already has the config + * at this point and the controller itself does not have it yet! + */ + if (!ts->firmware_name) { + error = goodix_i2c_read(ts->client, ts->chip->config_addr, + ts->config, ts->chip->config_len); + if (error) { + ts->int_trigger_type = GOODIX_INT_TRIGGER; + ts->max_touch_num = GOODIX_MAX_CONTACTS; + return; + } } ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03; @@ -1011,10 +985,8 @@ static int goodix_read_version(struct goodix_ts_data *ts) char id_str[GOODIX_ID_MAX_LEN + 1]; error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf)); - if (error) { - dev_err(&ts->client->dev, "read version failed: %d\n", error); + if (error) return error; - } memcpy(id_str, buf, GOODIX_ID_MAX_LEN); id_str[GOODIX_ID_MAX_LEN] = 0; @@ -1040,13 +1012,10 @@ static int goodix_i2c_test(struct i2c_client *client) u8 test; while (retry++ < 2) { - error = goodix_i2c_read(client, GOODIX_REG_ID, - &test, 1); + error = goodix_i2c_read(client, GOODIX_REG_ID, &test, 1); if (!error) return 0; - dev_err(&client->dev, "i2c test failed attempt %d: %d\n", - retry, error); msleep(20); } @@ -1182,7 +1151,16 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx) struct goodix_ts_data *ts = ctx; int error; - if (cfg) { + if (ts->firmware_name) { + if (!cfg) + goto err_release_cfg; + + error = goodix_check_cfg(ts, cfg->data, cfg->size); + if (error) + goto err_release_cfg; + + memcpy(ts->config, cfg->data, cfg->size); + } else if (cfg) { /* send device configuration to the firmware */ error = goodix_send_cfg(ts, cfg->data, cfg->size); if (error) @@ -1208,6 +1186,7 @@ static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; + const char *cfg_name; int error; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -1257,10 +1236,8 @@ reset: if (ts->reset_controller_at_probe) { /* reset the controller */ error = goodix_reset(ts); - if (error) { - dev_err(&client->dev, "Controller reset failed.\n"); + if (error) return error; - } } error = goodix_i2c_test(client); @@ -1275,20 +1252,27 @@ reset: return error; } + error = goodix_firmware_check(ts); + if (error) + return error; + error = goodix_read_version(ts); - if (error) { - dev_err(&client->dev, "Read version failed.\n"); + if (error) return error; - } ts->chip = goodix_get_chip_data(ts->id); if (ts->load_cfg_from_disk) { /* update device config */ - ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL, - "goodix_%s_cfg.bin", ts->id); - if (!ts->cfg_name) - return -ENOMEM; + error = device_property_read_string(&client->dev, + "goodix,config-name", + &cfg_name); + if (!error) + snprintf(ts->cfg_name, sizeof(ts->cfg_name), + "goodix/%s", cfg_name); + else + snprintf(ts->cfg_name, sizeof(ts->cfg_name), + "goodix_%s_cfg.bin", ts->id); error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name, &client->dev, GFP_KERNEL, ts, @@ -1338,6 +1322,9 @@ static int __maybe_unused goodix_suspend(struct device *dev) /* Free IRQ as IRQ pin is used as output in the suspend sequence */ goodix_free_irq(ts); + /* Save reference (calibration) info if necessary */ + goodix_save_bak_ref(ts); + /* Output LOW on the INT pin for 5 ms */ error = goodix_irq_direction_output(ts, 0); if (error) { @@ -1350,7 +1337,6 @@ static int __maybe_unused goodix_suspend(struct device *dev) error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND, GOODIX_CMD_SCREEN_OFF); if (error) { - dev_err(&ts->client->dev, "Screen off command failed\n"); goodix_irq_direction_input(ts); goodix_request_irq(ts); return -EAGAIN; @@ -1393,19 +1379,14 @@ static int __maybe_unused goodix_resume(struct device *dev) error = goodix_i2c_read(ts->client, ts->chip->config_addr, &config_ver, 1); - if (error) - dev_warn(dev, "Error reading config version: %d, resetting controller\n", - error); - else if (config_ver != ts->config[0]) + if (!error && config_ver != ts->config[0]) dev_info(dev, "Config version mismatch %d != %d, resetting controller\n", config_ver, ts->config[0]); if (error != 0 || config_ver != ts->config[0]) { error = goodix_reset(ts); - if (error) { - dev_err(dev, "Controller reset failed.\n"); + if (error) return error; - } error = goodix_send_cfg(ts, ts->config, ts->chip->config_len); if (error) diff --git a/drivers/input/touchscreen/goodix.h b/drivers/input/touchscreen/goodix.h new file mode 100644 index 000000000000..62138f930d1a --- /dev/null +++ b/drivers/input/touchscreen/goodix.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __GOODIX_H__ +#define __GOODIX_H__ + +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/regulator/consumer.h> + +/* Register defines */ +#define GOODIX_REG_MISCTL_DSP_CTL 0x4010 +#define GOODIX_REG_MISCTL_SRAM_BANK 0x4048 +#define GOODIX_REG_MISCTL_MEM_CD_EN 0x4049 +#define GOODIX_REG_MISCTL_CACHE_EN 0x404B +#define GOODIX_REG_MISCTL_TMR0_EN 0x40B0 +#define GOODIX_REG_MISCTL_SWRST 0x4180 +#define GOODIX_REG_MISCTL_CPU_SWRST_PULSE 0x4184 +#define GOODIX_REG_MISCTL_BOOTCTL 0x4190 +#define GOODIX_REG_MISCTL_BOOT_OPT 0x4218 +#define GOODIX_REG_MISCTL_BOOT_CTL 0x5094 + +#define GOODIX_REG_FW_SIG 0x8000 +#define GOODIX_FW_SIG_LEN 10 + +#define GOODIX_REG_MAIN_CLK 0x8020 +#define GOODIX_MAIN_CLK_LEN 6 + +#define GOODIX_REG_COMMAND 0x8040 +#define GOODIX_CMD_SCREEN_OFF 0x05 + +#define GOODIX_REG_SW_WDT 0x8041 + +#define GOODIX_REG_REQUEST 0x8043 +#define GOODIX_RQST_RESPONDED 0x00 +#define GOODIX_RQST_CONFIG 0x01 +#define GOODIX_RQST_BAK_REF 0x02 +#define GOODIX_RQST_RESET 0x03 +#define GOODIX_RQST_MAIN_CLOCK 0x04 +/* + * Unknown request which gets send by the controller aprox. + * every 34 seconds once it is up and running. + */ +#define GOODIX_RQST_UNKNOWN 0x06 +#define GOODIX_RQST_IDLE 0xFF + +#define GOODIX_REG_STATUS 0x8044 + +#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050 +#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047 +#define GOODIX_REG_ID 0x8140 +#define GOODIX_READ_COOR_ADDR 0x814E +#define GOODIX_REG_BAK_REF 0x99D0 + +#define GOODIX_ID_MAX_LEN 4 +#define GOODIX_CONFIG_MAX_LENGTH 240 +#define GOODIX_MAX_KEYS 7 + +enum goodix_irq_pin_access_method { + IRQ_PIN_ACCESS_NONE, + IRQ_PIN_ACCESS_GPIO, + IRQ_PIN_ACCESS_ACPI_GPIO, + IRQ_PIN_ACCESS_ACPI_METHOD, +}; + +struct goodix_ts_data; + +struct goodix_chip_data { + u16 config_addr; + int config_len; + int (*check_config)(struct goodix_ts_data *ts, const u8 *cfg, int len); + void (*calc_config_checksum)(struct goodix_ts_data *ts); +}; + +struct goodix_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct goodix_chip_data *chip; + const char *firmware_name; + struct touchscreen_properties prop; + unsigned int max_touch_num; + unsigned int int_trigger_type; + struct regulator *avdd28; + struct regulator *vddio; + struct gpio_desc *gpiod_int; + struct gpio_desc *gpiod_rst; + int gpio_count; + int gpio_int_idx; + char id[GOODIX_ID_MAX_LEN + 1]; + char cfg_name[64]; + u16 version; + bool reset_controller_at_probe; + bool load_cfg_from_disk; + struct completion firmware_loading_complete; + unsigned long irq_flags; + enum goodix_irq_pin_access_method irq_pin_access_method; + unsigned int contact_size; + u8 config[GOODIX_CONFIG_MAX_LENGTH]; + unsigned short keymap[GOODIX_MAX_KEYS]; + u8 main_clk[GOODIX_MAIN_CLK_LEN]; + int bak_ref_len; + u8 *bak_ref; +}; + +int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len); +int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len); +int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value); +int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len); +int goodix_int_sync(struct goodix_ts_data *ts); +int goodix_reset_no_int_sync(struct goodix_ts_data *ts); + +int goodix_firmware_check(struct goodix_ts_data *ts); +bool goodix_handle_fw_request(struct goodix_ts_data *ts); +void goodix_save_bak_ref(struct goodix_ts_data *ts); + +#endif diff --git a/drivers/input/touchscreen/goodix_fwupload.c b/drivers/input/touchscreen/goodix_fwupload.c new file mode 100644 index 000000000000..c1e7a2413078 --- /dev/null +++ b/drivers/input/touchscreen/goodix_fwupload.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Goodix Touchscreen firmware upload support + * + * Copyright (c) 2021 Hans de Goede <hdegoede@redhat.com> + * + * This is a rewrite of gt9xx_update.c from the Allwinner H3 BSP which is: + * Copyright (c) 2010 - 2012 Goodix Technology. + * Author: andrew@goodix.com + */ + +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include "goodix.h" + +#define GOODIX_FW_HEADER_LENGTH sizeof(struct goodix_fw_header) +#define GOODIX_FW_SECTION_LENGTH 0x2000 +#define GOODIX_FW_DSP_LENGTH 0x1000 +#define GOODIX_FW_UPLOAD_ADDRESS 0xc000 + +#define GOODIX_CFG_LOC_HAVE_KEY 7 +#define GOODIX_CFG_LOC_DRVA_NUM 27 +#define GOODIX_CFG_LOC_DRVB_NUM 28 +#define GOODIX_CFG_LOC_SENS_NUM 29 + +struct goodix_fw_header { + u8 hw_info[4]; + u8 pid[8]; + u8 vid[2]; +} __packed; + +static u16 goodix_firmware_checksum(const u8 *data, int size) +{ + u16 checksum = 0; + int i; + + for (i = 0; i < size; i += 2) + checksum += (data[i] << 8) + data[i + 1]; + + return checksum; +} + +static int goodix_firmware_verify(struct device *dev, const struct firmware *fw) +{ + const struct goodix_fw_header *fw_header; + size_t expected_size; + const u8 *data; + u16 checksum; + char buf[9]; + + expected_size = GOODIX_FW_HEADER_LENGTH + 4 * GOODIX_FW_SECTION_LENGTH + + GOODIX_FW_DSP_LENGTH; + if (fw->size != expected_size) { + dev_err(dev, "Firmware has wrong size, expected %zu got %zu\n", + expected_size, fw->size); + return -EINVAL; + } + + data = fw->data + GOODIX_FW_HEADER_LENGTH; + checksum = goodix_firmware_checksum(data, 4 * GOODIX_FW_SECTION_LENGTH); + if (checksum) { + dev_err(dev, "Main firmware checksum error\n"); + return -EINVAL; + } + + data += 4 * GOODIX_FW_SECTION_LENGTH; + checksum = goodix_firmware_checksum(data, GOODIX_FW_DSP_LENGTH); + if (checksum) { + dev_err(dev, "DSP firmware checksum error\n"); + return -EINVAL; + } + + fw_header = (const struct goodix_fw_header *)fw->data; + dev_info(dev, "Firmware hardware info %02x%02x%02x%02x\n", + fw_header->hw_info[0], fw_header->hw_info[1], + fw_header->hw_info[2], fw_header->hw_info[3]); + /* pid is a 8 byte buffer containing a string, weird I know */ + memcpy(buf, fw_header->pid, 8); + buf[8] = 0; + dev_info(dev, "Firmware PID: %s VID: %02x%02x\n", buf, + fw_header->vid[0], fw_header->vid[1]); + return 0; +} + +static int goodix_enter_upload_mode(struct i2c_client *client) +{ + int tries, error; + u8 val; + + tries = 200; + do { + error = goodix_i2c_write_u8(client, + GOODIX_REG_MISCTL_SWRST, 0x0c); + if (error) + return error; + + error = goodix_i2c_read(client, + GOODIX_REG_MISCTL_SWRST, &val, 1); + if (error) + return error; + + if (val == 0x0c) + break; + } while (--tries); + + if (!tries) { + dev_err(&client->dev, "Error could not hold ss51 & dsp\n"); + return -EIO; + } + + /* DSP_CK and DSP_ALU_CK PowerOn */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_DSP_CTL, 0x00); + if (error) + return error; + + /* Disable watchdog */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_TMR0_EN, 0x00); + if (error) + return error; + + /* Clear cache enable */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_CACHE_EN, 0x00); + if (error) + return error; + + /* Set boot from SRAM */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOTCTL, 0x02); + if (error) + return error; + + /* Software reboot */ + error = goodix_i2c_write_u8(client, + GOODIX_REG_MISCTL_CPU_SWRST_PULSE, 0x01); + if (error) + return error; + + /* Clear control flag */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOTCTL, 0x00); + if (error) + return error; + + /* Set scramble */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOT_OPT, 0x00); + if (error) + return error; + + /* Enable accessing code */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_MEM_CD_EN, 0x01); + if (error) + return error; + + return 0; +} + +static int goodix_start_firmware(struct i2c_client *client) +{ + int error; + u8 val; + + /* Init software watchdog */ + error = goodix_i2c_write_u8(client, GOODIX_REG_SW_WDT, 0xaa); + if (error) + return error; + + /* Release SS51 & DSP */ + error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_SWRST, 0x00); + if (error) + return error; + + error = goodix_i2c_read(client, GOODIX_REG_SW_WDT, &val, 1); + if (error) + return error; + + /* The value we've written to SW_WDT should have been cleared now */ + if (val == 0xaa) { + dev_err(&client->dev, "Error SW_WDT reg not cleared on fw startup\n"); + return -EIO; + } + + /* Re-init software watchdog */ + error = goodix_i2c_write_u8(client, GOODIX_REG_SW_WDT, 0xaa); + if (error) + return error; + + return 0; +} + +static int goodix_firmware_upload(struct goodix_ts_data *ts) +{ + const struct firmware *fw; + char fw_name[64]; + const u8 *data; + int error; + + snprintf(fw_name, sizeof(fw_name), "goodix/%s", ts->firmware_name); + + error = request_firmware(&fw, fw_name, &ts->client->dev); + if (error) { + dev_err(&ts->client->dev, "Firmware request error %d\n", error); + return error; + } + + error = goodix_firmware_verify(&ts->client->dev, fw); + if (error) + goto release; + + error = goodix_reset_no_int_sync(ts); + if (error) + return error; + + error = goodix_enter_upload_mode(ts->client); + if (error) + goto release; + + /* Select SRAM bank 0 and upload section 1 & 2 */ + error = goodix_i2c_write_u8(ts->client, + GOODIX_REG_MISCTL_SRAM_BANK, 0x00); + if (error) + goto release; + + data = fw->data + GOODIX_FW_HEADER_LENGTH; + error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, + data, 2 * GOODIX_FW_SECTION_LENGTH); + if (error) + goto release; + + /* Select SRAM bank 1 and upload section 3 & 4 */ + error = goodix_i2c_write_u8(ts->client, + GOODIX_REG_MISCTL_SRAM_BANK, 0x01); + if (error) + goto release; + + data += 2 * GOODIX_FW_SECTION_LENGTH; + error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, + data, 2 * GOODIX_FW_SECTION_LENGTH); + if (error) + goto release; + + /* Select SRAM bank 2 and upload the DSP firmware */ + error = goodix_i2c_write_u8(ts->client, + GOODIX_REG_MISCTL_SRAM_BANK, 0x02); + if (error) + goto release; + + data += 2 * GOODIX_FW_SECTION_LENGTH; + error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, + data, GOODIX_FW_DSP_LENGTH); + if (error) + goto release; + + error = goodix_start_firmware(ts->client); + if (error) + goto release; + + error = goodix_int_sync(ts); +release: + release_firmware(fw); + return error; +} + +static int goodix_prepare_bak_ref(struct goodix_ts_data *ts) +{ + u8 have_key, driver_num, sensor_num; + + if (ts->bak_ref) + return 0; /* Already done */ + + have_key = (ts->config[GOODIX_CFG_LOC_HAVE_KEY] & 0x01); + + driver_num = (ts->config[GOODIX_CFG_LOC_DRVA_NUM] & 0x1f) + + (ts->config[GOODIX_CFG_LOC_DRVB_NUM] & 0x1f); + if (have_key) + driver_num--; + + sensor_num = (ts->config[GOODIX_CFG_LOC_SENS_NUM] & 0x0f) + + ((ts->config[GOODIX_CFG_LOC_SENS_NUM] >> 4) & 0x0f); + + dev_dbg(&ts->client->dev, "Drv %d Sen %d Key %d\n", + driver_num, sensor_num, have_key); + + ts->bak_ref_len = (driver_num * (sensor_num - 2) + 2) * 2; + + ts->bak_ref = devm_kzalloc(&ts->client->dev, + ts->bak_ref_len, GFP_KERNEL); + if (!ts->bak_ref) + return -ENOMEM; + + /* + * The bak_ref array contains the backup of an array of (self/auto) + * calibration related values which the Android version of the driver + * stores on the filesystem so that it can be restored after reboot. + * The mainline kernel never writes directly to the filesystem like + * this, we always start will all the values which give a correction + * factor in approx. the -20 - +20 range (in 2s complement) set to 0. + * + * Note the touchscreen works fine without restoring the reference + * values after a reboot / power-cycle. + * + * The last 2 bytes are a 16 bits unsigned checksum which is expected + * to make the addition al all 16 bit unsigned values in the array add + * up to 1 (rather then the usual 0), so we must set the last byte to 1. + */ + ts->bak_ref[ts->bak_ref_len - 1] = 1; + + return 0; +} + +static int goodix_send_main_clock(struct goodix_ts_data *ts) +{ + u32 main_clk = 54; /* Default main clock */ + u8 checksum = 0; + int i; + + device_property_read_u32(&ts->client->dev, + "goodix,main-clk", &main_clk); + + for (i = 0; i < (GOODIX_MAIN_CLK_LEN - 1); i++) { + ts->main_clk[i] = main_clk; + checksum += main_clk; + } + + /* The value of all bytes combines must be 0 */ + ts->main_clk[GOODIX_MAIN_CLK_LEN - 1] = 256 - checksum; + + return goodix_i2c_write(ts->client, GOODIX_REG_MAIN_CLK, + ts->main_clk, GOODIX_MAIN_CLK_LEN); +} + +int goodix_firmware_check(struct goodix_ts_data *ts) +{ + device_property_read_string(&ts->client->dev, + "firmware-name", &ts->firmware_name); + if (!ts->firmware_name) + return 0; + + if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) { + dev_err(&ts->client->dev, "Error no IRQ-pin access method, cannot upload fw.\n"); + return -EINVAL; + } + + dev_info(&ts->client->dev, "Touchscreen controller needs fw-upload\n"); + ts->load_cfg_from_disk = true; + + return goodix_firmware_upload(ts); +} + +bool goodix_handle_fw_request(struct goodix_ts_data *ts) +{ + int error; + u8 val; + + error = goodix_i2c_read(ts->client, GOODIX_REG_REQUEST, &val, 1); + if (error) + return false; + + switch (val) { + case GOODIX_RQST_RESPONDED: + /* + * If we read back our own last ack the IRQ was not for + * a request. + */ + return false; + case GOODIX_RQST_CONFIG: + error = goodix_send_cfg(ts, ts->config, ts->chip->config_len); + if (error) + return false; + + break; + case GOODIX_RQST_BAK_REF: + error = goodix_prepare_bak_ref(ts); + if (error) + return false; + + error = goodix_i2c_write(ts->client, GOODIX_REG_BAK_REF, + ts->bak_ref, ts->bak_ref_len); + if (error) + return false; + + break; + case GOODIX_RQST_RESET: + error = goodix_firmware_upload(ts); + if (error) + return false; + + break; + case GOODIX_RQST_MAIN_CLOCK: + error = goodix_send_main_clock(ts); + if (error) + return false; + + break; + case GOODIX_RQST_UNKNOWN: + case GOODIX_RQST_IDLE: + break; + default: + dev_err_ratelimited(&ts->client->dev, "Unknown Request: 0x%02x\n", val); + } + + /* Ack the request */ + goodix_i2c_write_u8(ts->client, + GOODIX_REG_REQUEST, GOODIX_RQST_RESPONDED); + return true; +} + +void goodix_save_bak_ref(struct goodix_ts_data *ts) +{ + int error; + u8 val; + + if (!ts->firmware_name) + return; + + error = goodix_i2c_read(ts->client, GOODIX_REG_STATUS, &val, 1); + if (error) + return; + + if (!(val & 0x80)) + return; + + error = goodix_i2c_read(ts->client, GOODIX_REG_BAK_REF, + ts->bak_ref, ts->bak_ref_len); + if (error) { + memset(ts->bak_ref, 0, ts->bak_ref_len); + ts->bak_ref[ts->bak_ref_len - 1] = 1; + } +} diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 30576a5f2f04..2bd407d86bae 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include <linux/crc-ccitt.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> +#include <linux/ihex.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> @@ -12,7 +14,7 @@ #include <linux/slab.h> #include <asm/unaligned.h> -#define ILI2XXX_POLL_PERIOD 20 +#define ILI2XXX_POLL_PERIOD 15 #define ILI210X_DATA_SIZE 64 #define ILI211X_DATA_SIZE 43 @@ -22,8 +24,23 @@ /* Touchscreen commands */ #define REG_TOUCHDATA 0x10 #define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_PROTOCOL_VERSION 0x42 +#define REG_KERNEL_VERSION 0x61 +#define REG_IC_BUSY 0x80 +#define REG_IC_BUSY_NOT_BUSY 0x50 +#define REG_GET_MODE 0xc0 +#define REG_GET_MODE_AP 0x5a +#define REG_GET_MODE_BL 0x55 +#define REG_SET_MODE_AP 0xc1 +#define REG_SET_MODE_BL 0xc2 +#define REG_WRITE_DATA 0xc3 +#define REG_WRITE_ENABLE 0xc4 +#define REG_READ_DATA_CRC 0xc7 #define REG_CALIBRATE 0xcc +#define ILI251X_FW_FILENAME "ilitek/ili251x.bin" + struct ili2xxx_chip { int (*read_reg)(struct i2c_client *client, u8 reg, void *buf, size_t len); @@ -35,6 +52,7 @@ struct ili2xxx_chip { unsigned int max_touches; unsigned int resolution; bool has_calibrate_reg; + bool has_firmware_proto; bool has_pressure_reg; }; @@ -44,6 +62,10 @@ struct ili210x { struct gpio_desc *reset_gpio; struct touchscreen_properties prop; const struct ili2xxx_chip *chip; + u8 version_firmware[8]; + u8 version_kernel[5]; + u8 version_proto[2]; + u8 ic_mode[2]; bool stop; }; @@ -202,15 +224,17 @@ static const struct ili2xxx_chip ili212x_chip = { .has_calibrate_reg = true, }; -static int ili251x_read_reg(struct i2c_client *client, - u8 reg, void *buf, size_t len) +static int ili251x_read_reg_common(struct i2c_client *client, + u8 reg, void *buf, size_t len, + unsigned int delay) { int error; int ret; ret = i2c_master_send(client, ®, 1); if (ret == 1) { - usleep_range(5000, 5500); + if (delay) + usleep_range(delay, delay + 500); ret = i2c_master_recv(client, buf, len); if (ret == len) @@ -222,12 +246,18 @@ static int ili251x_read_reg(struct i2c_client *client, return ret; } +static int ili251x_read_reg(struct i2c_client *client, + u8 reg, void *buf, size_t len) +{ + return ili251x_read_reg_common(client, reg, buf, len, 5000); +} + static int ili251x_read_touch_data(struct i2c_client *client, u8 *data) { int error; - error = ili251x_read_reg(client, REG_TOUCHDATA, - data, ILI251X_DATA_SIZE1); + error = ili251x_read_reg_common(client, REG_TOUCHDATA, + data, ILI251X_DATA_SIZE1, 0); if (!error && data[0] == 2) { error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1, ILI251X_DATA_SIZE2); @@ -268,6 +298,7 @@ static const struct ili2xxx_chip ili251x_chip = { .continue_polling = ili251x_check_continue_polling, .max_touches = 10, .has_calibrate_reg = true, + .has_firmware_proto = true, .has_pressure_reg = true, }; @@ -303,10 +334,13 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data) const struct ili2xxx_chip *chip = priv->chip; u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; bool keep_polling; + ktime_t time_next; + s64 time_delta; bool touch; int error; do { + time_next = ktime_add_ms(ktime_get(), ILI2XXX_POLL_PERIOD); error = chip->get_touch_data(client, touchdata); if (error) { dev_err(&client->dev, @@ -316,13 +350,201 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data) touch = ili210x_report_events(priv, touchdata); keep_polling = chip->continue_polling(touchdata, touch); - if (keep_polling) - msleep(ILI2XXX_POLL_PERIOD); + if (keep_polling) { + time_delta = ktime_us_delta(time_next, ktime_get()); + if (time_delta > 0) + usleep_range(time_delta, time_delta + 1000); + } } while (!priv->stop && keep_polling); return IRQ_HANDLED; } +static int ili251x_firmware_update_resolution(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + u16 resx, resy; + u8 rs[10]; + int error; + + /* The firmware update blob might have changed the resolution. */ + error = priv->chip->read_reg(client, REG_PANEL_INFO, &rs, sizeof(rs)); + if (error) + return error; + + resx = le16_to_cpup((__le16 *)rs); + resy = le16_to_cpup((__le16 *)(rs + 2)); + + /* The value reported by the firmware is invalid. */ + if (!resx || resx == 0xffff || !resy || resy == 0xffff) + return -EINVAL; + + input_abs_set_max(priv->input, ABS_X, resx - 1); + input_abs_set_max(priv->input, ABS_Y, resy - 1); + input_abs_set_max(priv->input, ABS_MT_POSITION_X, resx - 1); + input_abs_set_max(priv->input, ABS_MT_POSITION_Y, resy - 1); + + return 0; +} + +static ssize_t ili251x_firmware_update_firmware_version(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + int error; + u8 fw[8]; + + /* Get firmware version */ + error = priv->chip->read_reg(client, REG_FIRMWARE_VERSION, + &fw, sizeof(fw)); + if (!error) + memcpy(priv->version_firmware, fw, sizeof(fw)); + + return error; +} + +static ssize_t ili251x_firmware_update_kernel_version(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + int error; + u8 kv[5]; + + /* Get kernel version */ + error = priv->chip->read_reg(client, REG_KERNEL_VERSION, + &kv, sizeof(kv)); + if (!error) + memcpy(priv->version_kernel, kv, sizeof(kv)); + + return error; +} + +static ssize_t ili251x_firmware_update_protocol_version(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + int error; + u8 pv[2]; + + /* Get protocol version */ + error = priv->chip->read_reg(client, REG_PROTOCOL_VERSION, + &pv, sizeof(pv)); + if (!error) + memcpy(priv->version_proto, pv, sizeof(pv)); + + return error; +} + +static ssize_t ili251x_firmware_update_ic_mode(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + int error; + u8 md[2]; + + /* Get chip boot mode */ + error = priv->chip->read_reg(client, REG_GET_MODE, &md, sizeof(md)); + if (!error) + memcpy(priv->ic_mode, md, sizeof(md)); + + return error; +} + +static int ili251x_firmware_update_cached_state(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + int error; + + if (!priv->chip->has_firmware_proto) + return 0; + + /* Wait for firmware to boot and stabilize itself. */ + msleep(200); + + /* Firmware does report valid information. */ + error = ili251x_firmware_update_resolution(dev); + if (error) + return error; + + error = ili251x_firmware_update_firmware_version(dev); + if (error) + return error; + + error = ili251x_firmware_update_kernel_version(dev); + if (error) + return error; + + error = ili251x_firmware_update_protocol_version(dev); + if (error) + return error; + + error = ili251x_firmware_update_ic_mode(dev); + if (error) + return error; + + return 0; +} + +static ssize_t ili251x_firmware_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + u8 *fw = priv->version_firmware; + + return sysfs_emit(buf, "%02x%02x.%02x%02x.%02x%02x.%02x%02x\n", + fw[0], fw[1], fw[2], fw[3], + fw[4], fw[5], fw[6], fw[7]); +} +static DEVICE_ATTR(firmware_version, 0444, ili251x_firmware_version_show, NULL); + +static ssize_t ili251x_kernel_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + u8 *kv = priv->version_kernel; + + return sysfs_emit(buf, "%02x.%02x.%02x.%02x.%02x\n", + kv[0], kv[1], kv[2], kv[3], kv[4]); +} +static DEVICE_ATTR(kernel_version, 0444, ili251x_kernel_version_show, NULL); + +static ssize_t ili251x_protocol_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + u8 *pv = priv->version_proto; + + return sysfs_emit(buf, "%02x.%02x\n", pv[0], pv[1]); +} +static DEVICE_ATTR(protocol_version, 0444, ili251x_protocol_version_show, NULL); + +static ssize_t ili251x_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + u8 *md = priv->ic_mode; + char *mode = "AP"; + + if (md[0] == REG_GET_MODE_AP) /* Application Mode */ + mode = "AP"; + else if (md[0] == REG_GET_MODE_BL) /* BootLoader Mode */ + mode = "BL"; + else /* Unknown Mode */ + mode = "??"; + + return sysfs_emit(buf, "%02x.%02x:%s\n", md[0], md[1], mode); +} +static DEVICE_ATTR(mode, 0444, ili251x_mode_show, NULL); + static ssize_t ili210x_calibrate(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -349,24 +571,333 @@ static ssize_t ili210x_calibrate(struct device *dev, } static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate); +static int ili251x_firmware_to_buffer(const struct firmware *fw, + u8 **buf, u16 *ac_end, u16 *df_end) +{ + const struct ihex_binrec *rec; + u32 fw_addr, fw_last_addr = 0; + u16 fw_len; + u8 *fw_buf; + int error; + + /* + * The firmware ihex blob can never be bigger than 64 kiB, so make this + * simple -- allocate a 64 kiB buffer, iterate over the ihex blob records + * once, copy them all into this buffer at the right locations, and then + * do all operations on this linear buffer. + */ + fw_buf = kzalloc(SZ_64K, GFP_KERNEL); + if (!fw_buf) + return -ENOMEM; + + rec = (const struct ihex_binrec *)fw->data; + while (rec) { + fw_addr = be32_to_cpu(rec->addr); + fw_len = be16_to_cpu(rec->len); + + /* The last 32 Byte firmware block can be 0xffe0 */ + if (fw_addr + fw_len > SZ_64K || fw_addr > SZ_64K - 32) { + error = -EFBIG; + goto err_big; + } + + /* Find the last address before DF start address, that is AC end */ + if (fw_addr == 0xf000) + *ac_end = fw_last_addr; + fw_last_addr = fw_addr + fw_len; + + memcpy(fw_buf + fw_addr, rec->data, fw_len); + rec = ihex_next_binrec(rec); + } + + /* DF end address is the last address in the firmware blob */ + *df_end = fw_addr + fw_len; + *buf = fw_buf; + return 0; + +err_big: + kfree(fw_buf); + return error; +} + +/* Switch mode between Application and BootLoader */ +static int ili251x_switch_ic_mode(struct i2c_client *client, u8 cmd_mode) +{ + struct ili210x *priv = i2c_get_clientdata(client); + u8 cmd_wren[3] = { REG_WRITE_ENABLE, 0x5a, 0xa5 }; + u8 md[2]; + int error; + + error = priv->chip->read_reg(client, REG_GET_MODE, md, sizeof(md)); + if (error) + return error; + /* Mode already set */ + if ((cmd_mode == REG_SET_MODE_AP && md[0] == REG_GET_MODE_AP) || + (cmd_mode == REG_SET_MODE_BL && md[0] == REG_GET_MODE_BL)) + return 0; + + /* Unlock writes */ + error = i2c_master_send(client, cmd_wren, sizeof(cmd_wren)); + if (error != sizeof(cmd_wren)) + return -EINVAL; + + mdelay(20); + + /* Select mode (BootLoader or Application) */ + error = i2c_master_send(client, &cmd_mode, 1); + if (error != 1) + return -EINVAL; + + mdelay(200); /* Reboot into bootloader takes a lot of time ... */ + + /* Read back mode */ + error = priv->chip->read_reg(client, REG_GET_MODE, md, sizeof(md)); + if (error) + return error; + /* Check if mode is correct now. */ + if ((cmd_mode == REG_SET_MODE_AP && md[0] == REG_GET_MODE_AP) || + (cmd_mode == REG_SET_MODE_BL && md[0] == REG_GET_MODE_BL)) + return 0; + + return -EINVAL; +} + +static int ili251x_firmware_busy(struct i2c_client *client) +{ + struct ili210x *priv = i2c_get_clientdata(client); + int error, i = 0; + u8 data; + + do { + /* The read_reg already contains suitable delay */ + error = priv->chip->read_reg(client, REG_IC_BUSY, &data, 1); + if (error) + return error; + if (i++ == 100000) + return -ETIMEDOUT; + } while (data != REG_IC_BUSY_NOT_BUSY); + + return 0; +} + +static int ili251x_firmware_write_to_ic(struct device *dev, u8 *fwbuf, + u16 start, u16 end, u8 dataflash) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + u8 cmd_crc = REG_READ_DATA_CRC; + u8 crcrb[4] = { 0 }; + u8 fw_data[33]; + u16 fw_addr; + int error; + + /* + * The DF (dataflash) needs 2 bytes offset for unknown reasons, + * the AC (application) has 2 bytes CRC16-CCITT at the end. + */ + u16 crc = crc_ccitt(0, fwbuf + start + (dataflash ? 2 : 0), + end - start - 2); + + /* Unlock write to either AC (application) or DF (dataflash) area */ + u8 cmd_wr[10] = { + REG_WRITE_ENABLE, 0x5a, 0xa5, dataflash, + (end >> 16) & 0xff, (end >> 8) & 0xff, end & 0xff, + (crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff + }; + + error = i2c_master_send(client, cmd_wr, sizeof(cmd_wr)); + if (error != sizeof(cmd_wr)) + return -EINVAL; + + error = ili251x_firmware_busy(client); + if (error) + return error; + + for (fw_addr = start; fw_addr < end; fw_addr += 32) { + fw_data[0] = REG_WRITE_DATA; + memcpy(&(fw_data[1]), fwbuf + fw_addr, 32); + error = i2c_master_send(client, fw_data, 33); + if (error != sizeof(fw_data)) + return error; + error = ili251x_firmware_busy(client); + if (error) + return error; + } + + error = i2c_master_send(client, &cmd_crc, 1); + if (error != 1) + return -EINVAL; + + error = ili251x_firmware_busy(client); + if (error) + return error; + + error = priv->chip->read_reg(client, REG_READ_DATA_CRC, + &crcrb, sizeof(crcrb)); + if (error) + return error; + + /* Check CRC readback */ + if ((crcrb[0] != (crc & 0xff)) || crcrb[1] != ((crc >> 8) & 0xff)) + return -EINVAL; + + return 0; +} + +static int ili251x_firmware_reset(struct i2c_client *client) +{ + u8 cmd_reset[2] = { 0xf2, 0x01 }; + int error; + + error = i2c_master_send(client, cmd_reset, sizeof(cmd_reset)); + if (error != sizeof(cmd_reset)) + return -EINVAL; + + return ili251x_firmware_busy(client); +} + +static void ili251x_hardware_reset(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + + /* Reset the controller */ + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(10000, 15000); + gpiod_set_value_cansleep(priv->reset_gpio, 0); + msleep(300); +} + +static ssize_t ili210x_firmware_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + const char *fwname = ILI251X_FW_FILENAME; + const struct firmware *fw; + u16 ac_end, df_end; + u8 *fwbuf; + int error; + int i; + + error = request_ihex_firmware(&fw, fwname, dev); + if (error) { + dev_err(dev, "Failed to request firmware %s, error=%d\n", + fwname, error); + return error; + } + + error = ili251x_firmware_to_buffer(fw, &fwbuf, &ac_end, &df_end); + release_firmware(fw); + if (error) + return error; + + /* + * Disable touchscreen IRQ, so that we would not get spurious touch + * interrupt during firmware update, and so that the IRQ handler won't + * trigger and interfere with the firmware update. There is no bit in + * the touch controller to disable the IRQs during update, so we have + * to do it this way here. + */ + disable_irq(client->irq); + + dev_dbg(dev, "Firmware update started, firmware=%s\n", fwname); + + ili251x_hardware_reset(dev); + + error = ili251x_firmware_reset(client); + if (error) + goto exit; + + /* This may not succeed on first try, so re-try a few times. */ + for (i = 0; i < 5; i++) { + error = ili251x_switch_ic_mode(client, REG_SET_MODE_BL); + if (!error) + break; + } + + if (error) + goto exit; + + dev_dbg(dev, "IC is now in BootLoader mode\n"); + + msleep(200); /* The bootloader seems to need some time too. */ + + error = ili251x_firmware_write_to_ic(dev, fwbuf, 0xf000, df_end, 1); + if (error) { + dev_err(dev, "DF firmware update failed, error=%d\n", error); + goto exit; + } + + dev_dbg(dev, "DataFlash firmware written\n"); + + error = ili251x_firmware_write_to_ic(dev, fwbuf, 0x2000, ac_end, 0); + if (error) { + dev_err(dev, "AC firmware update failed, error=%d\n", error); + goto exit; + } + + dev_dbg(dev, "Application firmware written\n"); + + /* This may not succeed on first try, so re-try a few times. */ + for (i = 0; i < 5; i++) { + error = ili251x_switch_ic_mode(client, REG_SET_MODE_AP); + if (!error) + break; + } + + if (error) + goto exit; + + dev_dbg(dev, "IC is now in Application mode\n"); + + error = ili251x_firmware_update_cached_state(dev); + if (error) + goto exit; + + error = count; + +exit: + ili251x_hardware_reset(dev); + dev_dbg(dev, "Firmware update ended, error=%i\n", error); + enable_irq(client->irq); + kfree(fwbuf); + return error; +} + +static DEVICE_ATTR(firmware_update, 0200, NULL, ili210x_firmware_update_store); + static struct attribute *ili210x_attributes[] = { &dev_attr_calibrate.attr, + &dev_attr_firmware_update.attr, + &dev_attr_firmware_version.attr, + &dev_attr_kernel_version.attr, + &dev_attr_protocol_version.attr, + &dev_attr_mode.attr, NULL, }; -static umode_t ili210x_calibrate_visible(struct kobject *kobj, +static umode_t ili210x_attributes_visible(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = kobj_to_dev(kobj); struct i2c_client *client = to_i2c_client(dev); struct ili210x *priv = i2c_get_clientdata(client); - return priv->chip->has_calibrate_reg ? attr->mode : 0; + /* Calibrate is present on all ILI2xxx which have calibrate register */ + if (attr == &dev_attr_calibrate.attr) + return priv->chip->has_calibrate_reg ? attr->mode : 0; + + /* Firmware/Kernel/Protocol/BootMode is implememted only for ILI251x */ + if (!priv->chip->has_firmware_proto) + return 0; + + return attr->mode; } static const struct attribute_group ili210x_attr_group = { .attrs = ili210x_attributes, - .is_visible = ili210x_calibrate_visible, + .is_visible = ili210x_attributes_visible, }; static void ili210x_power_down(void *data) @@ -449,6 +980,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); if (priv->chip->has_pressure_reg) input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xa, 0, 0); + error = ili251x_firmware_update_cached_state(dev); + if (error) { + dev_err(dev, "Unable to cache firmware information, err: %d\n", + error); + return error; + } touchscreen_parse_properties(input, true, &priv->prop); error = input_mt_init_slots(input, priv->chip->max_touches, diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index 4d2d22a86977..3a4952935366 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -37,6 +37,7 @@ #define RM_CMD_BOOT_READ 0x44 /* send wait bl data ready*/ #define RM_BOOT_RDY 0xFF /* bl data ready */ +#define RM_BOOT_CMD_READHWID 0x0E /* read hwid */ /* I2C main commands */ #define RM_CMD_QUERY_BANK 0x2B @@ -290,6 +291,44 @@ static int raydium_i2c_sw_reset(struct i2c_client *client) return 0; } +static int raydium_i2c_query_ts_bootloader_info(struct raydium_data *ts) +{ + struct i2c_client *client = ts->client; + static const u8 get_hwid[] = { RM_BOOT_CMD_READHWID, + 0x10, 0xc0, 0x01, 0x00, 0x04, 0x00 }; + u8 rbuf[5] = { 0 }; + u32 hw_ver; + int error; + + error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, + get_hwid, sizeof(get_hwid)); + if (error) { + dev_err(&client->dev, "WRT HWID command failed: %d\n", error); + return error; + } + + error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, rbuf, 1); + if (error) { + dev_err(&client->dev, "Ack HWID command failed: %d\n", error); + return error; + } + + error = raydium_i2c_read(client, RM_CMD_BOOT_CHK, rbuf, sizeof(rbuf)); + if (error) { + dev_err(&client->dev, "Read HWID command failed: %d (%4ph)\n", + error, rbuf + 1); + hw_ver = 0xffffffffUL; + } else { + hw_ver = get_unaligned_be32(rbuf + 1); + } + + ts->info.hw_ver = cpu_to_le32(hw_ver); + ts->info.main_ver = 0xff; + ts->info.sub_ver = 0xff; + + return error; +} + static int raydium_i2c_query_ts_info(struct raydium_data *ts) { struct i2c_client *client = ts->client; @@ -388,13 +427,10 @@ static int raydium_i2c_initialize(struct raydium_data *ts) if (error) ts->boot_mode = RAYDIUM_TS_BLDR; - if (ts->boot_mode == RAYDIUM_TS_BLDR) { - ts->info.hw_ver = cpu_to_le32(0xffffffffUL); - ts->info.main_ver = 0xff; - ts->info.sub_ver = 0xff; - } else { + if (ts->boot_mode == RAYDIUM_TS_BLDR) + raydium_i2c_query_ts_bootloader_info(ts); + else raydium_i2c_query_ts_info(ts); - } return error; } @@ -1082,11 +1118,11 @@ static int raydium_i2c_probe(struct i2c_client *client, if (error) return error; - error = devm_add_action(&client->dev, raydium_i2c_power_off, ts); + error = devm_add_action_or_reset(&client->dev, + raydium_i2c_power_off, ts); if (error) { dev_err(&client->dev, "failed to install power off action: %d\n", error); - raydium_i2c_power_off(ts); return error; } @@ -1218,7 +1254,7 @@ static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops, raydium_i2c_suspend, raydium_i2c_resume); static const struct i2c_device_id raydium_i2c_id[] = { - { "raydium_i2c" , 0 }, + { "raydium_i2c", 0 }, { "rm32380", 0 }, { /* sentinel */ } }; diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 6abae665ca71..e38ba3e4f183 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -92,7 +92,7 @@ static int st1232_ts_wait_ready(struct st1232_ts_data *ts) unsigned int retries; int error; - for (retries = 10; retries; retries--) { + for (retries = 100; retries; retries--) { error = st1232_ts_read_data(ts, REG_STATUS, 1); if (!error) { switch (ts->read_buf[0]) { @@ -389,6 +389,7 @@ static struct i2c_driver st1232_ts_driver = { .driver = { .name = ST1232_TS_NAME, .of_match_table = st1232_ts_dt_ids, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, .pm = &st1232_ts_pm_ops, }, }; diff --git a/drivers/input/touchscreen/tsc2004.c b/drivers/input/touchscreen/tsc2004.c index 0272cedcc726..9fdd870c4c0b 100644 --- a/drivers/input/touchscreen/tsc2004.c +++ b/drivers/input/touchscreen/tsc2004.c @@ -45,7 +45,9 @@ static int tsc2004_probe(struct i2c_client *i2c, static int tsc2004_remove(struct i2c_client *i2c) { - return tsc200x_remove(&i2c->dev); + tsc200x_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id tsc2004_idtable[] = { diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 923496bbb368..a2f55920b9b2 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -66,7 +66,9 @@ static int tsc2005_probe(struct spi_device *spi) static int tsc2005_remove(struct spi_device *spi) { - return tsc200x_remove(&spi->dev); + tsc200x_remove(&spi->dev); + + return 0; } #ifdef CONFIG_OF diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c index b8d720d52013..27810f6c69f6 100644 --- a/drivers/input/touchscreen/tsc200x-core.c +++ b/drivers/input/touchscreen/tsc200x-core.c @@ -577,15 +577,13 @@ disable_regulator: } EXPORT_SYMBOL_GPL(tsc200x_probe); -int tsc200x_remove(struct device *dev) +void tsc200x_remove(struct device *dev) { struct tsc200x *ts = dev_get_drvdata(dev); sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); regulator_disable(ts->vio); - - return 0; } EXPORT_SYMBOL_GPL(tsc200x_remove); diff --git a/drivers/input/touchscreen/tsc200x-core.h b/drivers/input/touchscreen/tsc200x-core.h index a43c08ccfd3d..4ded34425b21 100644 --- a/drivers/input/touchscreen/tsc200x-core.h +++ b/drivers/input/touchscreen/tsc200x-core.h @@ -74,6 +74,6 @@ extern const struct dev_pm_ops tsc200x_pm_ops; int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, struct regmap *regmap, int (*tsc200x_cmd)(struct device *dev, u8 cmd)); -int tsc200x_remove(struct device *dev); +void tsc200x_remove(struct device *dev); #endif diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index 22826c387da5..fe4ea6204a4e 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -6,6 +6,7 @@ * <tobita.tatsunosuke@wacom.co.jp> */ +#include <linux/bits.h> #include <linux/module.h> #include <linux/input.h> #include <linux/i2c.h> @@ -14,6 +15,15 @@ #include <linux/interrupt.h> #include <asm/unaligned.h> +/* Bitmasks (for data[3]) */ +#define WACOM_TIP_SWITCH BIT(0) +#define WACOM_BARREL_SWITCH BIT(1) +#define WACOM_ERASER BIT(2) +#define WACOM_INVERT BIT(3) +#define WACOM_BARREL_SWITCH_2 BIT(4) +#define WACOM_IN_PROXIMITY BIT(5) + +/* Registers */ #define WACOM_CMD_QUERY0 0x04 #define WACOM_CMD_QUERY1 0x00 #define WACOM_CMD_QUERY2 0x33 @@ -99,19 +109,19 @@ static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) if (error < 0) goto out; - tsw = data[3] & 0x01; - ers = data[3] & 0x04; - f1 = data[3] & 0x02; - f2 = data[3] & 0x10; + tsw = data[3] & WACOM_TIP_SWITCH; + ers = data[3] & WACOM_ERASER; + f1 = data[3] & WACOM_BARREL_SWITCH; + f2 = data[3] & WACOM_BARREL_SWITCH_2; x = le16_to_cpup((__le16 *)&data[4]); y = le16_to_cpup((__le16 *)&data[6]); pressure = le16_to_cpup((__le16 *)&data[8]); if (!wac_i2c->prox) - wac_i2c->tool = (data[3] & 0x0c) ? + wac_i2c->tool = (data[3] & (WACOM_ERASER | WACOM_INVERT)) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - wac_i2c->prox = data[3] & 0x20; + wac_i2c->prox = data[3] & WACOM_IN_PROXIMITY; input_report_key(input, BTN_TOUCH, tsw || ers); input_report_key(input, wac_i2c->tool, wac_i2c->prox); |