From 58ae4004b9c4bb040958cf73986b687a5ea4d85d Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Sun, 5 Sep 2021 18:57:32 -0700 Subject: Input: cpcap-pwrbutton - handle errors from platform_get_irq() The function cpcap_power_button_probe() does not perform sufficient error checking after executing platform_get_irq(), thus fix it. Signed-off-by: Tang Bin Link: https://lore.kernel.org/r/20210802121740.8700-1-tangbin@cmss.chinamobile.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/cpcap-pwrbutton.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/cpcap-pwrbutton.c b/drivers/input/misc/cpcap-pwrbutton.c index 0abef63217e2..372cb44d0635 100644 --- a/drivers/input/misc/cpcap-pwrbutton.c +++ b/drivers/input/misc/cpcap-pwrbutton.c @@ -54,9 +54,13 @@ static irqreturn_t powerbutton_irq(int irq, void *_button) static int cpcap_power_button_probe(struct platform_device *pdev) { struct cpcap_power_button *button; - int irq = platform_get_irq(pdev, 0); + int irq; int err; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL); if (!button) return -ENOMEM; -- cgit v1.2.3 From d5f9c43d41effc3c884e0139ca52015e451039b1 Mon Sep 17 00:00:00 2001 From: "simba.hsu" Date: Tue, 1 Jun 2021 21:38:38 -0700 Subject: Input: raydium_i2c_ts - read device version in bootloader mode Add support reading device ID when controller is in bootloader mode, which may happen if firmware update has been interrupted. Signed-off-by: simba.hsu Link: https://lore.kernel.org/r/20210818063644.8654-1-simba.hsu@rad-ic.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/raydium_i2c_ts.c | 50 +++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index 4d2d22a86977..92c48d8e35a4 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; } @@ -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 */ } }; -- cgit v1.2.3 From daf87bffd02e35387a62c77eb32337c934cf631a Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Fri, 17 Nov 2017 12:31:11 -0800 Subject: Input: palmas-pwrbutton - handle return value of platform_get_irq() platform_get_irq() can fail here and we must check its return value. Signed-off-by: Arvind Yadav Signed-off-by: Dmitry Torokhov --- drivers/input/misc/palmas-pwrbutton.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index 1e1baed63929..f9b05cf09ff5 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -210,6 +210,11 @@ static int palmas_pwron_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work); pwron->irq = platform_get_irq(pdev, 0); + if (pwron->irq < 0) { + error = pwron->irq; + goto err_free_input; + } + error = request_threaded_irq(pwron->irq, NULL, pwron_irq, IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | -- cgit v1.2.3 From de609b56b8322be0cd242ae364dc49bad0dfa89d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 9 Sep 2021 17:59:43 -0700 Subject: Input: ads7846 - add short-hand for spi->dev in probe() function This will make the code a bit more terse. No functional change intended. Signed-off-by: Daniel Mack Reviewed-by: Marco Felsch Link: https://lore.kernel.org/r/20210907200726.2034962-2-daniel@zonque.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 45 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index f113a27aeb1e..ce2e14816af5 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1216,20 +1216,21 @@ 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; } @@ -1262,9 +1263,9 @@ 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); + pdata = ads7846_probe_dt(dev); if (IS_ERR(pdata)) { err = PTR_ERR(pdata); goto err_free_mem; @@ -1306,12 +1307,12 @@ 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->dev.parent = dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); @@ -1345,16 +1346,16 @@ static int ads7846_probe(struct spi_device *spi) ads7846_setup_spi_msg(ts, pdata); - ts->reg = regulator_get(&spi->dev, "vcc"); + ts->reg = 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); + dev_err(dev, "unable to get regulator: %d\n", err); goto err_free_gpio; } err = regulator_enable(ts->reg); if (err) { - dev_err(&spi->dev, "unable to enable regulator: %d\n", err); + dev_err(dev, "unable to enable regulator: %d\n", err); goto err_put_regulator; } @@ -1362,18 +1363,18 @@ static int ads7846_probe(struct spi_device *spi) irq_flags |= IRQF_ONESHOT; err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq, - irq_flags, spi->dev.driver->name, ts); + irq_flags, dev->driver->name, ts); if (err && !pdata->irq_flags) { - dev_info(&spi->dev, + 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); + irq_flags, dev->driver->name, ts); } if (err) { - dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + dev_dbg(dev, "irq %d busy?\n", spi->irq); goto err_disable_regulator; } @@ -1381,18 +1382,18 @@ static int ads7846_probe(struct spi_device *spi) if (err) goto err_free_irq; - 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 = sysfs_create_group(&dev->kobj, &ads784x_attr_group); if (err) goto err_remove_hwmon; @@ -1400,19 +1401,19 @@ static int ads7846_probe(struct spi_device *spi) if (err) goto err_remove_attr_group; - 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); + sysfs_remove_group(&dev->kobj, &ads784x_attr_group); err_remove_hwmon: ads784x_hwmon_unregister(spi, ts); err_free_irq: -- cgit v1.2.3 From 937f5d5ec642501d2dd3c91918685de30a932b34 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 9 Sep 2021 18:01:28 -0700 Subject: Input: ads7846 - remove custom filter handling functions from pdata The functions in the platform data struct to initialize, cleanup and apply custom filters are not in use by any mainline board. Remove support for them to pave the road for more cleanups to come. The enum was moved as it has no users outside of the driver code itself. Signed-off-by: Daniel Mack Reviewed-by: Marco Felsch Link: https://lore.kernel.org/r/20210907200726.2034962-3-daniel@zonque.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 25 ++++++++----------------- include/linux/spi/ads7846.h | 15 --------------- 2 files changed, 8 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ce2e14816af5..b9c8496155b7 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -142,13 +142,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) @@ -1277,15 +1282,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; @@ -1299,7 +1296,7 @@ static int ads7846_probe(struct spi_device *spi) err = ads7846_setup_pendown(spi, ts, pdata); if (err) - goto err_cleanup_filter; + goto err_free_mem; if (pdata->penirq_recheck_delay_usecs) ts->penirq_recheck_delay_usecs = @@ -1425,9 +1422,6 @@ static int ads7846_probe(struct spi_device *spi) 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); @@ -1458,9 +1452,6 @@ static int ads7846_remove(struct spi_device *spi) gpio_free(ts->gpio_pendown); } - if (ts->filter_cleanup) - ts->filter_cleanup(ts->filter_data); - kfree(ts->packet); kfree(ts); diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 1a5eaef3b7f2..d424c1aadf38 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -1,17 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* linux/spi/ads7846.h */ -/* Touchscreen characteristics vary between boards and models. The - * platform_data for the device's "struct device" holds this information. - * - * It's OK if the min/max values are zero. - */ -enum ads7846_filter { - ADS7846_FILTER_OK, - ADS7846_FILTER_REPEAT, - ADS7846_FILTER_IGNORE, -}; - struct ads7846_platform_data { u16 model; /* 7843, 7845, 7846, 7873. */ u16 vref_delay_usecs; /* 0 for external vref; etc */ @@ -51,10 +40,6 @@ struct ads7846_platform_data { int gpio_pendown_debounce; /* platform specific debounce time for * the gpio_pendown */ int (*get_pendown_state)(void); - int (*filter_init) (const struct ads7846_platform_data *pdata, - void **filter_data); - int (*filter) (void *filter_data, int data_idx, int *val); - void (*filter_cleanup)(void *filter_data); void (*wait_for_sync)(void); bool wakeup; unsigned long irq_flags; -- cgit v1.2.3 From 845ef3a7ce5773638a8c5e9a6e7be6538c107ae1 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 9 Sep 2021 18:03:01 -0700 Subject: Input: ads7846 - switch to devm initialization This simplies the code a lot and fixes some potential resource leaks in the error return paths. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20210907200726.2034962-4-daniel@zonque.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 136 +++++++++++++----------------------- 1 file changed, 47 insertions(+), 89 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index b9c8496155b7..eaa8714ad19d 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; @@ -554,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: @@ -574,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 @@ -593,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, @@ -1019,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", @@ -1217,6 +1204,11 @@ 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; @@ -1251,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); @@ -1271,10 +1267,8 @@ static int ads7846_probe(struct spi_device *spi) pdata = dev_get_platdata(dev); if (!pdata) { pdata = ads7846_probe_dt(dev); - if (IS_ERR(pdata)) { - err = PTR_ERR(pdata); - goto err_free_mem; - } + if (IS_ERR(pdata)) + return PTR_ERR(pdata); } ts->model = pdata->model ? : 7846; @@ -1296,7 +1290,7 @@ static int ads7846_probe(struct spi_device *spi) err = ads7846_setup_pendown(spi, ts, pdata); if (err) - goto err_free_mem; + return err; if (pdata->penirq_recheck_delay_usecs) ts->penirq_recheck_delay_usecs = @@ -1309,7 +1303,6 @@ static int ads7846_probe(struct spi_device *spi) input_dev->name = ts->name; input_dev->phys = ts->phys; - input_dev->dev.parent = dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); @@ -1343,41 +1336,47 @@ static int ads7846_probe(struct spi_device *spi) ads7846_setup_spi_msg(ts, pdata); - ts->reg = regulator_get(dev, "vcc"); + ts->reg = devm_regulator_get(dev, "vcc"); if (IS_ERR(ts->reg)) { err = PTR_ERR(ts->reg); dev_err(dev, "unable to get regulator: %d\n", err); - goto err_free_gpio; + return err; } err = regulator_enable(ts->reg); if (err) { dev_err(dev, "unable to enable regulator: %d\n", err); - goto err_put_regulator; + 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, 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 && !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, 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(dev, "irq %d busy?\n", spi->irq); - goto err_disable_regulator; + return err; } err = ads784x_hwmon_register(spi, ts); if (err) - goto err_free_irq; + return err; dev_info(dev, "touchscreen, irq %d\n", spi->irq); @@ -1390,13 +1389,13 @@ static int ads7846_probe(struct spi_device *spi) else (void) ads7846_read12_ser(dev, READ_12BIT_SER(vaux)); - err = sysfs_create_group(&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(dev, pdata->wakeup); @@ -1408,54 +1407,13 @@ static int ads7846_probe(struct spi_device *spi) devm_kfree(dev, (void *)pdata); return 0; - - err_remove_attr_group: - sysfs_remove_group(&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_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); - } - - kfree(ts->packet); - kfree(ts); - - dev_dbg(&spi->dev, "unregistered touchscreen\n"); + ads7846_stop(ts); return 0; } -- cgit v1.2.3 From fcc28e0bfcfd2d98f2d096a185f0263759661c94 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 9 Sep 2021 22:12:29 -0700 Subject: Input: cypress-sf - add Cypress StreetFighter touchkey driver This adds support for Cypress StreetFighter touchkey controllers such as sf3155. This driver supports managing regulators and generating input events. Due to lack of documentation, this driver is entirely based on information gathered from a driver written for an old Android kernel fork[1][2]. [1] https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c [2] https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a4-msm8996-mtp.dtsi#L291-L314 Signed-off-by: Yassine Oudjana Link: https://lore.kernel.org/r/20210907174341.422013-2-y.oudjana@protonmail.com Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 6 + drivers/input/keyboard/Kconfig | 10 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/cypress-sf.c | 224 ++++++++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+) create mode 100644 drivers/input/keyboard/cypress-sf.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index b03c68cebe4c..f72af66b2889 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5006,6 +5006,12 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/cy8ctma140.c +CYPRESS STREETFIGHTER TOUCHKEYS DRIVER +M: Yassine Oudjana +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/input/keyboard/cypress-sf.c + CYTTSP TOUCHSCREEN DRIVER M: Linus Walleij L: linux-input@vger.kernel.org diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 1b0afc8bf841..d3bf5c3f9add 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -791,4 +791,14 @@ config KEYBOARD_MTK_PMIC To compile this driver as a module, choose M here: the module will be called pmic-keys. +config KEYBOARD_CYPRESS_SF + tristate "Cypress StreetFighter touchkey support" + depends on I2C + help + Say Y here if you want to enable support for Cypress StreetFighter + touchkeys. + + To compile this driver as a module, choose M here: the + module will be called cypress-sf. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1d689fdd5c00..e3c8648f834e 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o +obj-$(CONFIG_KEYBOARD_CYPRESS_SF) += cypress-sf.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_DLINK_DIR685) += dlink-dir685-touchkeys.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o diff --git a/drivers/input/keyboard/cypress-sf.c b/drivers/input/keyboard/cypress-sf.c new file mode 100644 index 000000000000..c28996028e80 --- /dev/null +++ b/drivers/input/keyboard/cypress-sf.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cypress StreetFighter Touchkey Driver + * + * Copyright (c) 2021 Yassine Oudjana + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CYPRESS_SF_DEV_NAME "cypress-sf" + +#define CYPRESS_SF_REG_BUTTON_STATUS 0x4a + +struct cypress_sf_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator_bulk_data regulators[2]; + u32 *keycodes; + unsigned long keystates; + int num_keys; +}; + +static irqreturn_t cypress_sf_irq_handler(int irq, void *devid) +{ + struct cypress_sf_data *touchkey = devid; + unsigned long keystates, changed; + bool new_state; + int val, key; + + val = i2c_smbus_read_byte_data(touchkey->client, + CYPRESS_SF_REG_BUTTON_STATUS); + if (val < 0) { + dev_err(&touchkey->client->dev, + "Failed to read button status: %d", val); + return IRQ_NONE; + } + keystates = val; + + bitmap_xor(&changed, &keystates, &touchkey->keystates, + touchkey->num_keys); + + for_each_set_bit(key, &changed, touchkey->num_keys) { + new_state = keystates & BIT(key); + dev_dbg(&touchkey->client->dev, + "Key %d changed to %d", key, new_state); + input_report_key(touchkey->input_dev, + touchkey->keycodes[key], new_state); + } + + input_sync(touchkey->input_dev); + touchkey->keystates = keystates; + + return IRQ_HANDLED; +} + +static int cypress_sf_probe(struct i2c_client *client) +{ + struct cypress_sf_data *touchkey; + int key, error; + + touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL); + if (!touchkey) + return -ENOMEM; + + touchkey->client = client; + i2c_set_clientdata(client, touchkey); + + touchkey->regulators[0].supply = "vdd"; + touchkey->regulators[1].supply = "avdd"; + + error = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); + if (error) { + dev_err(&client->dev, "Failed to get regulators: %d\n", error); + return error; + } + + touchkey->num_keys = device_property_read_u32_array(&client->dev, + "linux,keycodes", + NULL, 0); + if (touchkey->num_keys < 0) { + /* Default key count */ + touchkey->num_keys = 2; + } + + touchkey->keycodes = devm_kcalloc(&client->dev, + touchkey->num_keys, + sizeof(*touchkey->keycodes), + GFP_KERNEL); + if (!touchkey->keycodes) + return -ENOMEM; + + error = device_property_read_u32_array(&client->dev, "linux,keycodes", + touchkey->keycodes, + touchkey->num_keys); + + if (error) { + dev_warn(&client->dev, + "Failed to read keycodes: %d, using defaults\n", + error); + + /* Default keycodes */ + touchkey->keycodes[0] = KEY_BACK; + touchkey->keycodes[1] = KEY_MENU; + } + + error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); + if (error) { + dev_err(&client->dev, + "Failed to enable regulators: %d\n", error); + return error; + } + + touchkey->input_dev = devm_input_allocate_device(&client->dev); + if (!touchkey->input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + touchkey->input_dev->name = CYPRESS_SF_DEV_NAME; + touchkey->input_dev->id.bustype = BUS_I2C; + + for (key = 0; key < touchkey->num_keys; ++key) + input_set_capability(touchkey->input_dev, + EV_KEY, touchkey->keycodes[key]); + + error = input_register_device(touchkey->input_dev); + if (error) { + dev_err(&client->dev, + "Failed to register input device: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, cypress_sf_irq_handler, + IRQF_ONESHOT, + CYPRESS_SF_DEV_NAME, touchkey); + if (error) { + dev_err(&client->dev, + "Failed to register threaded irq: %d", error); + return error; + } + + return 0; +}; + +static int __maybe_unused cypress_sf_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cypress_sf_data *touchkey = i2c_get_clientdata(client); + int error; + + disable_irq(client->irq); + + error = regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); + if (error) { + dev_err(dev, "Failed to disable regulators: %d", error); + enable_irq(client->irq); + return error; + } + + return 0; +} + +static int __maybe_unused cypress_sf_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cypress_sf_data *touchkey = i2c_get_clientdata(client); + int error; + + error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); + if (error) { + dev_err(dev, "Failed to enable regulators: %d", error); + return error; + } + + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops, + cypress_sf_suspend, cypress_sf_resume); + +static struct i2c_device_id cypress_sf_id_table[] = { + { CYPRESS_SF_DEV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id cypress_sf_of_match[] = { + { .compatible = "cypress,sf3155", }, + { }, +}; +MODULE_DEVICE_TABLE(of, cypress_sf_of_match); +#endif + +static struct i2c_driver cypress_sf_driver = { + .driver = { + .name = CYPRESS_SF_DEV_NAME, + .pm = &cypress_sf_pm_ops, + .of_match_table = of_match_ptr(cypress_sf_of_match), + }, + .id_table = cypress_sf_id_table, + .probe_new = cypress_sf_probe, +}; +module_i2c_driver(cypress_sf_driver); + +MODULE_AUTHOR("Yassine Oudjana "); +MODULE_DESCRIPTION("Cypress StreetFighter Touchkey Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From cef6f5cc140852fcb6c75f85b8e6ba00d7de1bad Mon Sep 17 00:00:00 2001 From: Len Baker Date: Sat, 11 Sep 2021 15:55:27 -0700 Subject: Input: omap-keypad - prefer struct_size over open coded arithmetic As noted in the "Deprecated Interfaces, Language Features, Attributes, and Conventions" documentation [1], size calculations (especially multiplication) should not be performed in memory allocator (or similar) function arguments due to the risk of them overflowing. This could lead to values wrapping around and a smaller allocation being made than the caller was expecting. Using those allocations could lead to linear overflows of heap memory and other misbehaviors. So, use the struct_size() helper to do the arithmetic instead of the argument "size + count * size" in the kzalloc() function. [1] https://www.kernel.org/doc/html/v5.14/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments Signed-off-by: Len Baker Link: https://lore.kernel.org/r/20210911112716.10067-1-len.baker@gmx.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index dbe836c7ff47..eb3a687796e7 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -190,8 +190,7 @@ static int omap_kp_probe(struct platform_device *pdev) row_shift = get_count_order(pdata->cols); keycodemax = pdata->rows << row_shift; - omap_kp = kzalloc(sizeof(struct omap_kp) + - keycodemax * sizeof(unsigned short), GFP_KERNEL); + omap_kp = kzalloc(struct_size(omap_kp, keymap, keycodemax), GFP_KERNEL); input_dev = input_allocate_device(); if (!omap_kp || !input_dev) { kfree(omap_kp); -- cgit v1.2.3 From f1c80ba0cc8e7ae015a4b4828564e22f0b583ad5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 20 Sep 2021 18:15:07 -0700 Subject: Input: tmdc - fix spelling mistake "Millenium" -> "Millennium" There is a spelling mistake in the name of a joystick. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20210920184748.18519-1-colin.king@canonical.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/tmdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index f89e9aa6d328..7416de84b955 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -83,7 +83,7 @@ static const struct tmdc_model { const signed char *axes; const short *buttons; } tmdc_models[] = { - { 1, "ThrustMaster Millenium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy }, + { 1, "ThrustMaster Millennium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy }, { 3, "ThrustMaster Rage 3D Gamepad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad }, { 4, "ThrustMaster Attack Throttle", 5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at }, { 8, "ThrustMaster FragMaster", 4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm }, -- cgit v1.2.3 From 31ae0102a34ed863c7d32b10e768036324991679 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Sep 2021 21:08:02 -0700 Subject: Input: goodix - change goodix_i2c_write() len parameter type to int Change the type of the goodix_i2c_write() len parameter to from 'unsigned' to 'int' to avoid bare use of 'unsigned', changing it to 'int' makes goodix_i2c_write()' prototype consistent with goodix_i2c_read(). Reviewed-by: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210920150643.155872-2-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index c682b028f0a2..00536064f45e 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -290,7 +290,7 @@ static int goodix_i2c_read(struct i2c_client *client, * @len: length of the buffer to write */ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, - unsigned len) + int len) { u8 *addr_buf; struct i2c_msg msg; -- cgit v1.2.3 From a2233cb7b65a017067e2f2703375ecc930a0ab30 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Sep 2021 21:08:18 -0700 Subject: Input: goodix - add a goodix.h header file Add a goodix.h header file, and move the register definitions, and struct declarations there and add prototypes for various helper functions. This is a preparation patch for adding support for controllers without flash, which need to have their firmware uploaded and need some other special handling too. Since MAINTAINERS needs updating because of this change anyways, also add myself as co-maintainer. Reviewed-by: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210920150643.155872-3-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 3 +- drivers/input/touchscreen/goodix.c | 74 ++++---------------------------------- drivers/input/touchscreen/goodix.h | 73 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 69 deletions(-) create mode 100644 drivers/input/touchscreen/goodix.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 44e6c428703e..7ab4a058fe4d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7716,9 +7716,10 @@ F: drivers/media/usb/go7007/ GOODIX TOUCHSCREEN M: Bastien Nocera +M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained -F: drivers/input/touchscreen/goodix.c +F: drivers/input/touchscreen/goodix* GOOGLE ETHERNET DRIVERS M: Catherine Sullivan diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 00536064f45e..76e93bc8389a 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -14,20 +14,15 @@ #include #include #include -#include -#include -#include -#include -#include #include #include #include #include -#include #include #include #include #include +#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); @@ -289,8 +228,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, - int len) +int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len) { u8 *addr_buf; struct i2c_msg msg; @@ -314,7 +252,7 @@ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, return ret < 0 ? ret : (ret != 1 ? -EIO : 0); } -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)); } @@ -598,7 +536,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; @@ -696,7 +634,7 @@ 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; diff --git a/drivers/input/touchscreen/goodix.h b/drivers/input/touchscreen/goodix.h new file mode 100644 index 000000000000..cdaced4f2980 --- /dev/null +++ b/drivers/input/touchscreen/goodix.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __GOODIX_H__ +#define __GOODIX_H__ + +#include +#include +#include +#include +#include +#include + +/* Register defines */ +#define GOODIX_REG_COMMAND 0x8040 +#define GOODIX_CMD_SCREEN_OFF 0x05 + +#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_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; + 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]; +}; + +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); + +#endif -- cgit v1.2.3 From 209bda4741f68f102cf2f272227bfc938e387b51 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Sep 2021 21:08:26 -0700 Subject: Input: goodix - refactor reset handling Refactor reset handling a bit, change the main reset handler into a new goodix_reset_no_int_sync() helper and add a goodix_reset() wrapper which calls goodix_int_sync() separately. Also push the dev_err() call on reset failure into the goodix_reset_no_int_sync() and goodix_int_sync() functions, so that we don't need to have separate dev_err() calls in all their callers. This is a preparation patch for adding support for controllers without flash, which need to have their firmware uploaded and need some other special handling too. Reviewed-by: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210920150643.155872-4-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 48 +++++++++++++++++++++++++------------- drivers/input/touchscreen/goodix.h | 1 + 2 files changed, 33 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 76e93bc8389a..e5ec64036f06 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -640,56 +640,76 @@ int goodix_int_sync(struct goodix_ts_data *ts) 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 @@ -1195,10 +1215,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); @@ -1340,10 +1358,8 @@ static int __maybe_unused goodix_resume(struct device *dev) 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 index cdaced4f2980..0b88554ba2ae 100644 --- a/drivers/input/touchscreen/goodix.h +++ b/drivers/input/touchscreen/goodix.h @@ -69,5 +69,6 @@ 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); #endif -- cgit v1.2.3 From 7642f29c731e383623d368a234a99ff9fb2eb97b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Sep 2021 21:08:35 -0700 Subject: Input: goodix - push error logging up into i2c_read and i2c_write helpers Make the goodix_i2c_read() and goodix_i2c_write*() helpers log errors themselves. This allows removing all the error logging from their callers. This already results in a nice cleanup with the current code and it also helps to make the upcoming support for controllers without flash cleaner. Reviewed-by: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210920150643.155872-5-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 53 +++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index e5ec64036f06..f91a92c998a2 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -217,7 +217,13 @@ int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len) 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; } /** @@ -248,8 +254,15 @@ int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len) 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; } int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) @@ -291,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; @@ -418,9 +428,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; } @@ -545,11 +553,9 @@ 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 */ @@ -937,8 +943,6 @@ static void goodix_read_config(struct goodix_ts_data *ts) 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; @@ -969,10 +973,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; @@ -998,13 +1000,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); } @@ -1232,10 +1231,8 @@ reset: } 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); @@ -1306,7 +1303,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; @@ -1349,10 +1345,7 @@ 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]); -- cgit v1.2.3 From 20e317222eeabd74e9ff76cf4daf85f961f608dc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Sep 2021 21:08:44 -0700 Subject: Input: goodix - allow specifying the config filename The config which needs to be send to the controller on some device-models is model-specific. Allow specifying a model-specific filename through a device-property, rather then always using a fixed filename. Note the "goodix,config-name" device-property used by this is *not* documented in the Documentation/devicetree/bindings/input/touchscreen/goodix.yaml device-tree bindings for now. Not documenting these is intentional. This is done because this code has only been tested on x86/ACPI so far, where devicetree is not used. Instead these properties are set through a software-fwnode attached to the device by the drivers/platform/x86/touchscreen_dmi.c code. This means that the use of this property for now is purely a kernel-internal thing and the name/working of the property may still be changed for now. Reviewed-by: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210920150643.155872-6-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 14 ++++++++++---- drivers/input/touchscreen/goodix.h | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f91a92c998a2..75c53786516f 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -1165,6 +1165,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); @@ -1238,10 +1239,15 @@ reset: 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, diff --git a/drivers/input/touchscreen/goodix.h b/drivers/input/touchscreen/goodix.h index 0b88554ba2ae..b1d1bc004091 100644 --- a/drivers/input/touchscreen/goodix.h +++ b/drivers/input/touchscreen/goodix.h @@ -52,8 +52,8 @@ struct goodix_ts_data { int gpio_count; int gpio_int_idx; char id[GOODIX_ID_MAX_LEN + 1]; + char cfg_name[64]; u16 version; - const char *cfg_name; bool reset_controller_at_probe; bool load_cfg_from_disk; struct completion firmware_loading_complete; -- cgit v1.2.3 From 09182ed20c04d1b3a3a0d232d7748e745a438acd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Sep 2021 21:08:53 -0700 Subject: Input: goodix - add support for controllers without flash Some Goodix touchscreen controllers, such as for example the GT912, don't have flash-storage for their firmware. These models require the OS to load the firmware at runtime, as well as some other special handling. Add support for this to the goodix driver. This patch was developed and tested on a Glavey TM800A550L tablet. Note the "goodix,main-clk" and "firmware-name" device-properties used by the new code are *not* documented in the Documentation/devicetree/bindings/input/touchscreen/goodix.yaml device-tree bindings for now. Not documenting these is intentional. This is done because this code has only been tested on x86/ACPI so far, where devicetree is not used. Instead these properties are set through a software-fwnode attached to the device by the drivers/platform/x86/touchscreen_dmi.c code. This means that the use of this properties for now is purely a kernel-internal thing and the name/working of the properties may still be changed for now. Reviewed-by: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210920150643.155872-7-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Makefile | 3 +- drivers/input/touchscreen/goodix.c | 42 ++- drivers/input/touchscreen/goodix.h | 43 +++ drivers/input/touchscreen/goodix_fwupload.c | 427 ++++++++++++++++++++++++++++ 4 files changed, 507 insertions(+), 8 deletions(-) create mode 100644 drivers/input/touchscreen/goodix_fwupload.c (limited to 'drivers') 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/goodix.c b/drivers/input/touchscreen/goodix.c index 75c53786516f..906b5a6b52d1 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -326,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)); @@ -940,12 +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) { - 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; @@ -1139,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) @@ -1231,6 +1252,10 @@ reset: return error; } + error = goodix_firmware_check(ts); + if (error) + return error; + error = goodix_read_version(ts); if (error) return error; @@ -1297,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) { diff --git a/drivers/input/touchscreen/goodix.h b/drivers/input/touchscreen/goodix.h index b1d1bc004091..62138f930d1a 100644 --- a/drivers/input/touchscreen/goodix.h +++ b/drivers/input/touchscreen/goodix.h @@ -10,13 +10,48 @@ #include /* 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 @@ -42,6 +77,7 @@ 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; @@ -62,6 +98,9 @@ struct goodix_ts_data { 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); @@ -71,4 +110,8 @@ 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 + * + * 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 +#include +#include +#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; + } +} -- cgit v1.2.3 From b083704fbf6cea21a187a99f6dae20fbca86b44e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:39:42 -0700 Subject: Input: elants_i2c - make use of devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and if devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210922125212.95-2-caihuoqing@baidu.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elants_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 17540bdb1eaf..98ba5cab685b 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -1440,11 +1440,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; } -- cgit v1.2.3 From 4b3ed1ae2817d78e72a4281be2a9751a41f20508 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:49:14 -0700 Subject: Input: raydium_i2c_ts - make use of the helper function devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and if devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210922125212.95-3-caihuoqing@baidu.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/raydium_i2c_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index 92c48d8e35a4..3a4952935366 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -1118,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; } -- cgit v1.2.3 From d5af8a8f7c4c54758e89fa3b077b62270de3910c Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:50:49 -0700 Subject: Input: mpr121 - make use of the helper function devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and if devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210922125954.533-1-caihuoqing@baidu.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mpr121_touchkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 40d6e5087cde..230ab3d50b9e 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -107,9 +107,9 @@ static struct regulator *mpr121_vdd_supply_init(struct device *dev) return ERR_PTR(err); } - err = devm_add_action(dev, mpr121_vdd_supply_disable, vdd_supply); + err = devm_add_action_or_reset(dev, mpr121_vdd_supply_disable, + vdd_supply); if (err) { - regulator_disable(vdd_supply); dev_err(dev, "failed to add disable regulator action: %d\n", err); return ERR_PTR(err); -- cgit v1.2.3 From 5c4c2c8e6fac26fa0b80c234d6e9f75d637193af Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 1 Oct 2021 21:20:49 -0700 Subject: Input: ariel-pwrbutton - add SPI device ID table Currently autoloading for SPI devices does not use the DT ID table, it uses SPI modalises. Supporting OF modalises is going to be difficult if not impractical, an attempt was made but has been reverted, so ensure that module autoloading works for this driver by adding a SPI device ID table. Fixes: 96c8395e2166 ("spi: Revert modalias changes") Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210927134104.38648-1-broonie@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ariel-pwrbutton.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/input/misc/ariel-pwrbutton.c b/drivers/input/misc/ariel-pwrbutton.c index 17bbaac8b80c..cdc80715b5fd 100644 --- a/drivers/input/misc/ariel-pwrbutton.c +++ b/drivers/input/misc/ariel-pwrbutton.c @@ -149,12 +149,19 @@ static const struct of_device_id ariel_pwrbutton_of_match[] = { }; MODULE_DEVICE_TABLE(of, ariel_pwrbutton_of_match); +static const struct spi_device_id ariel_pwrbutton_spi_ids[] = { + { .name = "wyse-ariel-ec-input" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ariel_pwrbutton_spi_ids); + static struct spi_driver ariel_pwrbutton_driver = { .driver = { .name = "dell-wyse-ariel-ec-input", .of_match_table = ariel_pwrbutton_of_match, }, .probe = ariel_pwrbutton_probe, + .id_table = ariel_pwrbutton_spi_ids, }; module_spi_driver(ariel_pwrbutton_driver); -- cgit v1.2.3 From 1f59342be6c075a9520679981c4cfd08bb26c659 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 5 Oct 2021 13:27:26 -0700 Subject: Input: analog - fix invalid snprintf() call Overlapping input and output arguments to snprintf() are undefined behavior in C99: drivers/input/joystick/analog.c: In function 'analog_name': drivers/input/joystick/analog.c:428:3: error: 'snprintf' argument 4 overlaps destination object 'analog' [-Werror=restrict] 428 | snprintf(analog->name, sizeof(analog->name), "%s %d-hat", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 429 | analog->name, hweight16(analog->mask & ANALOG_HATS_ALL)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/input/joystick/analog.c:420:40: note: destination object referenced by 'restrict'-qualified argument 1 was declared here 420 | static void analog_name(struct analog *analog) | ~~~~~~~~~~~~~~~^~~~~~ Change this function to use the simpler seq_buf interface instead. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20210927101416.1569609-1-arnd@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/analog.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index f798922a4598..d9f866ffde6b 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -419,23 +420,24 @@ static void analog_calibrate_timer(struct analog_port *port) static void analog_name(struct analog *analog) { - snprintf(analog->name, sizeof(analog->name), "Analog %d-axis %d-button", + struct seq_buf s; + + seq_buf_init(&s, analog->name, sizeof(analog->name)); + seq_buf_printf(&s, "Analog %d-axis %d-button", hweight8(analog->mask & ANALOG_AXES_STD), hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 + hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4); if (analog->mask & ANALOG_HATS_ALL) - snprintf(analog->name, sizeof(analog->name), "%s %d-hat", - analog->name, hweight16(analog->mask & ANALOG_HATS_ALL)); + seq_buf_printf(&s, " %d-hat", + hweight16(analog->mask & ANALOG_HATS_ALL)); if (analog->mask & ANALOG_HAT_FCS) - strlcat(analog->name, " FCS", sizeof(analog->name)); + seq_buf_printf(&s, " FCS"); if (analog->mask & ANALOG_ANY_CHF) - strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF", - sizeof(analog->name)); + seq_buf_printf(&s, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF"); - strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick", - sizeof(analog->name)); + seq_buf_printf(&s, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick"); } /* -- cgit v1.2.3 From 2667f6b7af99e81958fa97c03bb519fcb09d0055 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Wed, 6 Oct 2021 11:06:03 -0700 Subject: Input: st1232 - increase "wait ready" timeout I have a ST1633 touch controller which fails to probe due to a timeout waiting for the controller to become ready. Increasing the minimum delay to 100ms ensures that the probe sequence completes successfully. The ST1633 datasheet says nothing about the maximum delay here and the ST1232 I2C protocol document says "wait until" with no notion of a timeout. Since this only runs once during probe, being generous with the timout seems reasonable and most likely the device will become ready eventually. (It may be worth noting that I saw this issue with a PREEMPT_RT patched kernel which probably has tighter wakeups from usleep_range() than other preemption models.) Fixes: f605be6a57b4 ("Input: st1232 - wait until device is ready before reading resolution") Signed-off-by: John Keeping Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20210929152609.2421483-1-john@metanate.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 6abae665ca71..9d1dea6996a2 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]) { -- cgit v1.2.3 From b415ed4f49b90655659479fa7c5ddaffe88e41b9 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 11 Oct 2021 18:31:40 -0700 Subject: Input: st1232 - prefer asynchronous probing The device may take up to 100ms to become responsive during probe, so prefer asynchronous probing to avoid delaying the rest of the system. Suggested-by: Dmitry Torokhov Signed-off-by: John Keeping Link: https://lore.kernel.org/r/20211007111217.1935858-1-john@metanate.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 9d1dea6996a2..e38ba3e4f183 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -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, }, }; -- cgit v1.2.3 From af98ff045f1e0bed4caa28741266430af9724eb2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Oct 2021 19:37:44 -0700 Subject: Input: adxl34x - make adxl34x_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now adxl34x_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211012153945.2651412-6-u.kleine-koenig@pengutronix.de Signed-off-by: Dmitry Torokhov --- drivers/input/misc/adxl34x-i2c.c | 4 +++- drivers/input/misc/adxl34x-spi.c | 4 +++- drivers/input/misc/adxl34x.c | 4 +--- drivers/input/misc/adxl34x.h | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index e64368a63346..a3b5f88d2bd1 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -103,7 +103,9 @@ static int adxl34x_i2c_remove(struct i2c_client *client) { struct adxl34x *ac = i2c_get_clientdata(client); - return adxl34x_remove(ac); + adxl34x_remove(ac); + + return 0; } static int __maybe_unused adxl34x_i2c_suspend(struct device *dev) diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index df6afa455e46..6e51c9bc619f 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -91,7 +91,9 @@ static int adxl34x_spi_remove(struct spi_device *spi) { struct adxl34x *ac = spi_get_drvdata(spi); - return adxl34x_remove(ac); + adxl34x_remove(ac); + + return 0; } static int __maybe_unused adxl34x_spi_suspend(struct device *dev) diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 4cc4e8ff42b3..34beac80e6f0 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -896,15 +896,13 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, } EXPORT_SYMBOL_GPL(adxl34x_probe); -int adxl34x_remove(struct adxl34x *ac) +void adxl34x_remove(struct adxl34x *ac) { sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group); free_irq(ac->irq, ac); input_unregister_device(ac->input); dev_dbg(ac->dev, "unregistered accelerometer\n"); kfree(ac); - - return 0; } EXPORT_SYMBOL_GPL(adxl34x_remove); diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h index 83a0eeccf613..febf85270fff 100644 --- a/drivers/input/misc/adxl34x.h +++ b/drivers/input/misc/adxl34x.h @@ -25,6 +25,6 @@ void adxl34x_resume(struct adxl34x *ac); struct adxl34x *adxl34x_probe(struct device *dev, int irq, bool fifo_delay_default, const struct adxl34x_bus_ops *bops); -int adxl34x_remove(struct adxl34x *ac); +void adxl34x_remove(struct adxl34x *ac); #endif -- cgit v1.2.3 From 39e4e75a9f1cc4c448e965c14962a62c803922e3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Oct 2021 19:38:36 -0700 Subject: Input: tsc200x - make tsc200x_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now tsc200x_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211012153945.2651412-7-u.kleine-koenig@pengutronix.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2004.c | 4 +++- drivers/input/touchscreen/tsc2005.c | 4 +++- drivers/input/touchscreen/tsc200x-core.c | 4 +--- drivers/input/touchscreen/tsc200x-core.h | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') 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 -- cgit v1.2.3 From 03b47b3ad0a9dc4a33b9e4528f4eac74ee781e63 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 11 Oct 2021 18:37:32 -0700 Subject: Input: ep93xx_keypad - annotate suspend/resume as __maybe_unused Instead of guarding suspend/resume methods with #ifdef CONFIG_PM let's mark them as __maybe_unused as this allows better compile coverage. Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20211012013735.3523140-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ep93xx_keypad.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index e0e931e796fa..a0c6cdf8e0d3 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -175,8 +175,7 @@ static void ep93xx_keypad_close(struct input_dev *pdev) } -#ifdef CONFIG_PM_SLEEP -static int ep93xx_keypad_suspend(struct device *dev) +static int __maybe_unused ep93xx_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); @@ -197,7 +196,7 @@ static int ep93xx_keypad_suspend(struct device *dev) return 0; } -static int ep93xx_keypad_resume(struct device *dev) +static int __maybe_unused ep93xx_keypad_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); @@ -220,7 +219,6 @@ static int ep93xx_keypad_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops, ep93xx_keypad_suspend, ep93xx_keypad_resume); -- cgit v1.2.3 From 4ce73b052bdd64f6edba86d3a4e8608cca78c105 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 11 Oct 2021 18:37:33 -0700 Subject: Input: ep93xx_keypad - use BIT() and GENMASK() macros Also drop parenthesis around macros that do not use expressions as they are not needed. Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20211012013735.3523140-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ep93xx_keypad.c | 37 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index a0c6cdf8e0d3..6be5474ba2f2 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -17,6 +17,7 @@ * flag. */ +#include #include #include #include @@ -35,28 +36,28 @@ #define KEY_REG 0x08 /* Key Value Capture register */ /* Key Scan Initialization Register bit defines */ -#define KEY_INIT_DBNC_MASK (0x00ff0000) -#define KEY_INIT_DBNC_SHIFT (16) -#define KEY_INIT_DIS3KY (1<<15) -#define KEY_INIT_DIAG (1<<14) -#define KEY_INIT_BACK (1<<13) -#define KEY_INIT_T2 (1<<12) -#define KEY_INIT_PRSCL_MASK (0x000003ff) -#define KEY_INIT_PRSCL_SHIFT (0) +#define KEY_INIT_DBNC_MASK GENMASK(23, 16) +#define KEY_INIT_DBNC_SHIFT 16 +#define KEY_INIT_DIS3KY BIT(15) +#define KEY_INIT_DIAG BIT(14) +#define KEY_INIT_BACK BIT(13) +#define KEY_INIT_T2 BIT(12) +#define KEY_INIT_PRSCL_MASK GENMASK(9, 0) +#define KEY_INIT_PRSCL_SHIFT 0 /* Key Scan Diagnostic Register bit defines */ -#define KEY_DIAG_MASK (0x0000003f) -#define KEY_DIAG_SHIFT (0) +#define KEY_DIAG_MASK GENMASK(5, 0) +#define KEY_DIAG_SHIFT 0 /* Key Value Capture Register bit defines */ -#define KEY_REG_K (1<<15) -#define KEY_REG_INT (1<<14) -#define KEY_REG_2KEYS (1<<13) -#define KEY_REG_1KEY (1<<12) -#define KEY_REG_KEY2_MASK (0x00000fc0) -#define KEY_REG_KEY2_SHIFT (6) -#define KEY_REG_KEY1_MASK (0x0000003f) -#define KEY_REG_KEY1_SHIFT (0) +#define KEY_REG_K BIT(15) +#define KEY_REG_INT BIT(14) +#define KEY_REG_2KEYS BIT(13) +#define KEY_REG_1KEY BIT(12) +#define KEY_REG_KEY2_MASK GENMASK(11, 6) +#define KEY_REG_KEY2_SHIFT 6 +#define KEY_REG_KEY1_MASK GENMASK(5, 0) +#define KEY_REG_KEY1_SHIFT 0 #define EP93XX_MATRIX_SIZE (EP93XX_MATRIX_ROWS * EP93XX_MATRIX_COLS) -- cgit v1.2.3 From ab317169673dbdddba8b6132c53f6e5ce64726a3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 11 Oct 2021 18:37:34 -0700 Subject: Input: ep93xx_keypad - use dev_pm_set_wake_irq() Instead of manually toggling interrupt as wakeup source in suspend/resume methods, let's declare keypad interrupt and wakeup interrupt and leave the rest to the PM core. Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20211012013735.3523140-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ep93xx_keypad.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index 6be5474ba2f2..a66cfeaf5b21 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -27,6 +27,7 @@ #include #include #include +#include /* * Keypad Interface Register offsets @@ -191,9 +192,6 @@ static int __maybe_unused ep93xx_keypad_suspend(struct device *dev) mutex_unlock(&input_dev->mutex); - if (device_may_wakeup(&pdev->dev)) - enable_irq_wake(keypad->irq); - return 0; } @@ -203,9 +201,6 @@ static int __maybe_unused ep93xx_keypad_resume(struct device *dev) struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - if (device_may_wakeup(&pdev->dev)) - disable_irq_wake(keypad->irq); - mutex_lock(&input_dev->mutex); if (input_device_enabled(input_dev)) { @@ -316,7 +311,11 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) goto failed_free_irq; platform_set_drvdata(pdev, keypad); + device_init_wakeup(&pdev->dev, 1); + err = dev_pm_set_wake_irq(&pdev->dev, keypad->irq); + if (err) + dev_warn(&pdev->dev, "failed to set up wakeup irq: %d\n", err); return 0; @@ -342,6 +341,8 @@ static int ep93xx_keypad_remove(struct platform_device *pdev) struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; + dev_pm_clear_wake_irq(&pdev->dev); + free_irq(keypad->irq, keypad); if (keypad->enabled) -- cgit v1.2.3 From c4be5e5a113d78490c426b1124fd458a9d807933 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 12 Oct 2021 19:36:25 -0700 Subject: Input: ep93xx_keypad - switch to using managed resources By using managed resources (devm) we are able to streamline error handling in probe and remove most of the custom remove method. Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/YWZGKWgdarGtvtYA@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ep93xx_keypad.c | 118 +++++++++++---------------------- 1 file changed, 37 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index a66cfeaf5b21..272a4f1c6e81 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -219,6 +219,13 @@ static int __maybe_unused ep93xx_keypad_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops, ep93xx_keypad_suspend, ep93xx_keypad_resume); +static void ep93xx_keypad_release_gpio_action(void *_pdev) +{ + struct platform_device *pdev = _pdev; + + ep93xx_keypad_release_gpio(pdev); +} + static int ep93xx_keypad_probe(struct platform_device *pdev) { struct ep93xx_keypad *keypad; @@ -227,61 +234,46 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) struct resource *res; int err; - keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL); + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); if (!keypad) return -ENOMEM; keypad->pdata = dev_get_platdata(&pdev->dev); - if (!keypad->pdata) { - err = -EINVAL; - goto failed_free; - } + if (!keypad->pdata) + return -EINVAL; keymap_data = keypad->pdata->keymap_data; - if (!keymap_data) { - err = -EINVAL; - goto failed_free; - } + if (!keymap_data) + return -EINVAL; keypad->irq = platform_get_irq(pdev, 0); - if (keypad->irq < 0) { - err = keypad->irq; - goto failed_free; - } + if (keypad->irq < 0) + return keypad->irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENXIO; - goto failed_free; - } - - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - err = -EBUSY; - goto failed_free; - } + if (!res) + return -ENXIO; - keypad->mmio_base = ioremap(res->start, resource_size(res)); - if (keypad->mmio_base == NULL) { - err = -ENXIO; - goto failed_free_mem; - } + keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(keypad->mmio_base)) + return PTR_ERR(keypad->mmio_base); err = ep93xx_keypad_acquire_gpio(pdev); if (err) - goto failed_free_io; + return err; - keypad->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(keypad->clk)) { - err = PTR_ERR(keypad->clk); - goto failed_free_gpio; - } + err = devm_add_action_or_reset(&pdev->dev, + ep93xx_keypad_release_gpio_action, pdev); + if (err) + return err; - input_dev = input_allocate_device(); - if (!input_dev) { - err = -ENOMEM; - goto failed_put_clk; - } + keypad->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) + return PTR_ERR(keypad->clk); + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; keypad->input_dev = input_dev; @@ -289,26 +281,26 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) input_dev->id.bustype = BUS_HOST; input_dev->open = ep93xx_keypad_open; input_dev->close = ep93xx_keypad_close; - input_dev->dev.parent = &pdev->dev; err = matrix_keypad_build_keymap(keymap_data, NULL, EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS, keypad->keycodes, input_dev); if (err) - goto failed_free_dev; + return err; if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT) __set_bit(EV_REP, input_dev->evbit); input_set_drvdata(input_dev, keypad); - err = request_irq(keypad->irq, ep93xx_keypad_irq_handler, - 0, pdev->name, keypad); + err = devm_request_irq(&pdev->dev, keypad->irq, + ep93xx_keypad_irq_handler, + 0, pdev->name, keypad); if (err) - goto failed_free_dev; + return err; err = input_register_device(input_dev); if (err) - goto failed_free_irq; + return err; platform_set_drvdata(pdev, keypad); @@ -318,48 +310,12 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to set up wakeup irq: %d\n", err); return 0; - -failed_free_irq: - free_irq(keypad->irq, keypad); -failed_free_dev: - input_free_device(input_dev); -failed_put_clk: - clk_put(keypad->clk); -failed_free_gpio: - ep93xx_keypad_release_gpio(pdev); -failed_free_io: - iounmap(keypad->mmio_base); -failed_free_mem: - release_mem_region(res->start, resource_size(res)); -failed_free: - kfree(keypad); - return err; } static int ep93xx_keypad_remove(struct platform_device *pdev) { - struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); - struct resource *res; - dev_pm_clear_wake_irq(&pdev->dev); - free_irq(keypad->irq, keypad); - - if (keypad->enabled) - clk_disable(keypad->clk); - clk_put(keypad->clk); - - input_unregister_device(keypad->input_dev); - - ep93xx_keypad_release_gpio(pdev); - - iounmap(keypad->mmio_base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad); - return 0; } -- cgit v1.2.3 From 804f354ab6ce6668244db6d4c2da8e481ad89fb5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 12 Oct 2021 19:46:37 -0700 Subject: Input: adxl34x - fix sparse warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the following warning from sparse: CC [M] drivers/input/misc/adxl34x.o CHECK drivers/input/misc/adxl34x.c drivers/input/misc/adxl34x.c:245:29: warning: cast to restricted __le16 drivers/input/misc/adxl34x.c:248:29: warning: cast to restricted __le16 drivers/input/misc/adxl34x.c:251:29: warning: cast to restricted __le16 Acked-by: Michael Hennerich Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/YWZIjb91d6aAwgss@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/adxl34x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 34beac80e6f0..a4af314392a9 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -237,7 +237,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = { static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis) { - short buf[3]; + __le16 buf[3]; ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf); -- cgit v1.2.3 From f041a7af12636639d9c87e541c4c261d40f5afa7 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 13 Oct 2021 13:23:04 +0200 Subject: Input: tm2-touchkey - report scan codes Report the index of pressed touch key as MSC_SCAN code to userspace so it is possible to identify which of the keys was pressed (not just the function that is currently assigned to the key). This is done similarly also in mcs_touchkey and mpr121_touchkey. Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20211013112305.41574-1-stephan@gerhold.net Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tm2-touchkey.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index 6218b1c682ef..ce40ea2d940f 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -156,6 +156,8 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) goto out; } + input_event(touchkey->input_dev, EV_MSC, MSC_SCAN, index); + if (data & TM2_TOUCHKEY_BIT_PRESS_EV) { for (i = 0; i < touchkey->num_keycodes; i++) input_report_key(touchkey->input_dev, @@ -250,6 +252,7 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME; touchkey->input_dev->id.bustype = BUS_I2C; + input_set_capability(touchkey->input_dev, EV_MSC, MSC_SCAN); for (i = 0; i < touchkey->num_keycodes; i++) input_set_capability(touchkey->input_dev, EV_KEY, touchkey->keycodes[i]); -- cgit v1.2.3 From 872e57abd171515bc180f2d44c2c99da29542320 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 13 Oct 2021 13:23:05 +0200 Subject: Input: tm2-touchkey - allow changing keycodes from userspace At the moment the touch keys have key codes assigned from the device tree. In some cases, users might want to change the key code from userspace. There is existing functionality for this in the input core using the EVIOCSKEYCODE ioctl, which is integrated for example into udev. Make it possible to use this functionality for tm2-touchkey by simply making the input core aware of the array that holds the keycodes. Similar code also exists in mcs_touchkey and mpr121_touchkey. Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20211013112305.41574-2-stephan@gerhold.net Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tm2-touchkey.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index ce40ea2d940f..632cd6c1c8d4 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -252,6 +252,10 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME; touchkey->input_dev->id.bustype = BUS_I2C; + touchkey->input_dev->keycode = touchkey->keycodes; + touchkey->input_dev->keycodemax = touchkey->num_keycodes; + touchkey->input_dev->keycodesize = sizeof(touchkey->keycodes[0]); + input_set_capability(touchkey->input_dev, EV_MSC, MSC_SCAN); for (i = 0; i < touchkey->num_keycodes; i++) input_set_capability(touchkey->input_dev, EV_KEY, -- cgit v1.2.3 From 9271cda2bb41b16063e2c24aae890e814caf82a1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Sep 2021 21:50:37 -0700 Subject: Input: ads7846 - set input device bus type and product ID Set input device's bus type as BUS_SPI and use model as product ID. Link: https://lore.kernel.org/r/20210910045039.4020199-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index eaa8714ad19d..a018481e9d8b 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1304,6 +1304,9 @@ static int ads7846_probe(struct spi_device *spi) input_dev->name = ts->name; input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_SPI; + input_dev->id.product = pdata->model; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, -- cgit v1.2.3 From ccd661392abb728fc685e543f6be86fc435e163f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Sep 2021 21:50:38 -0700 Subject: Input: ads7846 - use input_set_capability() Instead of manipulating capability bits directly use input_set_capability(). Also stop setting EV_ABS explicitly as input_set_abs_params() does it for us. Link: https://lore.kernel.org/r/20210910045039.4020199-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index a018481e9d8b..0f973351bc67 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1307,8 +1307,7 @@ static int ads7846_probe(struct spi_device *spi) input_dev->id.bustype = BUS_SPI; input_dev->id.product = pdata->model; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + 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, -- cgit v1.2.3 From 36fc54375f985cb9fc52b655ed18052147596f8f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Sep 2021 21:50:39 -0700 Subject: Input: ads7846 - do not attempt IRQ workaround when deferring probe When request_irq() returns -EPORBE_DEFER we should abort probe and try again later instead of trying to engage IRQ trigger workaround. Link: https://lore.kernel.org/r/20210910045039.4020199-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 0f973351bc67..a25a77dd9a32 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1361,7 +1361,7 @@ static int ads7846_probe(struct spi_device *spi) err = devm_request_threaded_irq(dev, spi->irq, ads7846_hard_irq, ads7846_irq, irq_flags, dev->driver->name, ts); - if (err && !pdata->irq_flags) { + 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; -- cgit v1.2.3 From a88638c4e69cfcbfebc7523d777d1273f2fce806 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 15 Oct 2021 22:08:50 -0700 Subject: Input: max8925_onkey - don't mark comment as kernel-doc Change the comment to a normal (non-kernel-doc) comment to avoid these kernel-doc warnings: max8925_onkey.c:2: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * MAX8925 ONKEY driver max8925_onkey.c:2: warning: missing initial short description on line: * MAX8925 ONKEY driver Fixes: 3734574cac100 ("Input: enable onkey driver of max8925") Signed-off-by: Randy Dunlap Reported-by: kernel test robot Link: https://lore.kernel.org/r/20211002045943.9406-1-rdunlap@infradead.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/max8925_onkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index ffab4a490c75..4770cb55631a 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -1,4 +1,4 @@ -/** +/* * MAX8925 ONKEY driver * * Copyright (C) 2009 Marvell International Ltd. -- cgit v1.2.3 From ec45b858c867b2489f4ae93958379f5cf2597163 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 15 Oct 2021 22:25:58 -0700 Subject: Input: cpcap-pwrbutton - do not set input parent explicitly We are using devm_input_allocate_device() that already sets parent of the input device, there is no need to do that again. Reviewed-by: Sebastian Reichel Link: https://lore.kernel.org/r/YWpiZqrfC9+GQsM4@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/cpcap-pwrbutton.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/cpcap-pwrbutton.c b/drivers/input/misc/cpcap-pwrbutton.c index 372cb44d0635..879790bbf9fe 100644 --- a/drivers/input/misc/cpcap-pwrbutton.c +++ b/drivers/input/misc/cpcap-pwrbutton.c @@ -77,7 +77,6 @@ static int cpcap_power_button_probe(struct platform_device *pdev) button->idev->name = "cpcap-pwrbutton"; button->idev->phys = "cpcap-pwrbutton/input0"; - button->idev->dev.parent = button->dev; input_set_capability(button->idev, EV_KEY, KEY_POWER); err = devm_request_threaded_irq(&pdev->dev, irq, NULL, -- cgit v1.2.3 From dcd6a66a23e97e999e8ccededb6256863d288379 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 16 Oct 2021 21:16:55 -0700 Subject: Input: max77693-haptic - drop unneeded MODULE_ALIAS The MODULE_DEVICE_TABLE already creates proper alias for platform driver. Having another MODULE_ALIAS causes the alias to be duplicated. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210916170514.137977-1-krzysztof.kozlowski@canonical.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/max77693-haptic.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 0d09ffeafeea..4369d3c04d38 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -424,5 +424,4 @@ module_platform_driver(max77693_haptic_driver); MODULE_AUTHOR("Jaewon Kim "); MODULE_AUTHOR("Krzysztof Kozlowski "); MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver"); -MODULE_ALIAS("platform:max77693-haptic"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9e5afc84ff94815ad10853420dcecdf33e9226f8 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 16 Oct 2021 21:27:59 -0700 Subject: Input: pm8941-pwrkey - respect reboot_mode for warm reset On some devices, e.g. Sony Xperia M4 Aqua, warm reset is used to reboot device into bootloader and recovery mode. Instead of always doing hard reset, add a check on reboot_mode for possible warm reset. Signed-off-by: Shawn Guo Tested-by: Luca Weiss Link: https://lore.kernel.org/r/20210714095850.27185-3-shawn.guo@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pm8941-pwrkey.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index 33609603245d..89af52498c96 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -29,6 +29,7 @@ #define PON_PS_HOLD_RST_CTL2 0x5b #define PON_PS_HOLD_ENABLE BIT(7) #define PON_PS_HOLD_TYPE_MASK 0x0f +#define PON_PS_HOLD_TYPE_WARM_RESET 1 #define PON_PS_HOLD_TYPE_SHUTDOWN 4 #define PON_PS_HOLD_TYPE_HARD_RESET 7 @@ -99,7 +100,10 @@ static int pm8941_reboot_notify(struct notifier_block *nb, break; case SYS_RESTART: default: - reset_type = PON_PS_HOLD_TYPE_HARD_RESET; + if (reboot_mode == REBOOT_WARM) + reset_type = PON_PS_HOLD_TYPE_WARM_RESET; + else + reset_type = PON_PS_HOLD_TYPE_HARD_RESET; break; } -- cgit v1.2.3 From 235300ed8c6ccf8d3044f4ad25b1e984d5dd20d0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 16 Oct 2021 21:34:20 -0700 Subject: Input: ili210x - use resolution from ili251x firmware The ili251x firmware protocol permits readout of panel resolution, implement this, but make it possible to override this value using DT bindings. This way, older DTs which contain touchscreen-size-x and touchscreen-size-y properties will behave just like before and new DTs may avoid specifying these for ILI251x. Note that the command format is different on other controllers, so this functionality is isolated to ILI251x. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20210831202506.181927-1-marex@denx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 30576a5f2f04..b3f6b4d51df2 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -35,6 +35,7 @@ struct ili2xxx_chip { unsigned int max_touches; unsigned int resolution; bool has_calibrate_reg; + bool has_firmware_proto; bool has_pressure_reg; }; @@ -268,6 +269,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, }; @@ -323,6 +325,54 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data) 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 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; + + return 0; +} + static ssize_t ili210x_calibrate(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -449,6 +499,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, -- cgit v1.2.3 From 70a7681db0c9266bd0a3fd6c90a5cfa20ac44995 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 16 Oct 2021 21:39:04 -0700 Subject: Input: ili210x - export ili251x version details via sysfs The ili251x firmware protocol permits readout of firmware version, protocol version, mcu version and current mode (application, boot loader, forced update). These information are useful when updating the firmware on the il251x, e.g. to avoid updating the same firmware into the device multiple times. The locking is now necessary to avoid races between interrupt handler and the sysfs readouts. Note that the protocol differs considerably between the ili2xxx devices, this patch therefore implements this functionality only for ili251x that I can test. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20210831202506.181927-2-marex@denx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 165 +++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index b3f6b4d51df2..7a20e3db186a 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -22,6 +22,12 @@ /* 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_GET_MODE 0xc0 +#define REG_GET_MODE_AP 0x5a +#define REG_GET_MODE_BL 0x55 #define REG_CALIBRATE 0xcc struct ili2xxx_chip { @@ -45,6 +51,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; }; @@ -353,6 +363,69 @@ static int ili251x_firmware_update_resolution(struct device *dev) 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); @@ -370,9 +443,83 @@ static int ili251x_firmware_update_cached_state(struct device *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) @@ -401,22 +548,34 @@ static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate); static struct attribute *ili210x_attributes[] = { &dev_attr_calibrate.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) -- cgit v1.2.3 From c6ac8f0b4ca927316eb40e1e9ba83df5d29f3793 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 16 Oct 2021 21:39:53 -0700 Subject: Input: ili210x - add ili251x firmware update support The ili251x firmware can be updated, this is used when switching between different modes of operation of the touch surface, e.g. glove operation. This patch implements the firmware update mechanism triggered by a write into an sysfs attribute. The firmware itself is distributed as an intel hex file with non-standard types. The first two lines are of type 0xad, which indicates the start of DataFlash payload, that is always at address 0xf000 on the ili251x, so it can be dropped, and 0xac which indicates the position of firmware info in the Application payload, that is always at address 0x2020 on the ili251x and we do not care. The rest of the firmware is data of type 0x00, and we care about that. To convert the firmware hex file into something usable by the kernel, remove the first two lines and then use ihex2fw: $ tail -n +3 input.hex > temp.hex $ ./tools/firmware/ihex2fw temp.hex firmware/ilitek/ili251x.bin To trigger the firmware update, place firmware file ilitek/ili251x.bin into /lib/firmware/, write into firmware_update sysfs attribute and wait about 30-40 seconds. The firmware update is slow. Afterward, verify the firmware_version and mode sysfs attributes to check whether the firmware got updated and the controller switched back to application (AP) mode by reading out 'mode' attribute in sysfs. Note that the content of firmware_version, e.g. 0600.0005.abcd.aa04 can be matched to the content of the firmware hex file. The first four bytes, 0x06 0x00 0x00 0x05 can be found at ^:102030 00 05000006, the next four bytes 0xab 0xcd 0xaa 0x04 at ^:10F000 00 nnnnnnnn ABCDAA04. Note that the protocol differs considerably between the ili2xxx devices, this patch therefore implements this functionality only for ili251x that I can test. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20210831202506.181927-3-marex@denx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 1 + drivers/input/touchscreen/ili210x.c | 308 ++++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+) (limited to 'drivers') 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/ili210x.c b/drivers/input/touchscreen/ili210x.c index 7a20e3db186a..867c13d3cb17 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 #include #include #include +#include #include #include #include @@ -25,11 +27,20 @@ #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); @@ -546,8 +557,305 @@ 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, -- cgit v1.2.3 From 322a552e19550872eb9f3a54330a5f615db42916 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 2 Nov 2021 16:55:49 -0700 Subject: Input: cap11xx - add support for cap1206 According to the datasheet "The CAP1206 is pin- and register-compatible with the CAP1106, with the exception of the GAIN[1:0] bits and ALT_POL bit"(57). So, this patch aims to disable them as they are no longer used. Signed-off-by: Jesse Taube Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/cap11xx.txt | 1 + drivers/input/keyboard/cap11xx.c | 43 +++++++++++++++------- 2 files changed, 30 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/cap11xx.txt b/Documentation/devicetree/bindings/input/cap11xx.txt index 8c67a0b5058d..74b91c398d22 100644 --- a/Documentation/devicetree/bindings/input/cap11xx.txt +++ b/Documentation/devicetree/bindings/input/cap11xx.txt @@ -9,6 +9,7 @@ Required properties: "microchip,cap1106" "microchip,cap1126" "microchip,cap1188" + "microchip,cap1206" reg: The I2C slave address of the device. diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 688e2bef682e..7c85343cd32f 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -91,18 +91,21 @@ struct cap11xx_hw_model { u8 product_id; unsigned int num_channels; unsigned int num_leds; + bool no_gain; }; enum { CAP1106, CAP1126, CAP1188, + CAP1206, }; static const struct cap11xx_hw_model cap11xx_devices[] = { - [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 }, - [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 }, - [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 }, + [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0, .no_gain = false }, + [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2, .no_gain = false }, + [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8, .no_gain = false }, + [CAP1206] = { .product_id = 0x67, .num_channels = 6, .num_leds = 0, .no_gain = true }, }; static const struct reg_default cap11xx_reg_defaults[] = { @@ -378,17 +381,24 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client, node = dev->of_node; if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) { - if (is_power_of_2(gain32) && gain32 <= 8) + if (cap->no_gain) + dev_warn(dev, + "This version doesn't support sensor gain\n"); + else if (is_power_of_2(gain32) && gain32 <= 8) gain = ilog2(gain32); else dev_err(dev, "Invalid sensor-gain value %d\n", gain32); } - if (of_property_read_bool(node, "microchip,irq-active-high")) { - error = regmap_update_bits(priv->regmap, CAP11XX_REG_CONFIG2, - CAP11XX_REG_CONFIG2_ALT_POL, 0); - if (error) - return error; + if (id->driver_data != CAP1206) { + if (of_property_read_bool(node, "microchip,irq-active-high")) { + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_CONFIG2, + CAP11XX_REG_CONFIG2_ALT_POL, + 0); + if (error) + return error; + } } /* Provide some useful defaults */ @@ -398,11 +408,14 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client, of_property_read_u32_array(node, "linux,keycodes", priv->keycodes, cap->num_channels); - error = regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL, - CAP11XX_REG_MAIN_CONTROL_GAIN_MASK, - gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT); - if (error) - return error; + if (!cap->no_gain) { + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_MAIN_CONTROL, + CAP11XX_REG_MAIN_CONTROL_GAIN_MASK, + gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT); + if (error) + return error; + } /* Disable autorepeat. The Linux input system has its own handling. */ error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); @@ -470,6 +483,7 @@ static const struct of_device_id cap11xx_dt_ids[] = { { .compatible = "microchip,cap1106", }, { .compatible = "microchip,cap1126", }, { .compatible = "microchip,cap1188", }, + { .compatible = "microchip,cap1206", }, {} }; MODULE_DEVICE_TABLE(of, cap11xx_dt_ids); @@ -478,6 +492,7 @@ static const struct i2c_device_id cap11xx_i2c_ids[] = { { "cap1106", CAP1106 }, { "cap1126", CAP1126 }, { "cap1188", CAP1188 }, + { "cap1206", CAP1206 }, {} }; MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids); -- cgit v1.2.3 From 16e28abb7290c4ca3b3a0f333ba067f34bb18c86 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 3 Nov 2021 08:00:19 +0100 Subject: Input: i8042 - Add quirk for Fujitsu Lifebook T725 Fujitsu Lifebook T725 laptop requires, like a few other similar models, the nomux and notimeout options to probe the touchpad properly. This patch adds the corresponding quirk entries. BugLink: https://bugzilla.suse.com/show_bug.cgi?id=1191980 Tested-by: Neal Gompa Cc: Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20211103070019.13374-1-tiwai@suse.de Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a5a003553646..aedd05541044 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -272,6 +272,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, }, + { + /* Fujitsu Lifebook T725 laptop */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + }, + }, { /* Fujitsu Lifebook U745 */ .matches = { @@ -840,6 +847,13 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), }, }, + { + /* Fujitsu Lifebook T725 laptop */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + }, + }, { /* Fujitsu U574 laptop */ /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ -- cgit v1.2.3 From 6e4860410b828f8576c0c003d412fcf8a7d433f9 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 29 Oct 2021 15:42:51 +0200 Subject: Input: synaptics-rmi4 - Fix device hierarchy The created rmi device is orphan, which breaks the real device hierarchy, and can cause some trouble, especially during suspend and resume sequences. E.g. in case of I2C, rmi dev should be child of the I2C client device. Fix this, assigning the transport device as parent of the rmi device. Signed-off-by: Loic Poulain Link: https://lore.kernel.org/r/1635514971-18415-1-git-send-email-loic.poulain@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_bus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 24f31a5c0e04..50a0134b6901 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -90,6 +90,7 @@ int rmi_register_transport_device(struct rmi_transport_dev *xport) rmi_dev->dev.bus = &rmi_bus_type; rmi_dev->dev.type = &rmi_device_type; + rmi_dev->dev.parent = xport->dev; xport->rmi_dev = rmi_dev; -- cgit v1.2.3 From be896bd3b72b44126c55768f14c22a8729b0992e Mon Sep 17 00:00:00 2001 From: Phoenix Huang Date: Sun, 7 Nov 2021 22:00:03 -0800 Subject: Input: elantench - fix misreporting trackpoint coordinates Some firmwares occasionally report bogus data from trackpoint, with X or Y displacement being too large (outside of [-127, 127] range). Let's drop such packets so that we do not generate jumps. Signed-off-by: Phoenix Huang Tested-by: Yufei Du Link: https://lore.kernel.org/r/20210729010940.5752-1-phoenix@emc.com.tw Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 2d0bc029619f..956d9cd34796 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -517,6 +517,19 @@ static void elantech_report_trackpoint(struct psmouse *psmouse, case 0x16008020U: case 0x26800010U: case 0x36808000U: + + /* + * This firmware misreport coordinates for trackpoint + * occasionally. Discard packets outside of [-127, 127] range + * to prevent cursor jumps. + */ + if (packet[4] == 0x80 || packet[5] == 0x80 || + packet[1] >> 7 == packet[4] >> 7 || + packet[2] >> 7 == packet[5] >> 7) { + elantech_debug("discarding packet [%6ph]\n", packet); + break; + + } x = packet[4] - (int)((packet[1]^0x80) << 1); y = (int)((packet[2]^0x80) << 1) - packet[5]; -- cgit v1.2.3 From de889108391f0d6b8d5cdebb538d3629cf0050c6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 7 Nov 2021 19:37:53 -0800 Subject: Input: ili210x - special case ili251x sample read out The ili251x touch controller needs 5ms delay between sending I2C device address and register address, and, writing or reading register data. According to downstream ili251x example code, this 5ms delay is not required when reading touch samples out of the controller. Implement such a special case. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20211108005259.480545-1-marex@denx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 867c13d3cb17..e1551cdc6e1a 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -224,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) @@ -244,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); -- cgit v1.2.3 From 8639e042ad6aca7fc2a5b2fe8652396e2f522627 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 9 Nov 2021 22:17:03 -0800 Subject: Input: ili210x - improve polled sample spacing Currently the ili210x driver implements a threaded interrupt handler which starts upon edge on the interrupt line, and then polls the touch controller for samples. Every time a sample is obtained from the controller, the thread function checks whether further polling is required, and if so, waits fixed amount of time before polling for next sample. The delay between consecutive samples can thus vary greatly, because the I2C transfer required to retrieve the sample from the controller takes different amount of time on different platforms. Furthermore, different models of the touch controllers supported by this driver require different delays during retrieval of samples too. Instead of waiting fixed amount of time before polling for next sample, determine how much time passed since the beginning of sampling cycle and then wait only the remaining amount of time within the sampling cycle. This makes the driver deliver samples with equal spacing between them. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20211108005216.480525-1-marex@denx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index e1551cdc6e1a..de81ba77ffc7 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -334,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, @@ -347,8 +350,11 @@ 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; -- cgit v1.2.3 From 27931d38ce057f36e68bc68cd4bf4ba24bbb9c57 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 9 Nov 2021 22:17:49 -0800 Subject: Input: ili210x - reduce sample period to 15ms Modern devices may redraw display at 60 Hz, make sure we have one input sample per one frame. Reduce sample period to 15ms, so we would get up to 66.6 samples per second, although realistically with all the jitter and extra scheduling wiggle room, we would end up just above 60 samples per second. This should be a good compromise between sampling too often and sampling too seldom. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20211108114145.84118-1-marex@denx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index de81ba77ffc7..2bd407d86bae 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -14,7 +14,7 @@ #include #include -#define ILI2XXX_POLL_PERIOD 20 +#define ILI2XXX_POLL_PERIOD 15 #define ILI210X_DATA_SIZE 64 #define ILI211X_DATA_SIZE 43 -- cgit v1.2.3 From 91e2e76695fe52339be4bc4722475641ab0fc75c Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 12 Oct 2021 20:10:59 -0700 Subject: Input: wacom_i2c - use macros for the bit masks To make the code easier to read use macros for the bit masks. Signed-off-by: Alistair Francis Link: https://lore.kernel.org/r/20211009113707.17568-2-alistair@alistair23.me Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_i2c.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers') 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 @@ * */ +#include #include #include #include @@ -14,6 +15,15 @@ #include #include +/* 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); -- cgit v1.2.3 From 744d0090a5f6dfa4c81b53402ccdf08313100429 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 9 Nov 2021 22:58:01 -0800 Subject: Input: iforce - fix control-message timeout USB control-message timeouts are specified in milliseconds and should specifically not vary with CONFIG_HZ. Fixes: 487358627825 ("Input: iforce - use DMA-safe buffer when getting IDs from USB") Signed-off-by: Johan Hovold Cc: stable@vger.kernel.org # 5.3 Link: https://lore.kernel.org/r/20211025115501.5190-1-johan@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/iforce-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 6c554c11a7ac..ea58805c480f 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -92,7 +92,7 @@ static int iforce_usb_get_id(struct iforce *iforce, u8 id, id, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, 0, buf, IFORCE_MAX_LENGTH, HZ); + 0, 0, buf, IFORCE_MAX_LENGTH, 1000); if (status < 0) { dev_err(&iforce_usb->intf->dev, "usb_submit_urb failed: %d\n", status); -- cgit v1.2.3