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/input') 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/input') 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/input') 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/input') 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 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'drivers/input') 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); -- 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/input') 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 --- drivers/input/keyboard/Kconfig | 10 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/cypress-sf.c | 224 ++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 drivers/input/keyboard/cypress-sf.c (limited to 'drivers/input') 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/input') 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/input') 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/input') 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 --- drivers/input/touchscreen/goodix.c | 74 ++++---------------------------------- drivers/input/touchscreen/goodix.h | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 68 deletions(-) create mode 100644 drivers/input/touchscreen/goodix.h (limited to 'drivers/input') 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/input') 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/input') 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/input') 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/input') 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)