From 7dbad03ebcb924cde142f7477d65a54ffb1166a3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2020 20:20:39 +0200 Subject: ARM: s3c: adc: move header to linux/soc/samsung There are multiple drivers using the private adc interface. It seems unlikely that they would ever get converted to iio, so make the current state official by making the header file global. The s3c2410_ts driver needs a couple of register definitions as well. Signed-off-by: Arnd Bergmann Acked-by: Guenter Roeck Acked-by: Dmitry Torokhov Acked-by: Sebastian Reichel Link: https://lore.kernel.org/r/20200806182059.2431-22-krzk@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/input/touchscreen/s3c2410_ts.c | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 82920ff46f72..2e70c0b79444 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -20,10 +20,43 @@ #include #include -#include -#include +#include #include +#define S3C2410_ADCCON (0x00) +#define S3C2410_ADCTSC (0x04) +#define S3C2410_ADCDLY (0x08) +#define S3C2410_ADCDAT0 (0x0C) +#define S3C2410_ADCDAT1 (0x10) +#define S3C64XX_ADCUPDN (0x14) +#define S3C2443_ADCMUX (0x18) +#define S3C64XX_ADCCLRINT (0x18) +#define S5P_ADCMUX (0x1C) +#define S3C64XX_ADCCLRINTPNDNUP (0x20) + +/* ADCTSC Register Bits */ +#define S3C2443_ADCTSC_UD_SEN (1 << 8) +#define S3C2410_ADCTSC_YM_SEN (1<<7) +#define S3C2410_ADCTSC_YP_SEN (1<<6) +#define S3C2410_ADCTSC_XM_SEN (1<<5) +#define S3C2410_ADCTSC_XP_SEN (1<<4) +#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3) +#define S3C2410_ADCTSC_AUTO_PST (1<<2) +#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0) + +/* ADCDAT0 Bits */ +#define S3C2410_ADCDAT0_UPDOWN (1<<15) +#define S3C2410_ADCDAT0_AUTO_PST (1<<14) +#define S3C2410_ADCDAT0_XY_PST (0x3<<12) +#define S3C2410_ADCDAT0_XPDATA_MASK (0x03FF) + +/* ADCDAT1 Bits */ +#define S3C2410_ADCDAT1_UPDOWN (1<<15) +#define S3C2410_ADCDAT1_AUTO_PST (1<<14) +#define S3C2410_ADCDAT1_XY_PST (0x3<<12) +#define S3C2410_ADCDAT1_YPDATA_MASK (0x03FF) + + #define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) #define INT_DOWN (0) -- cgit v1.2.3 From b4e452b5e97dc70d529475168a37ad7394994b8b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 22 Aug 2020 08:08:55 -0700 Subject: platform/input: cros_ec: Replace -ENOTSUPP with -ENOPROTOOPT -ENOTSUPP is not a SUSV4 error code and should not be used. Use -ENOPROTOOPT instead to report EC_RES_INVALID_VERSION responses from the EC. This matches match the NFS response for unsupported protocol versions. Cc: Gwendal Grignou Cc: Yu-Hsuan Hsu Cc: Prashant Malani Cc: Brian Norris Acked-by: Dmitry Torokhov Signed-off-by: Guenter Roeck Signed-off-by: Enric Balletbo i Serra --- drivers/input/keyboard/cros_ec_keyb.c | 2 +- drivers/platform/chrome/cros_ec_proto.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index fc1793ca2f17..15d17c717081 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -348,7 +348,7 @@ static int cros_ec_keyb_info(struct cros_ec_device *ec_dev, params->event_type = event_type; ret = cros_ec_cmd_xfer_status(ec_dev, msg); - if (ret == -ENOTSUPP) { + if (ret == -ENOPROTOOPT) { /* With older ECs we just return 0 for everything */ memset(result, 0, result_size); ret = 0; diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 8d52b3b4bd4e..f4727511fb41 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -579,7 +579,7 @@ static int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, * * Return: * >=0 - The number of bytes transferred - * -ENOTSUPP - Operation not supported + * -ENOPROTOOPT - Operation not supported * -EPROTO - Protocol error */ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, @@ -593,7 +593,7 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, } else if (msg->result == EC_RES_INVALID_VERSION) { dev_dbg(ec_dev->dev, "Command invalid version (err:%d)\n", msg->result); - return -ENOTSUPP; + return -ENOPROTOOPT; } else if (msg->result != EC_RES_SUCCESS) { dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result); return -EPROTO; -- cgit v1.2.3 From 50597970aa84cc087424ef8d3bc430665aebcece Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Wed, 16 Sep 2020 11:48:14 +0800 Subject: Input: hyperv-keyboard: Use VMBUS_RING_SIZE() for ringbuffer sizes For a Hyper-V vmbus, the size of the ringbuffer has two requirements: 1) it has to take one PAGE_SIZE for the header 2) it has to be PAGE_SIZE aligned so that double-mapping can work VMBUS_RING_SIZE() could calculate a correct ringbuffer size which fulfills both requirements, therefore use it to make sure vmbus work when PAGE_SIZE != HV_HYP_PAGE_SIZE (4K). Note that since the argument for VMBUS_RING_SIZE() is the size of payload (data part), so it will be minus 4k (the size of header when PAGE_SIZE = 4k) than the original value to keep the ringbuffer total size unchanged when PAGE_SIZE = 4k. Signed-off-by: Boqun Feng Cc: Michael Kelley Cc: Dmitry Torokhov Acked-by: Dmitry Torokhov Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20200916034817.30282-9-boqun.feng@gmail.com Signed-off-by: Wei Liu --- drivers/input/serio/hyperv-keyboard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index df4e9f6f4529..1a7b72a9016d 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -75,8 +75,8 @@ struct synth_kbd_keystroke { #define HK_MAXIMUM_MESSAGE_SIZE 256 -#define KBD_VSC_SEND_RING_BUFFER_SIZE (40 * 1024) -#define KBD_VSC_RECV_RING_BUFFER_SIZE (40 * 1024) +#define KBD_VSC_SEND_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) +#define KBD_VSC_RECV_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) #define XTKBD_EMUL0 0xe0 #define XTKBD_EMUL1 0xe1 -- cgit v1.2.3 From 879bc2d27904354b98ca295b6168718e045c4aa2 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 19 Oct 2020 16:57:50 +0200 Subject: hil/parisc: Disable HIL driver when it gets stuck When starting a HP machine with HIL driver but without an HIL keyboard or HIL mouse attached, it may happen that data written to the HIL loop gets stuck (e.g. because the transaction queue is full). Usually one will then have to reboot the machine because all you see is and endless output of: Transaction add failed: transaction already queued? In the higher layers hp_sdc_enqueue_transaction() is called to queued up a HIL packet. This function returns an error code, and this patch adds the necessary checks for this return code and disables the HIL driver if further packets can't be sent. Tested on a HP 730 and a HP 715/64 machine. Signed-off-by: Helge Deller Cc: --- drivers/input/serio/hil_mlc.c | 21 ++++++++++++++++++--- drivers/input/serio/hp_sdc_mlc.c | 8 ++++---- include/linux/hil_mlc.h | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index 65f4e9d62a67..d36e89d6fc54 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -74,7 +74,7 @@ EXPORT_SYMBOL(hil_mlc_unregister); static LIST_HEAD(hil_mlcs); static DEFINE_RWLOCK(hil_mlcs_lock); static struct timer_list hil_mlcs_kicker; -static int hil_mlcs_probe; +static int hil_mlcs_probe, hil_mlc_stop; static void hil_mlcs_process(unsigned long unused); static DECLARE_TASKLET_DISABLED_OLD(hil_mlcs_tasklet, hil_mlcs_process); @@ -702,9 +702,13 @@ static int hilse_donode(hil_mlc *mlc) if (!mlc->ostarted) { mlc->ostarted = 1; mlc->opacket = pack; - mlc->out(mlc); + rc = mlc->out(mlc); nextidx = HILSEN_DOZE; write_unlock_irqrestore(&mlc->lock, flags); + if (rc) { + hil_mlc_stop = 1; + return 1; + } break; } mlc->ostarted = 0; @@ -715,8 +719,13 @@ static int hilse_donode(hil_mlc *mlc) case HILSE_CTS: write_lock_irqsave(&mlc->lock, flags); - nextidx = mlc->cts(mlc) ? node->bad : node->good; + rc = mlc->cts(mlc); + nextidx = rc ? node->bad : node->good; write_unlock_irqrestore(&mlc->lock, flags); + if (rc) { + hil_mlc_stop = 1; + return 1; + } break; default: @@ -780,6 +789,12 @@ static void hil_mlcs_process(unsigned long unused) static void hil_mlcs_timer(struct timer_list *unused) { + if (hil_mlc_stop) { + /* could not send packet - stop immediately. */ + pr_warn(PREFIX "HIL seems stuck - Disabling HIL MLC.\n"); + return; + } + hil_mlcs_probe = 1; tasklet_schedule(&hil_mlcs_tasklet); /* Re-insert the periodic task. */ diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index 232d30c825bd..3e85e9039374 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -210,7 +210,7 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) priv->tseq[2] = 1; priv->tseq[3] = 0; priv->tseq[4] = 0; - __hp_sdc_enqueue_transaction(&priv->trans); + return __hp_sdc_enqueue_transaction(&priv->trans); busy: return 1; done: @@ -219,7 +219,7 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) return 0; } -static void hp_sdc_mlc_out(hil_mlc *mlc) +static int hp_sdc_mlc_out(hil_mlc *mlc) { struct hp_sdc_mlc_priv_s *priv; @@ -234,7 +234,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) do_data: if (priv->emtestmode) { up(&mlc->osem); - return; + return 0; } /* Shouldn't be sending commands when loop may be busy */ BUG_ON(down_trylock(&mlc->csem)); @@ -296,7 +296,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) BUG_ON(down_trylock(&mlc->csem)); } enqueue: - hp_sdc_enqueue_transaction(&priv->trans); + return hp_sdc_enqueue_transaction(&priv->trans); } static int __init hp_sdc_mlc_init(void) diff --git a/include/linux/hil_mlc.h b/include/linux/hil_mlc.h index 774f7d3b8f6a..369221fd5518 100644 --- a/include/linux/hil_mlc.h +++ b/include/linux/hil_mlc.h @@ -103,7 +103,7 @@ struct hilse_node { /* Methods for back-end drivers, e.g. hp_sdc_mlc */ typedef int (hil_mlc_cts) (hil_mlc *mlc); -typedef void (hil_mlc_out) (hil_mlc *mlc); +typedef int (hil_mlc_out) (hil_mlc *mlc); typedef int (hil_mlc_in) (hil_mlc *mlc, suseconds_t timeout); struct hil_mlc_devinfo { -- cgit v1.2.3 From 376ccca853fdb9959f7ac5185a428a9f91e71e86 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 27 Oct 2020 10:57:24 +0100 Subject: Input: ads7846: do not overwrite spi->mode flags set by spi framework Do not overwrite spi->mode flags set by spi framework, otherwise the chip select polarity will get lost. Signed-off-by: Oleksij Rempel Acked-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20201027095724.18654-3-o.rempel@pengutronix.de Signed-off-by: Mark Brown --- drivers/input/touchscreen/ads7846.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 8fd7fc39c4fd..f2dc2c8ab5ec 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1288,7 +1288,8 @@ static int ads7846_probe(struct spi_device *spi) * may not. So we stick to very-portable 8 bit words, both RX and TX. */ spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; + spi->mode &= ~SPI_MODE_X_MASK; + spi->mode |= SPI_MODE_0; err = spi_setup(spi); if (err < 0) return err; -- cgit v1.2.3 From 1e2020aa0da00051d94c4690c023c45d8f3834bd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 15 Dec 2020 17:41:16 -0800 Subject: Input: da7280 - fix missing error test An "if" testing for error condition has accidentally been dropped from the code. Reported-by: kernel test robot Fixes: cd3f609823a5 ("Input: new da7280 haptic driver") Reviewed-by: Roy Im Link: https://lore.kernel.org/r/X9j8lGFgijzHyYZZ@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/da7280.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c index 37568b00873d..2f698a8c1d65 100644 --- a/drivers/input/misc/da7280.c +++ b/drivers/input/misc/da7280.c @@ -863,6 +863,7 @@ static void da7280_parse_properties(struct device *dev, gpi_str3[7] = '0' + i; haptics->gpi_ctl[i].polarity = 0; error = device_property_read_string(dev, gpi_str3, &str); + if (!error) haptics->gpi_ctl[i].polarity = da7280_haptic_of_gpi_pol_str(dev, str); } -- cgit v1.2.3 From fafd320ae51b9c72d371585b2501f86640ea7b7d Mon Sep 17 00:00:00 2001 From: "jeffrey.lin" Date: Tue, 15 Dec 2020 10:50:12 -0800 Subject: Input: raydium_ts_i2c - do not send zero length Add default write command package to prevent i2c quirk error of zero data length as Raydium touch firmware update is executed. Signed-off-by: jeffrey.lin Link: https://lore.kernel.org/r/1608031217-7247-1-git-send-email-jeffrey.lin@raydium.corp-partner.google.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/raydium_i2c_ts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index e694a9b2b1e5..678afac85507 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -395,6 +395,7 @@ static int raydium_i2c_write_object(struct i2c_client *client, enum raydium_bl_ack state) { int error; + static const u8 cmd[] = { 0xFF, 0x39 }; error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len); if (error) { @@ -403,7 +404,7 @@ static int raydium_i2c_write_object(struct i2c_client *client, return error; } - error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0); + error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, cmd, sizeof(cmd)); if (error) { dev_err(&client->dev, "Ack obj command failed: %d\n", error); return error; -- cgit v1.2.3 From 6d2ad82fece2f5adcafe77252614fcf7211dec28 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 18 Dec 2020 10:01:33 -0800 Subject: Input: da7280 - protect OF match table with CONFIG_OF The OF match table is only used when OF is enabled. Fixes: cd3f609823a5 ("Input: new da7280 haptic driver") Reported-by: kernel test robot Acked-by: Roy Im Link: https://lore.kernel.org/r/X9xRLVPt9eBi0CT6@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/da7280.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c index 2f698a8c1d65..b08610d6e575 100644 --- a/drivers/input/misc/da7280.c +++ b/drivers/input/misc/da7280.c @@ -1300,11 +1300,13 @@ static int __maybe_unused da7280_resume(struct device *dev) return retval; } +#ifdef CONFIG_OF static const struct of_device_id da7280_of_match[] = { { .compatible = "dlg,da7280", }, { } }; MODULE_DEVICE_TABLE(of, da7280_of_match); +#endif static const struct i2c_device_id da7280_i2c_id[] = { { "da7280", }, -- cgit v1.2.3 From f5cace4b93d736cef348211ae0814cabdd26d86a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 18 Dec 2020 10:03:12 -0800 Subject: Input: imx_keypad - add dependency on HAS_IOMEM devm_platform_ioremap_resource() depends on CONFIG_HAS_IOMEM, so let's add it to the dependencies when COMPILE_TEST is enabled. Reported-by: kernel test robot Fixes: c8834032ffe2 ("Input: imx_keypad - add COMPILE_TEST support") Link: https://lore.kernel.org/r/X9llpA3w1zlZCHXU@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2b321c17054a..94eab82086b2 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -446,7 +446,7 @@ config KEYBOARD_MPR121 config KEYBOARD_SNVS_PWRKEY tristate "IMX SNVS Power Key Driver" - depends on ARCH_MXC || COMPILE_TEST + depends on ARCH_MXC || (COMPILE_TEST && HAS_IOMEM) depends on OF help This is the snvs powerkey driver for the Freescale i.MX application -- cgit v1.2.3 From 60dc45119465d086724851d2a5fd09daeb3c515e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 10 Jan 2021 18:19:59 -0800 Subject: Input: omap4-keypad - switch to use managed resources Now that input core supports devres-managed input devices we can clean up this driver a bit. Link: https://lore.kernel.org/r/X/qfJKiM21uyksYl@google.com Tested-by: Tony Lindgren Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 137 +++++++++++++--------------------- 1 file changed, 53 insertions(+), 84 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index b17ac2a295b9..55cc4855fb5c 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -252,8 +252,14 @@ static int omap4_keypad_check_revision(struct device *dev, return 0; } +static void omap4_disable_pm(void *d) +{ + pm_runtime_disable(d); +} + static int omap4_keypad_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct omap4_keypad *keypad_data; struct input_dev *input_dev; struct resource *res; @@ -271,33 +277,29 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (irq < 0) return irq; - keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL); + keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL); if (!keypad_data) { - dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); + dev_err(dev, "keypad_data memory allocation failed\n"); return -ENOMEM; } keypad_data->irq = irq; - error = omap4_keypad_parse_dt(&pdev->dev, keypad_data); + error = omap4_keypad_parse_dt(dev, keypad_data); if (error) - goto err_free_keypad; + return error; - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "can't request mem region\n"); - error = -EBUSY; - goto err_free_keypad; - } + keypad_data->base = devm_ioremap_resource(dev, res); + if (IS_ERR(keypad_data->base)) + return PTR_ERR(keypad_data->base); - keypad_data->base = ioremap(res->start, resource_size(res)); - if (!keypad_data->base) { - dev_err(&pdev->dev, "can't ioremap mem resource\n"); - error = -ENOMEM; - goto err_release_mem; - } + pm_runtime_enable(dev); - pm_runtime_enable(&pdev->dev); + error = devm_add_action_or_reset(dev, omap4_disable_pm, dev); + if (error) { + dev_err(dev, "unable to register cleanup action\n"); + return error; + } /* * Enable clocks for the keypad module so that we can read @@ -307,27 +309,25 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (error) { dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); pm_runtime_put_noidle(&pdev->dev); - } else { - error = omap4_keypad_check_revision(&pdev->dev, - keypad_data); - if (!error) { - /* Ensure device does not raise interrupts */ - omap4_keypad_stop(keypad_data); - } - pm_runtime_put_sync(&pdev->dev); + return error; + } + + error = omap4_keypad_check_revision(&pdev->dev, keypad_data); + if (!error) { + /* Ensure device does not raise interrupts */ + omap4_keypad_stop(keypad_data); } + + pm_runtime_put_sync(&pdev->dev); if (error) - goto err_pm_disable; + return error; /* input device allocation */ - keypad_data->input = input_dev = input_allocate_device(); - if (!input_dev) { - error = -ENOMEM; - goto err_pm_disable; - } + keypad_data->input = input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return -ENOMEM; input_dev->name = pdev->name; - input_dev->dev.parent = &pdev->dev; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; @@ -344,84 +344,53 @@ static int omap4_keypad_probe(struct platform_device *pdev) keypad_data->row_shift = get_count_order(keypad_data->cols); max_keys = keypad_data->rows << keypad_data->row_shift; - keypad_data->keymap = kcalloc(max_keys, - sizeof(keypad_data->keymap[0]), - GFP_KERNEL); + keypad_data->keymap = devm_kcalloc(dev, + max_keys, + sizeof(keypad_data->keymap[0]), + GFP_KERNEL); if (!keypad_data->keymap) { - dev_err(&pdev->dev, "Not enough memory for keymap\n"); - error = -ENOMEM; - goto err_free_input; + dev_err(dev, "Not enough memory for keymap\n"); + return -ENOMEM; } error = matrix_keypad_build_keymap(NULL, NULL, keypad_data->rows, keypad_data->cols, keypad_data->keymap, input_dev); if (error) { - dev_err(&pdev->dev, "failed to build keymap\n"); - goto err_free_keymap; + dev_err(dev, "failed to build keymap\n"); + return error; } - error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler, - omap4_keypad_irq_thread_fn, IRQF_ONESHOT, - "omap4-keypad", keypad_data); + error = devm_request_threaded_irq(dev, keypad_data->irq, + omap4_keypad_irq_handler, + omap4_keypad_irq_thread_fn, + IRQF_ONESHOT, + "omap4-keypad", keypad_data); if (error) { - dev_err(&pdev->dev, "failed to register interrupt\n"); - goto err_free_keymap; + dev_err(dev, "failed to register interrupt\n"); + return error; } error = input_register_device(keypad_data->input); - if (error < 0) { - dev_err(&pdev->dev, "failed to register input device\n"); - goto err_free_irq; + if (error) { + dev_err(dev, "failed to register input device\n"); + return error; } - device_init_wakeup(&pdev->dev, true); - error = dev_pm_set_wake_irq(&pdev->dev, keypad_data->irq); + device_init_wakeup(dev, true); + error = dev_pm_set_wake_irq(dev, keypad_data->irq); if (error) - dev_warn(&pdev->dev, - "failed to set up wakeup irq: %d\n", error); + dev_warn(dev, "failed to set up wakeup irq: %d\n", error); platform_set_drvdata(pdev, keypad_data); return 0; - -err_free_irq: - free_irq(keypad_data->irq, keypad_data); -err_free_keymap: - kfree(keypad_data->keymap); -err_free_input: - input_free_device(input_dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - iounmap(keypad_data->base); -err_release_mem: - release_mem_region(res->start, resource_size(res)); -err_free_keypad: - kfree(keypad_data); - return error; } static int omap4_keypad_remove(struct platform_device *pdev) { - struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); - struct resource *res; - dev_pm_clear_wake_irq(&pdev->dev); - free_irq(keypad_data->irq, keypad_data); - - pm_runtime_disable(&pdev->dev); - - input_unregister_device(keypad_data->input); - - iounmap(keypad_data->base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad_data->keymap); - kfree(keypad_data); - return 0; } -- cgit v1.2.3 From 7e0541b25702ccaa9560bdd2441caedca2d438e9 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 18:29:45 -0800 Subject: Input: omap4-keypad - disable unused long interrupts We are not using the long events and they produce extra interrupts. Let's not enable them at all. Note that also the v3.0.8 Linux Android kernel has long interrupts disabled. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210110190529.46135-2-tony@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 55cc4855fb5c..ab761aa66b6d 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -176,10 +176,9 @@ static int omap4_keypad_open(struct input_dev *input) kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, - OMAP4_DEF_IRQENABLE_EVENTEN | - OMAP4_DEF_IRQENABLE_LONGKEY); + OMAP4_DEF_IRQENABLE_EVENTEN); kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE, - OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA); + OMAP4_DEF_WUP_EVENT_ENA); enable_irq(keypad_data->irq); -- cgit v1.2.3 From 905dbf1d39d7f008acc46475384b33f98c512baf Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 18:50:53 -0800 Subject: Input: omap4-keypad - scan keys in two phases and simplify with bitmask Because of errata i689 the keyboard can idle with state where no key up interrupts are seen until after the next key press. This means we need to first check for any lost key up events before scanning for new down events. For example, rapidly pressing shift-shift-j can sometimes produce a J instead of j. Let's fix the issue by scanning the keyboard in two phases. First we scan for any key up events that we may have missed, and then we scan for key down events. Let's also simplify things with for_each_set_bit() as suggested by Dmitry Torokhov . Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210110190529.46135-3-tony@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 73 +++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index ab761aa66b6d..6dcf27af856d 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -78,7 +78,7 @@ struct omap4_keypad { u32 irqreg_offset; unsigned int row_shift; bool no_autorepeat; - unsigned char key_state[8]; + u64 keys; unsigned short *keymap; }; @@ -107,6 +107,33 @@ static void kbd_write_irqreg(struct omap4_keypad *keypad_data, keypad_data->base + keypad_data->irqreg_offset + offset); } +static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data, + u64 keys, bool down) +{ + struct input_dev *input_dev = keypad_data->input; + unsigned int col, row, code; + DECLARE_BITMAP(mask, 64); + unsigned long bit; + int events = 0; + + bitmap_from_u64(mask, keys); + + for_each_set_bit(bit, mask, keypad_data->rows * BITS_PER_BYTE) { + row = bit / BITS_PER_BYTE; + col = bit % BITS_PER_BYTE; + code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad_data->keymap[code], down); + + events++; + } + + if (events) + input_sync(input_dev); + + return events; +} /* Interrupt handlers */ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) @@ -122,35 +149,25 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; - struct input_dev *input_dev = keypad_data->input; - unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)]; - unsigned int col, row, code, changed; - u32 *new_state = (u32 *) key_state; - - *new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); - *(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); - - for (row = 0; row < keypad_data->rows; row++) { - changed = key_state[row] ^ keypad_data->key_state[row]; - if (!changed) - continue; - - for (col = 0; col < keypad_data->cols; col++) { - if (changed & (1 << col)) { - code = MATRIX_SCAN_CODE(row, col, - keypad_data->row_shift); - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, - keypad_data->keymap[code], - key_state[row] & (1 << col)); - } - } - } + u32 low, high; + u64 keys, changed; + + low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); + high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); + keys = low | (u64)high << 32; + + changed = keys ^ keypad_data->keys; + + /* + * Report key up events separately and first. This matters in case we + * lost key-up interrupt and just now catching up. + */ + omap4_keypad_report_keys(keypad_data, changed & ~keys, false); - input_sync(input_dev); + /* Report key down events */ + omap4_keypad_report_keys(keypad_data, changed & keys, true); - memcpy(keypad_data->key_state, key_state, - sizeof(keypad_data->key_state)); + keypad_data->keys = keys; /* clear pending interrupts */ kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, -- cgit v1.2.3 From 98b0c88d4bfb53b309350d9cdada96fd0deaa4af Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 20:28:40 -0800 Subject: Input: omap4-keypad - move rest of key scanning to a separate function Let's move rest of the key scanning code to omap4_keypad_scan_keys(). We will use omap4_keypad_scan_keys() also for implementing errata handling to clear the stuck last key in the following patch. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210110190529.46135-4-tony@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 39 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 6dcf27af856d..c48705dd049b 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -71,6 +71,7 @@ struct omap4_keypad { void __iomem *base; unsigned int irq; + struct mutex lock; /* for key scan */ unsigned int rows; unsigned int cols; @@ -135,6 +136,28 @@ static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data, return events; } +static void omap4_keypad_scan_keys(struct omap4_keypad *keypad_data, u64 keys) +{ + u64 changed; + + mutex_lock(&keypad_data->lock); + + changed = keys ^ keypad_data->keys; + + /* + * Report key up events separately and first. This matters in case we + * lost key-up interrupt and just now catching up. + */ + omap4_keypad_report_keys(keypad_data, changed & ~keys, false); + + /* Report key down events */ + omap4_keypad_report_keys(keypad_data, changed & keys, true); + + keypad_data->keys = keys; + + mutex_unlock(&keypad_data->lock); +} + /* Interrupt handlers */ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) { @@ -150,24 +173,13 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; u32 low, high; - u64 keys, changed; + u64 keys; low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); keys = low | (u64)high << 32; - changed = keys ^ keypad_data->keys; - - /* - * Report key up events separately and first. This matters in case we - * lost key-up interrupt and just now catching up. - */ - omap4_keypad_report_keys(keypad_data, changed & ~keys, false); - - /* Report key down events */ - omap4_keypad_report_keys(keypad_data, changed & keys, true); - - keypad_data->keys = keys; + omap4_keypad_scan_keys(keypad_data, keys); /* clear pending interrupts */ kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, @@ -300,6 +312,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) } keypad_data->irq = irq; + mutex_init(&keypad_data->lock); error = omap4_keypad_parse_dt(dev, keypad_data); if (error) -- cgit v1.2.3 From 29bf35e5ee808dbbcc8cff7a0bf6d4349148b586 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 22:16:51 -0800 Subject: Input: omap4-keypad - use PM runtime autosuspend Implement PM runtime autosuspend support to prepare for adding handling to clear stuck last pressed key in the following patches. The hardware has support for autoidle and can wake up to keypress events. Let's also update omap4_keypad_probe() to use dev instead of &pdev->dev since we already have it from the earlier changes. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/X/vqCs5/EDURprAJ@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 50 +++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index c48705dd049b..291970c09091 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -172,9 +172,17 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; + struct device *dev = keypad_data->input->dev.parent; u32 low, high; + int error; u64 keys; + error = pm_runtime_get_sync(dev); + if (error < 0) { + pm_runtime_put_noidle(dev); + return IRQ_NONE; + } + low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); keys = low | (u64)high << 32; @@ -185,14 +193,23 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return IRQ_HANDLED; } static int omap4_keypad_open(struct input_dev *input) { struct omap4_keypad *keypad_data = input_get_drvdata(input); + struct device *dev = input->dev.parent; + int error; - pm_runtime_get_sync(input->dev.parent); + error = pm_runtime_get_sync(dev); + if (error < 0) { + pm_runtime_put_noidle(dev); + return error; + } disable_irq(keypad_data->irq); @@ -211,6 +228,9 @@ static int omap4_keypad_open(struct input_dev *input) enable_irq(keypad_data->irq); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; } @@ -228,14 +248,20 @@ static void omap4_keypad_stop(struct omap4_keypad *keypad_data) static void omap4_keypad_close(struct input_dev *input) { - struct omap4_keypad *keypad_data; + struct omap4_keypad *keypad_data = input_get_drvdata(input); + struct device *dev = input->dev.parent; + int error; + + error = pm_runtime_get_sync(dev); + if (error < 0) + pm_runtime_put_noidle(dev); - keypad_data = input_get_drvdata(input); disable_irq(keypad_data->irq); omap4_keypad_stop(keypad_data); enable_irq(keypad_data->irq); - pm_runtime_put_sync(input->dev.parent); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static int omap4_keypad_parse_dt(struct device *dev, @@ -282,6 +308,7 @@ static int omap4_keypad_check_revision(struct device *dev, static void omap4_disable_pm(void *d) { + pm_runtime_dont_use_autosuspend(d); pm_runtime_disable(d); } @@ -313,6 +340,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) keypad_data->irq = irq; mutex_init(&keypad_data->lock); + platform_set_drvdata(pdev, keypad_data); error = omap4_keypad_parse_dt(dev, keypad_data); if (error) @@ -322,6 +350,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (IS_ERR(keypad_data->base)) return PTR_ERR(keypad_data->base); + pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); error = devm_add_action_or_reset(dev, omap4_disable_pm, dev); @@ -334,20 +363,21 @@ static int omap4_keypad_probe(struct platform_device *pdev) * Enable clocks for the keypad module so that we can read * revision register. */ - error = pm_runtime_get_sync(&pdev->dev); + error = pm_runtime_get_sync(dev); if (error) { - dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_put_noidle(&pdev->dev); + dev_err(dev, "pm_runtime_get_sync() failed\n"); + pm_runtime_put_noidle(dev); return error; } - error = omap4_keypad_check_revision(&pdev->dev, keypad_data); + error = omap4_keypad_check_revision(dev, keypad_data); if (!error) { /* Ensure device does not raise interrupts */ omap4_keypad_stop(keypad_data); } - pm_runtime_put_sync(&pdev->dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); if (error) return error; @@ -411,8 +441,6 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (error) dev_warn(dev, "failed to set up wakeup irq: %d\n", error); - platform_set_drvdata(pdev, keypad_data); - return 0; } -- cgit v1.2.3 From 2f6aa61d87e7d93c39e37b7ba2c4a9035000a0c8 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 22:20:25 -0800 Subject: Input: omap4-keypad - implement errata check for lost key-up events We are still missing handling for errata i689 related issues for the case where we never see a key up interrupt for the last pressed key. To fix the issue, we must scan the key state again after the keyboard controller has idled to check if a key up event was missed. This is described in the omap4 silicon errata documentation for Errata ID i689 "1.32 Keyboard Key Up Event Can Be Missed": "When a key is released for a time shorter than the debounce time, in-between 2 key press (KP1 and KP2), the keyboard state machine will go to idle mode and will never detect the key release (after KP1, and also after KP2), and thus will never generate a new IRQ indicating the key release." We can use PM runtime autosuspend features to check the keyboard state after it enters idle. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/X/vrygoBxzGyXhfc@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 291970c09091..43375b38ee59 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -60,6 +60,8 @@ ((((dbms) * 1000) / ((1 << ((ptv) + 1)) * (1000000 / 32768))) - 1) #define OMAP4_VAL_DEBOUNCINGTIME_16MS \ OMAP4_KEYPAD_DEBOUNCINGTIME_MS(16, OMAP4_KEYPAD_PTV_DIV_128) +#define OMAP4_KEYPAD_AUTOIDLE_MS 50 /* Approximate measured time */ +#define OMAP4_KEYPAD_IDLE_CHECK_MS (OMAP4_KEYPAD_AUTOIDLE_MS / 2) enum { KBD_REVISION_OMAP4 = 0, @@ -306,6 +308,32 @@ static int omap4_keypad_check_revision(struct device *dev, return 0; } +/* + * Errata ID i689 "1.32 Keyboard Key Up Event Can Be Missed". + * Interrupt may not happen for key-up events. We must clear stuck + * key-up events after the keyboard hardware has auto-idled. + */ +static int __maybe_unused omap4_keypad_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + u32 active; + + active = kbd_readl(keypad_data, OMAP4_KBD_STATEMACHINE); + if (active) { + pm_runtime_mark_last_busy(dev); + return -EBUSY; + } + + omap4_keypad_scan_keys(keypad_data, 0); + + return 0; +} + +static const struct dev_pm_ops omap4_keypad_pm_ops = { + SET_RUNTIME_PM_OPS(omap4_keypad_runtime_suspend, NULL, NULL) +}; + static void omap4_disable_pm(void *d) { pm_runtime_dont_use_autosuspend(d); @@ -351,6 +379,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) return PTR_ERR(keypad_data->base); pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, OMAP4_KEYPAD_IDLE_CHECK_MS); pm_runtime_enable(dev); error = devm_add_action_or_reset(dev, omap4_disable_pm, dev); @@ -463,6 +492,7 @@ static struct platform_driver omap4_keypad_driver = { .driver = { .name = "omap4-keypad", .of_match_table = omap_keypad_dt_match, + .pm = &omap4_keypad_pm_ops, }, }; module_platform_driver(omap4_keypad_driver); -- cgit v1.2.3 From a1ba9c2986e4ad3157f0dd6a6aad18cd210d2ffa Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 19 Jan 2021 18:43:11 -0800 Subject: Input: synaptics - replace NOOP with suitable commentary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following W=1 kernel build warning(s): drivers/input/mouse/synaptics.c: In function ‘synaptics_process_packet’: drivers/input/mouse/synaptics.c:1110:6: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body] Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20210114152323.2382283-2-lee.jones@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 8fb7b4385ded..ffad142801b3 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1106,8 +1106,11 @@ static void synaptics_process_packet(struct psmouse *psmouse) num_fingers = hw.w + 2; break; case 2: - if (SYN_MODEL_PEN(info->model_id)) - ; /* Nothing, treat a pen as a single finger */ + /* + * SYN_MODEL_PEN(info->model_id): even if + * the device supports pen, we treat it as + * a single finger. + */ break; case 4 ... 15: if (SYN_CAP_PALMDETECT(info->capabilities)) -- cgit v1.2.3 From 7c0c38402a61db1141dd5a2ae2f44d4cdc17a23a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 19 Jan 2021 18:17:53 -0800 Subject: Input: melfas_mip4 - mark a bunch of variables as __always_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dmitry requested to keep these around for the purposes of documentation. Fixes the following W=1 kernel build warning(s): drivers/input/touchscreen/melfas_mip4.c: In function ‘mip4_report_touch’: drivers/input/touchscreen/melfas_mip4.c:474:5: warning: variable ‘size’ set but not used [-Wunused-but-set-variable] drivers/input/touchscreen/melfas_mip4.c:472:5: warning: variable ‘pressure_stage’ set but not used [-Wunused-but-set-variable] drivers/input/touchscreen/melfas_mip4.c:469:7: warning: variable ‘palm’ set but not used [-Wunused-but-set-variable] drivers/input/touchscreen/melfas_mip4.c:468:7: warning: variable ‘hover’ set but not used [-Wunused-but-set-variable] Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20210114152323.2382283-3-lee.jones@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/melfas_mip4.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index c0050044a5a9..225796a3f546 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -465,13 +465,13 @@ static void mip4_report_keys(struct mip4_ts *ts, u8 *packet) static void mip4_report_touch(struct mip4_ts *ts, u8 *packet) { int id; - bool hover; - bool palm; + bool __always_unused hover; + bool __always_unused palm; bool state; u16 x, y; - u8 pressure_stage = 0; + u8 __always_unused pressure_stage = 0; u8 pressure; - u8 size; + u8 __always_unused size; u8 touch_major; u8 touch_minor; -- cgit v1.2.3 From 33f93726b7b28f0206e2dfbfa98b54f4415a47ad Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 19 Jan 2021 18:19:04 -0800 Subject: Input: usbtouchscreen - actually check return value of usb_submit_urb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following W=1 kernel build warning(s): drivers/input/touchscreen/usbtouchscreen.c: In function ‘nexio_read_data’: drivers/input/touchscreen/usbtouchscreen.c:1052:50: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20210114152323.2382283-4-lee.jones@linaro.org [dtor: log error code as well] Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 397cb1d3f481..c847453a03c2 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1044,6 +1044,7 @@ static void nexio_exit(struct usbtouch_usb *usbtouch) static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) { + struct device *dev = &usbtouch->interface->dev; struct nexio_touch_packet *packet = (void *) pkt; struct nexio_priv *priv = usbtouch->priv; unsigned int data_len = be16_to_cpu(packet->data_len); @@ -1062,6 +1063,8 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) /* send ACK */ ret = usb_submit_urb(priv->ack, GFP_ATOMIC); + if (ret) + dev_warn(dev, "Failed to submit ACK URB: %d\n", ret); if (!usbtouch->type->max_xc) { usbtouch->type->max_xc = 2 * x_len; -- cgit v1.2.3 From e664f0021fcaf4e4e5078e00a75db0f0bfa79256 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 19 Jan 2021 18:22:29 -0800 Subject: Input: surface3_spi - remove set but unused variable 'timestamp' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following W=1 kernel build warning(s): drivers/input/touchscreen/surface3_spi.c: In function ‘surface3_spi_process_touch’: drivers/input/touchscreen/surface3_spi.c:97:6: warning: variable ‘timestamp’ set but not used [-Wunused-but-set-variable] Signed-off-by: Lee Jones Reviewed-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20210114152323.2382283-5-lee.jones@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/surface3_spi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c index 731454599fce..1da23e5585a0 100644 --- a/drivers/input/touchscreen/surface3_spi.c +++ b/drivers/input/touchscreen/surface3_spi.c @@ -94,9 +94,7 @@ static void surface3_spi_report_touch(struct surface3_ts_data *ts_data, static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data) { - u16 timestamp; unsigned int i; - timestamp = get_unaligned_le16(&data[15]); for (i = 0; i < 13; i++) { struct surface3_ts_data_finger *finger; -- cgit v1.2.3 From 05b67b7a08fb64ecb80d365f5d745a473a020dba Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 19 Jan 2021 18:23:23 -0800 Subject: Input: stmpe-ts - add description for 'prop' struct member Fixes the following W=1 kernel build warning(s): drivers/input/touchscreen/stmpe-ts.c:82: warning: Function parameter or member 'prop' not described in 'stmpe_touch' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20210114152323.2382283-6-lee.jones@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index cd747725589b..25c45c3a3561 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -52,6 +52,7 @@ * @idev: registered input device * @work: a work item used to scan the device * @dev: a pointer back to the MFD cell struct device* + * @prop: Touchscreen properties * @ave_ctrl: Sample average control * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples) * @touch_det_delay: Touch detect interrupt delay -- cgit v1.2.3 From 6965eece2a89c3f1d00881c6052ee1e987870c08 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 20 Jan 2021 23:18:28 -0800 Subject: Input: ads7846 - convert to one message Convert multiple full duplex transfers in to a single transfer to reduce CPU load. Current driver version support following filtering modes: - ads7846_no_filter() - not filtered - ads7846_debounce_filter() - driver specific debounce filter - pdata->filter - platform specific debounce filter (do any platform provides such filter?) Without filter this HW is not really usable, since the physic of resistive touchscreen can provide some bounce effects. With driver internal filter, we have constant amount of retries + debounce retries if some anomaly was detected. High amount of tiny SPI transfers is the primer reason of high CPU load and interrupt frequency. This patch create one SPI transfer with all fields and not optional retires. If bounce anomaly was detected, we will make more transfer if needed. Without this patch, we will get about 10% CPU load on iMX6S on pen-down event. For example by holding stylus on the screen. With this patch, depending in the amount of retries, the CPU load will be 1% with "ti,debounce-rep = <3>". One buffer transfer allows us to use PIO FIFO or DMA engine, depending on the platform. Signed-off-by: Oleksij Rempel Link: https://lore.kernel.org/r/20201110085041.16303-3-o.rempel@pengutronix.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 376 ++++++++++++++++++------------------ 1 file changed, 193 insertions(+), 183 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index de058644ef8a..93fc182f5e84 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -64,24 +64,13 @@ struct ads7846_buf { u8 cmd; - /* - * This union is a temporary hack. The driver does an in-place - * endianness conversion. This will be cleaned up in the next - * patch. - */ - union { - __be16 data_be16; - u16 data; - }; + __be16 data; } __packed; - -struct ts_event { - bool ignore; - struct ads7846_buf x; - struct ads7846_buf y; - struct ads7846_buf z1; - struct ads7846_buf z2; +struct ads7846_buf_layout { + unsigned int offset; + unsigned int count; + unsigned int skip; }; /* @@ -90,12 +79,18 @@ struct ts_event { * systems where main memory is not DMA-coherent (most non-x86 boards). */ struct ads7846_packet { - struct ts_event tc; - struct ads7846_buf read_x_cmd; - struct ads7846_buf read_y_cmd; - struct ads7846_buf read_z1_cmd; - struct ads7846_buf read_z2_cmd; + unsigned int count; + unsigned int count_skip; + unsigned int cmds; + unsigned int last_cmd_idx; + struct ads7846_buf_layout l[5]; + struct ads7846_buf *rx; + struct ads7846_buf *tx; + struct ads7846_buf pwrdown_cmd; + + bool ignore; + u16 x, y, z1, z2; }; struct ads7846 { @@ -194,7 +189,6 @@ struct ads7846 { #define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref)) #define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref)) #define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref)) - #define READ_X(vref) (READ_12BIT_DFR(x, 1, vref)) #define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ @@ -207,6 +201,21 @@ struct ads7846 { #define REF_ON (READ_12BIT_DFR(x, 1, 1)) #define REF_OFF (READ_12BIT_DFR(y, 0, 0)) +/* Order commands in the most optimal way to reduce Vref switching and + * settling time: + * Measure: X; Vref: X+, X-; IN: Y+ + * Measure: Y; Vref: Y+, Y-; IN: X+ + * Measure: Z1; Vref: Y+, X-; IN: X+ + * Measure: Z2; Vref: Y+, X-; IN: Y- + */ +enum ads7846_cmds { + ADS7846_X, + ADS7846_Y, + ADS7846_Z1, + ADS7846_Z2, + ADS7846_PWDOWN, +}; + static int get_pendown_state(struct ads7846 *ts) { if (ts->get_pendown_state) @@ -689,26 +698,109 @@ static int ads7846_no_filter(void *ads, int data_idx, int *val) return ADS7846_FILTER_OK; } -static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) +static int ads7846_get_value(struct ads7846_buf *buf) { int value; - struct spi_transfer *t = - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - struct ads7846_buf *buf = t->rx_buf; - value = be16_to_cpup(&buf->data_be16); + value = be16_to_cpup(&buf->data); /* enforce ADC output is 12 bits width */ return (value >> 3) & 0xfff; } -static void ads7846_update_value(struct spi_message *m, int val) +static void ads7846_set_cmd_val(struct ads7846 *ts, enum ads7846_cmds cmd_idx, + u16 val) +{ + struct ads7846_packet *packet = ts->packet; + + switch (cmd_idx) { + case ADS7846_Y: + packet->y = val; + break; + case ADS7846_X: + packet->x = val; + break; + case ADS7846_Z1: + packet->z1 = val; + break; + case ADS7846_Z2: + packet->z2 = val; + break; + default: + WARN_ON_ONCE(1); + } +} + +static u8 ads7846_get_cmd(enum ads7846_cmds cmd_idx, int vref) +{ + switch (cmd_idx) { + case ADS7846_Y: + return READ_Y(vref); + case ADS7846_X: + return READ_X(vref); + + /* 7846 specific commands */ + case ADS7846_Z1: + return READ_Z1(vref); + case ADS7846_Z2: + return READ_Z2(vref); + case ADS7846_PWDOWN: + return PWRDOWN; + default: + WARN_ON_ONCE(1); + } + + return 0; +} + +static bool ads7846_cmd_need_settle(enum ads7846_cmds cmd_idx) +{ + switch (cmd_idx) { + case ADS7846_X: + case ADS7846_Y: + case ADS7846_Z1: + case ADS7846_Z2: + return true; + case ADS7846_PWDOWN: + return false; + default: + WARN_ON_ONCE(1); + } + + return false; +} + +static int ads7846_filter(struct ads7846 *ts) { - struct spi_transfer *t = - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - struct ads7846_buf *buf = t->rx_buf; + struct ads7846_packet *packet = ts->packet; + int action; + int val; + unsigned int cmd_idx, b; - buf->data = val; + packet->ignore = false; + for (cmd_idx = packet->last_cmd_idx; cmd_idx < packet->cmds - 1; cmd_idx++) { + struct ads7846_buf_layout *l = &packet->l[cmd_idx]; + + packet->last_cmd_idx = cmd_idx; + + for (b = l->skip; b < l->count; b++) { + val = ads7846_get_value(&packet->rx[l->offset + b]); + + action = ts->filter(ts->filter_data, cmd_idx, &val); + if (action == ADS7846_FILTER_REPEAT) { + if (b == l->count - 1) + return -EAGAIN; + } else if (action == ADS7846_FILTER_OK) { + ads7846_set_cmd_val(ts, cmd_idx, val); + break; + } else { + packet->ignore = true; + return 0; + } + } + } + + return 0; } static void ads7846_read_state(struct ads7846 *ts) @@ -716,52 +808,26 @@ static void ads7846_read_state(struct ads7846 *ts) struct ads7846_packet *packet = ts->packet; struct spi_message *m; int msg_idx = 0; - int val; - int action; int error; - while (msg_idx < ts->msg_count) { + packet->last_cmd_idx = 0; + while (true) { ts->wait_for_sync(); m = &ts->msg[msg_idx]; error = spi_sync(ts->spi, m); if (error) { dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); - packet->tc.ignore = true; + packet->ignore = true; return; } - /* - * Last message is power down request, no need to convert - * or filter the value. - */ - if (msg_idx < ts->msg_count - 1) { - - val = ads7846_get_value(ts, m); - - action = ts->filter(ts->filter_data, msg_idx, &val); - switch (action) { - case ADS7846_FILTER_REPEAT: - continue; - - case ADS7846_FILTER_IGNORE: - packet->tc.ignore = true; - msg_idx = ts->msg_count - 1; - continue; - - case ADS7846_FILTER_OK: - ads7846_update_value(m, val); - packet->tc.ignore = false; - msg_idx++; - break; + error = ads7846_filter(ts); + if (error) + continue; - default: - BUG(); - } - } else { - msg_idx++; - } + return; } } @@ -771,19 +837,14 @@ static void ads7846_report_state(struct ads7846 *ts) unsigned int Rt; u16 x, y, z1, z2; - /* - * ads7846_get_value() does in-place conversion (including byte swap) - * from on-the-wire format as part of debouncing to get stable - * readings. - */ - x = packet->tc.x.data; - y = packet->tc.y.data; + x = packet->x; + y = packet->y; if (ts->model == 7845) { z1 = 0; z2 = 0; } else { - z1 = packet->tc.z1.data; - z2 = packet->tc.z2.data; + z1 = packet->z1; + z2 = packet->z2; } /* range filtering */ @@ -816,9 +877,9 @@ static void ads7846_report_state(struct ads7846 *ts) * the maximum. Don't report it to user space, repeat at least * once more the measurement */ - if (packet->tc.ignore || Rt > ts->pressure_max) { + if (packet->ignore || Rt > ts->pressure_max) { dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", - packet->tc.ignore, Rt); + packet->ignore, Rt); return; } @@ -979,13 +1040,59 @@ static int ads7846_setup_pendown(struct spi_device *spi, * Set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. */ -static void ads7846_setup_spi_msg(struct ads7846 *ts, +static int ads7846_setup_spi_msg(struct ads7846 *ts, const struct ads7846_platform_data *pdata) { struct spi_message *m = &ts->msg[0]; struct spi_transfer *x = ts->xfer; struct ads7846_packet *packet = ts->packet; int vref = pdata->keep_vref_on; + unsigned int count, offset = 0; + unsigned int cmd_idx, b; + unsigned long time; + size_t size = 0; + + /* time per bit */ + time = NSEC_PER_SEC / ts->spi->max_speed_hz; + + count = pdata->settle_delay_usecs * NSEC_PER_USEC / time; + packet->count_skip = DIV_ROUND_UP(count, 24); + + if (ts->debounce_max && ts->debounce_rep) + /* ads7846_debounce_filter() is making ts->debounce_rep + 2 + * reads. So we need to get all samples for normal case. */ + packet->count = ts->debounce_rep + 2; + else + packet->count = 1; + + if (ts->model == 7846) + packet->cmds = 5; /* x, y, z1, z2, pwdown */ + else + packet->cmds = 3; /* x, y, pwdown */ + + for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) { + struct ads7846_buf_layout *l = &packet->l[cmd_idx]; + unsigned int max_count; + + if (ads7846_cmd_need_settle(cmd_idx)) + max_count = packet->count + packet->count_skip; + else + max_count = packet->count; + + l->offset = offset; + offset += max_count; + l->count = max_count; + l->skip = packet->count_skip; + size += sizeof(*packet->tx) * max_count; + } + + packet->tx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL); + if (!packet->tx) + return -ENOMEM; + + packet->rx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL); + if (!packet->rx) + return -ENOMEM; if (ts->model == 7873) { /* @@ -1001,117 +1108,20 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, spi_message_init(m); m->context = ts; - packet->read_y_cmd.cmd = READ_Y(vref); - x->tx_buf = &packet->read_y_cmd; - x->rx_buf = &packet->tc.y; - x->len = 3; - spi_message_add_tail(x, m); + for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) { + struct ads7846_buf_layout *l = &packet->l[cmd_idx]; + u8 cmd = ads7846_get_cmd(cmd_idx, vref); - /* - * The first sample after switching drivers can be low quality; - * optionally discard it, using a second one after the signals - * have had enough time to stabilize. - */ - if (pdata->settle_delay_usecs) { - x->delay.value = pdata->settle_delay_usecs; - x->delay.unit = SPI_DELAY_UNIT_USECS; - x++; - - x->tx_buf = &packet->read_y_cmd; - x->rx_buf = &packet->tc.y; - x->len = 3; - spi_message_add_tail(x, m); + for (b = 0; b < l->count; b++) + packet->tx[l->offset + b].cmd = cmd; } - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - /* turn y- off, x+ on, then leave in lowpower */ - x++; - packet->read_x_cmd.cmd = READ_X(vref); - x->tx_buf = &packet->read_x_cmd; - x->rx_buf = &packet->tc.x; - x->len = 3; + x->tx_buf = packet->tx; + x->rx_buf = packet->rx; + x->len = size; spi_message_add_tail(x, m); - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay.value = pdata->settle_delay_usecs; - x->delay.unit = SPI_DELAY_UNIT_USECS; - - x++; - x->tx_buf = &packet->read_x_cmd; - x->rx_buf = &packet->tc.x; - x->len = 3; - spi_message_add_tail(x, m); - } - - /* turn y+ off, x- on; we'll use formula #2 */ - if (ts->model == 7846) { - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - x++; - packet->read_z1_cmd.cmd = READ_Z1(vref); - x->tx_buf = &packet->read_z1_cmd; - x->rx_buf = &packet->tc.z1; - x->len = 3; - spi_message_add_tail(x, m); - - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay.value = pdata->settle_delay_usecs; - x->delay.unit = SPI_DELAY_UNIT_USECS; - - x++; - x->tx_buf = &packet->read_z1_cmd; - x->rx_buf = &packet->tc.z1; - x->len = 3; - spi_message_add_tail(x, m); - } - - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - x++; - packet->read_z2_cmd.cmd = READ_Z2(vref); - x->tx_buf = &packet->read_z2_cmd; - x->rx_buf = &packet->tc.z2; - x->len = 3; - spi_message_add_tail(x, m); - - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay.value = pdata->settle_delay_usecs; - x->delay.unit = SPI_DELAY_UNIT_USECS; - - x++; - x->tx_buf = &packet->read_z2_cmd; - x->rx_buf = &packet->tc.z2; - x->len = 3; - spi_message_add_tail(x, m); - } - } - - /* power down */ - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - x++; - packet->pwrdown_cmd.cmd = PWRDOWN; - x->tx_buf = &packet->pwrdown_cmd; - x->len = 3; - - CS_CHANGE(*x); - spi_message_add_tail(x, m); + return 0; } #ifdef CONFIG_OF -- cgit v1.2.3 From 9517b95bdc4699d94c2ffc6a520bb0ccec396f5b Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sun, 24 Jan 2021 14:28:01 -0800 Subject: Input: elants_i2c - add support for eKTF3624 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ELAN KTF3624 touchscreen support to the elants_i2c driver. The eKTF3624 TS is found on a series of ASUS Transformer tablet devices, Nexus 7 tablet and etc. The firmware interface of eKTF3624 is nearly identical to eKTH3500, which is already supported by the driver. The minor differences of the firmware interface are now handled by the driver. The eKTF3624 support was tested on ASUS Transformer TF700T, TF300T and Nexus 7 tablets. Signed-off-by: Michał Mirosław Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210124195414.27333-1-digetx@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elants_i2c.c | 152 +++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 17 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index d51cb910fba1..6f57ec579f00 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -56,6 +56,7 @@ #define QUEUE_HEADER_SINGLE 0x62 #define QUEUE_HEADER_NORMAL 0X63 #define QUEUE_HEADER_WAIT 0x64 +#define QUEUE_HEADER_NORMAL2 0x66 /* Command header definition */ #define CMD_HEADER_WRITE 0x54 @@ -69,6 +70,7 @@ #define CMD_HEADER_REK 0x66 /* FW position data */ +#define PACKET_SIZE_OLD 40 #define PACKET_SIZE 55 #define MAX_CONTACT_NUM 10 #define FW_POS_HEADER 0 @@ -90,6 +92,8 @@ /* FW read command, 0x53 0x?? 0x0, 0x01 */ #define E_ELAN_INFO_FW_VER 0x00 #define E_ELAN_INFO_BC_VER 0x10 +#define E_ELAN_INFO_X_RES 0x60 +#define E_ELAN_INFO_Y_RES 0x63 #define E_ELAN_INFO_REK 0xD0 #define E_ELAN_INFO_TEST_VER 0xE0 #define E_ELAN_INFO_FW_ID 0xF0 @@ -112,6 +116,11 @@ #define ELAN_POWERON_DELAY_USEC 500 #define ELAN_RESET_DELAY_MSEC 20 +enum elants_chip_id { + EKTH3500, + EKTF3624, +}; + enum elants_state { ELAN_STATE_NORMAL, ELAN_WAIT_QUEUE_HEADER, @@ -143,9 +152,12 @@ struct elants_data { unsigned int y_res; unsigned int x_max; unsigned int y_max; + unsigned int phy_x; + unsigned int phy_y; struct touchscreen_properties prop; enum elants_state state; + enum elants_chip_id chip_id; enum elants_iap_mode iap_mode; /* Guards against concurrent access to the device via sysfs */ @@ -433,7 +445,51 @@ static int elants_i2c_query_bc_version(struct elants_data *ts) return 0; } -static int elants_i2c_query_ts_info(struct elants_data *ts) +static int elants_i2c_query_ts_info_ektf(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int error; + u8 resp[4]; + u16 phy_x, phy_y; + const u8 get_xres_cmd[] = { + CMD_HEADER_READ, E_ELAN_INFO_X_RES, 0x00, 0x00 + }; + const u8 get_yres_cmd[] = { + CMD_HEADER_READ, E_ELAN_INFO_Y_RES, 0x00, 0x00 + }; + + /* Get X/Y size in mm */ + error = elants_i2c_execute_command(client, get_xres_cmd, + sizeof(get_xres_cmd), + resp, sizeof(resp), 1, + "get X size"); + if (error) + return error; + + phy_x = resp[2] | ((resp[3] & 0xF0) << 4); + + error = elants_i2c_execute_command(client, get_yres_cmd, + sizeof(get_yres_cmd), + resp, sizeof(resp), 1, + "get Y size"); + if (error) + return error; + + phy_y = resp[2] | ((resp[3] & 0xF0) << 4); + + dev_dbg(&client->dev, "phy_x=%d, phy_y=%d\n", phy_x, phy_y); + + ts->phy_x = phy_x; + ts->phy_y = phy_y; + + /* eKTF doesn't report max size, set it to default values */ + ts->x_max = 2240 - 1; + ts->y_max = 1408 - 1; + + return 0; +} + +static int elants_i2c_query_ts_info_ekth(struct elants_data *ts) { struct i2c_client *client = ts->client; int error; @@ -508,6 +564,8 @@ static int elants_i2c_query_ts_info(struct elants_data *ts) ts->x_res = DIV_ROUND_CLOSEST(ts->x_max, phy_x); ts->y_max = ELAN_TS_RESOLUTION(cols, osr); ts->y_res = DIV_ROUND_CLOSEST(ts->y_max, phy_y); + ts->phy_x = phy_x; + ts->phy_y = phy_y; } return 0; @@ -587,8 +645,20 @@ static int elants_i2c_initialize(struct elants_data *ts) error = elants_i2c_query_fw_version(ts); if (!error) error = elants_i2c_query_test_version(ts); - if (!error) - error = elants_i2c_query_ts_info(ts); + + switch (ts->chip_id) { + case EKTH3500: + if (!error) + error = elants_i2c_query_ts_info_ekth(ts); + break; + case EKTF3624: + if (!error) + error = elants_i2c_query_ts_info_ektf(ts); + break; + default: + unreachable(); + break; + } if (error) ts->iap_mode = ELAN_IAP_RECOVERY; @@ -853,7 +923,8 @@ out: * Event reporting. */ -static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) +static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf, + size_t packet_size) { struct input_dev *input = ts->input; unsigned int n_fingers; @@ -880,8 +951,24 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) pos = &buf[FW_POS_XY + i * 3]; x = (((u16)pos[0] & 0xf0) << 4) | pos[1]; y = (((u16)pos[0] & 0x0f) << 8) | pos[2]; - p = buf[FW_POS_PRESSURE + i]; - w = buf[FW_POS_WIDTH + i]; + + /* + * eKTF3624 may have use "old" touch-report format, + * depending on a device and TS firmware version. + * For example, ASUS Transformer devices use the "old" + * format, while ASUS Nexus 7 uses the "new" formant. + */ + if (packet_size == PACKET_SIZE_OLD && + ts->chip_id == EKTF3624) { + w = buf[FW_POS_WIDTH + i / 2]; + w >>= 4 * (~i & 1); + w |= w << 4; + w |= !w; + p = w; + } else { + p = buf[FW_POS_PRESSURE + i]; + w = buf[FW_POS_WIDTH + i]; + } dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n", i, x, y, p, w); @@ -913,7 +1000,8 @@ static u8 elants_i2c_calculate_checksum(u8 *buf) return checksum; } -static void elants_i2c_event(struct elants_data *ts, u8 *buf) +static void elants_i2c_event(struct elants_data *ts, u8 *buf, + size_t packet_size) { u8 checksum = elants_i2c_calculate_checksum(buf); @@ -927,7 +1015,7 @@ static void elants_i2c_event(struct elants_data *ts, u8 *buf) "%s: unknown packet type: %02x\n", __func__, buf[FW_POS_HEADER]); else - elants_i2c_mt_event(ts, buf); + elants_i2c_mt_event(ts, buf, packet_size); } static irqreturn_t elants_i2c_irq(int irq, void *_dev) @@ -970,7 +1058,6 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) switch (ts->buf[FW_HDR_TYPE]) { case CMD_HEADER_HELLO: case CMD_HEADER_RESP: - case CMD_HEADER_REK: break; case QUEUE_HEADER_WAIT: @@ -985,9 +1072,24 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) break; case QUEUE_HEADER_SINGLE: - elants_i2c_event(ts, &ts->buf[HEADER_SIZE]); + elants_i2c_event(ts, &ts->buf[HEADER_SIZE], + ts->buf[FW_HDR_LENGTH]); break; + case QUEUE_HEADER_NORMAL2: /* CMD_HEADER_REK */ + /* + * Depending on firmware version, eKTF3624 touchscreens + * may utilize one of these opcodes for the touch events: + * 0x63 (NORMAL) and 0x66 (NORMAL2). The 0x63 is used by + * older firmware version and differs from 0x66 such that + * touch pressure value needs to be adjusted. The 0x66 + * opcode of newer firmware is equal to 0x63 of eKTH3500. + */ + if (ts->chip_id != EKTF3624) + break; + + fallthrough; + case QUEUE_HEADER_NORMAL: report_count = ts->buf[FW_HDR_COUNT]; if (report_count == 0 || report_count > 3) { @@ -998,7 +1100,12 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) } report_len = ts->buf[FW_HDR_LENGTH] / report_count; - if (report_len != PACKET_SIZE) { + + if (report_len == PACKET_SIZE_OLD && + ts->chip_id == EKTF3624) { + dev_dbg_once(&client->dev, + "using old report format\n"); + } else if (report_len != PACKET_SIZE) { dev_err(&client->dev, "mismatching report length: %*ph\n", HEADER_SIZE, ts->buf); @@ -1007,8 +1114,8 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) for (i = 0; i < report_count; i++) { u8 *buf = ts->buf + HEADER_SIZE + - i * PACKET_SIZE; - elants_i2c_event(ts, buf); + i * report_len; + elants_i2c_event(ts, buf, report_len); } break; @@ -1250,6 +1357,7 @@ static int elants_i2c_probe(struct i2c_client *client, init_completion(&ts->cmd_done); ts->client = client; + ts->chip_id = (enum elants_chip_id)id->driver_data; i2c_set_clientdata(client, ts); ts->vcc33 = devm_regulator_get(&client->dev, "vcc33"); @@ -1331,13 +1439,20 @@ static int elants_i2c_probe(struct i2c_client *client, input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0); input_set_abs_params(ts->input, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0); + + touchscreen_parse_properties(ts->input, true, &ts->prop); + + if (ts->chip_id == EKTF3624) { + /* calculate resolution from size */ + ts->x_res = DIV_ROUND_CLOSEST(ts->prop.max_x, ts->phy_x); + ts->y_res = DIV_ROUND_CLOSEST(ts->prop.max_y, ts->phy_y); + } + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res); input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res); if (ts->major_res > 0) input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, ts->major_res); - touchscreen_parse_properties(ts->input, true, &ts->prop); - error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); if (error) { @@ -1466,14 +1581,16 @@ static SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops, elants_i2c_suspend, elants_i2c_resume); static const struct i2c_device_id elants_i2c_id[] = { - { DEVICE_NAME, 0 }, + { DEVICE_NAME, EKTH3500 }, + { "ekth3500", EKTH3500 }, + { "ektf3624", EKTF3624 }, { } }; MODULE_DEVICE_TABLE(i2c, elants_i2c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id elants_acpi_id[] = { - { "ELAN0001", 0 }, + { "ELAN0001", EKTH3500 }, { } }; MODULE_DEVICE_TABLE(acpi, elants_acpi_id); @@ -1482,6 +1599,7 @@ MODULE_DEVICE_TABLE(acpi, elants_acpi_id); #ifdef CONFIG_OF static const struct of_device_id elants_of_match[] = { { .compatible = "elan,ekth3500" }, + { .compatible = "elan,ektf3624" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, elants_of_match); -- cgit v1.2.3 From 785a19d97cb072c748a310d558859853f8ba987b Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:14:35 -0800 Subject: Input: iqs5xx - minor cosmetic improvements Copyrights are generally followed by the name of a person or a company (i.e. the copyright holder) but that was not done here. Fix this by squashing the 'copyright' and 'author' lines. Also, trim some leading whitespace ahead of the parameters for the fw_file_store() function and re-align them for readability. Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-2-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 4fd21bc3ce0f..08e79d6ed9f5 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -2,8 +2,7 @@ /* * Azoteq IQS550/572/525 Trackpad/Touchscreen Controller * - * Copyright (C) 2018 - * Author: Jeff LaBundy + * Copyright (C) 2018 Jeff LaBundy * * These devices require firmware exported from a PC-based configuration tool * made available by the vendor. Firmware files may be pushed to the device's @@ -952,8 +951,9 @@ err_kfree: return error; } -static ssize_t fw_file_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t fw_file_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev); struct i2c_client *client = iqs5xx->client; -- cgit v1.2.3 From 2539da6677b6355e124b99d1dbe15eb1066f1d46 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:14:50 -0800 Subject: Input: iqs5xx - preserve bootloader errors After user space writes the fw_file attribute to push new firmware to the device, the driver calls iqs5xx_dev_init() to re-initialize the device with the updated firmware or recover the device in case the update failed. In the case of the latter, however, iqs5xx_fw_file_write() returns zero (success) so long as iqs5xx_dev_init() does not fail, and any error encountered during the update process is lost. Solve this by saving the error before calling iqs5xx_dev_init(). Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-3-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 08e79d6ed9f5..ff0a0e96aa5c 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -883,7 +883,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) { struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); - int error; + int error, error_bl; u8 *pmap; if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE) @@ -937,6 +937,7 @@ err_reset: usleep_range(10000, 10100); } + error_bl = error; error = iqs5xx_dev_init(client); if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET) error = -EINVAL; @@ -948,6 +949,9 @@ err_reset: err_kfree: kfree(pmap); + if (error_bl) + return error_bl; + return error; } -- cgit v1.2.3 From 1302c71a30615226838f3583028d122f6792d720 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:17:08 -0800 Subject: Input: iqs5xx - accommodate bootloader latency The bootloader NAK's all I2C communication after the first 64-byte bulk write if the bus frequency is equal to 400 kHz. This prevents the platform from pushing updated firmware to the device. The vendor's USB bootloader programming dongle appears to insert a delay between the "open" command and the first 64-byte bulk write. Adding a similar delay to the driver seems to eliminate the issue. Furthermore, the dongle does not access the bootloader immediately after powering up the device. Follow suit by adding a delay before the "open" command to avoid wasted retries at 400 kHz. Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-4-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index ff0a0e96aa5c..b2de8c67b541 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -336,11 +336,16 @@ static int iqs5xx_bl_open(struct i2c_client *client) */ for (i = 0; i < IQS5XX_BL_ATTEMPTS; i++) { iqs5xx_reset(client); + usleep_range(350, 400); for (j = 0; j < IQS5XX_NUM_RETRIES; j++) { error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0); - if (!error || error == -EINVAL) - return error; + if (!error) + usleep_range(10000, 10100); + else if (error != -EINVAL) + continue; + + return error; } } -- cgit v1.2.3 From e10ba0d3c042161a26311d43bd37c3634d7bc5ca Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:29:38 -0800 Subject: Input: iqs5xx - re-initialize device upon warm reset The device may be inadvertently reset during runtime in the event of ESD strike, etc. To protect against this case, acknowledge the SHOW_RESET interrupt and re-initialize the device. To facilitate this change, expand the range of registers that are read in the interrupt handler to include the system status fields. Also, update the unrelated (but nearby) SUSPEND register field to use the BIT() macro. The remaining register fields are cleaned up in another patch. Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-6-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 52 ++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index b2de8c67b541..cc84bcc3395b 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -11,6 +11,7 @@ * Link to PC-based configuration tool and data sheet: http://www.azoteq.com/ */ +#include #include #include #include @@ -40,8 +41,11 @@ #define IQS5XX_PROJ_NUM_B000 15 #define IQS5XX_MAJOR_VER_MIN 2 -#define IQS5XX_RESUME 0x00 -#define IQS5XX_SUSPEND 0x01 +#define IQS5XX_SHOW_RESET BIT(7) +#define IQS5XX_ACK_RESET BIT(7) + +#define IQS5XX_SUSPEND BIT(0) +#define IQS5XX_RESUME 0 #define IQS5XX_SW_INPUT_EVENT 0x10 #define IQS5XX_SETUP_COMPLETE 0x40 @@ -53,8 +57,8 @@ #define IQS5XX_SWITCH_XY_AXIS 0x04 #define IQS5XX_PROD_NUM 0x0000 -#define IQS5XX_ABS_X 0x0016 -#define IQS5XX_ABS_Y 0x0018 +#define IQS5XX_SYS_INFO0 0x000F +#define IQS5XX_SYS_INFO1 0x0010 #define IQS5XX_SYS_CTRL0 0x0431 #define IQS5XX_SYS_CTRL1 0x0432 #define IQS5XX_SYS_CFG0 0x058E @@ -125,6 +129,14 @@ struct iqs5xx_touch_data { u8 area; } __packed; +struct iqs5xx_status { + u8 sys_info[2]; + u8 num_active; + __be16 rel_x; + __be16 rel_y; + struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS]; +} __packed; + static int iqs5xx_read_burst(struct i2c_client *client, u16 reg, void *val, u16 len) { @@ -670,6 +682,10 @@ static int iqs5xx_dev_init(struct i2c_client *client) if (error) return error; + error = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL0, IQS5XX_ACK_RESET); + if (error) + return error; + error = iqs5xx_read_byte(client, IQS5XX_SYS_CFG0, &val); if (error) return error; @@ -706,7 +722,7 @@ static int iqs5xx_dev_init(struct i2c_client *client) static irqreturn_t iqs5xx_irq(int irq, void *data) { struct iqs5xx_private *iqs5xx = data; - struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS]; + struct iqs5xx_status status; struct i2c_client *client = iqs5xx->client; struct input_dev *input = iqs5xx->input; int error, i; @@ -719,21 +735,35 @@ static irqreturn_t iqs5xx_irq(int irq, void *data) if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET) return IRQ_NONE; - error = iqs5xx_read_burst(client, IQS5XX_ABS_X, - touch_data, sizeof(touch_data)); + error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0, + &status, sizeof(status)); if (error) return IRQ_NONE; - for (i = 0; i < ARRAY_SIZE(touch_data); i++) { - u16 pressure = be16_to_cpu(touch_data[i].strength); + if (status.sys_info[0] & IQS5XX_SHOW_RESET) { + dev_err(&client->dev, "Unexpected device reset\n"); + + error = iqs5xx_dev_init(client); + if (error) { + dev_err(&client->dev, + "Failed to re-initialize device: %d\n", error); + return IRQ_NONE; + } + + return IRQ_HANDLED; + } + + for (i = 0; i < ARRAY_SIZE(status.touch_data); i++) { + struct iqs5xx_touch_data *touch_data = &status.touch_data[i]; + u16 pressure = be16_to_cpu(touch_data->strength); input_mt_slot(input, i); if (input_mt_report_slot_state(input, MT_TOOL_FINGER, pressure != 0)) { input_report_abs(input, ABS_MT_POSITION_X, - be16_to_cpu(touch_data[i].abs_x)); + be16_to_cpu(touch_data->abs_x)); input_report_abs(input, ABS_MT_POSITION_Y, - be16_to_cpu(touch_data[i].abs_y)); + be16_to_cpu(touch_data->abs_y)); input_report_abs(input, ABS_MT_PRESSURE, pressure); } } -- cgit v1.2.3 From 4a76d861d9182f2edfab96e7aa1f1e10f4f86bc0 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:33:37 -0800 Subject: Input: iqs5xx - simplify axis setup logic The present implementation manipulates axis swap and inversion fields in the device to more or less duplicate what touchscreen_report_pos() does. The resulting logic is convoluted and difficult to follow. Instead report the maximum X and Y coordinates in earnest as they are read from the device, then let touchscreen_parse_properties() fix the axes up as necessary. Finally, use touchscreen_report_pos() to report the transformed coordinates. Last but not least, the maximum X and Y coordinates are not functions of the number of rows/columns that comprise the touch surface. Either coordinate is simply limited to 1 below what is reported for absolute X or Y coordinates when no fingers are present (0xFFFF). Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-7-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 100 ++++++++----------------------------- 1 file changed, 21 insertions(+), 79 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index cc84bcc3395b..127687ac160e 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -30,9 +30,9 @@ #define IQS5XX_FW_FILE_LEN 64 #define IQS5XX_NUM_RETRIES 10 -#define IQS5XX_NUM_POINTS 256 #define IQS5XX_NUM_CONTACTS 5 #define IQS5XX_WR_BYTES_MAX 2 +#define IQS5XX_XY_RES_MAX 0xFFFE #define IQS5XX_PROD_NUM_IQS550 40 #define IQS5XX_PROD_NUM_IQS572 58 @@ -52,10 +52,6 @@ #define IQS5XX_EVENT_MODE 0x01 #define IQS5XX_TP_EVENT 0x04 -#define IQS5XX_FLIP_X 0x01 -#define IQS5XX_FLIP_Y 0x02 -#define IQS5XX_SWITCH_XY_AXIS 0x04 - #define IQS5XX_PROD_NUM 0x0000 #define IQS5XX_SYS_INFO0 0x000F #define IQS5XX_SYS_INFO1 0x0010 @@ -63,9 +59,6 @@ #define IQS5XX_SYS_CTRL1 0x0432 #define IQS5XX_SYS_CFG0 0x058E #define IQS5XX_SYS_CFG1 0x058F -#define IQS5XX_TOTAL_RX 0x063D -#define IQS5XX_TOTAL_TX 0x063E -#define IQS5XX_XY_CFG0 0x0669 #define IQS5XX_X_RES 0x066E #define IQS5XX_Y_RES 0x0670 #define IQS5XX_CHKSM 0x83C0 @@ -102,6 +95,7 @@ struct iqs5xx_private { struct i2c_client *client; struct input_dev *input; struct gpio_desc *reset_gpio; + struct touchscreen_properties prop; struct mutex lock; u8 bl_status; }; @@ -497,12 +491,10 @@ static void iqs5xx_close(struct input_dev *input) static int iqs5xx_axis_init(struct i2c_client *client) { struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); - struct touchscreen_properties prop; + struct touchscreen_properties *prop = &iqs5xx->prop; struct input_dev *input; + u16 max_x, max_y; int error; - u16 max_x, max_x_hw; - u16 max_y, max_y_hw; - u8 val; if (!iqs5xx->input) { input = devm_input_allocate_device(&client->dev); @@ -522,89 +514,39 @@ static int iqs5xx_axis_init(struct i2c_client *client) iqs5xx->input = input; } - touchscreen_parse_properties(iqs5xx->input, true, &prop); - - error = iqs5xx_read_byte(client, IQS5XX_TOTAL_RX, &val); - if (error) - return error; - max_x_hw = (val - 1) * IQS5XX_NUM_POINTS; - - error = iqs5xx_read_byte(client, IQS5XX_TOTAL_TX, &val); + error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x); if (error) return error; - max_y_hw = (val - 1) * IQS5XX_NUM_POINTS; - error = iqs5xx_read_byte(client, IQS5XX_XY_CFG0, &val); + error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y); if (error) return error; - if (val & IQS5XX_SWITCH_XY_AXIS) - swap(max_x_hw, max_y_hw); - - if (prop.swap_x_y) - val ^= IQS5XX_SWITCH_XY_AXIS; + input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_X, max_x); + input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_Y, max_y); - if (prop.invert_x) - val ^= prop.swap_x_y ? IQS5XX_FLIP_Y : IQS5XX_FLIP_X; + touchscreen_parse_properties(iqs5xx->input, true, prop); - if (prop.invert_y) - val ^= prop.swap_x_y ? IQS5XX_FLIP_X : IQS5XX_FLIP_Y; - - error = iqs5xx_write_byte(client, IQS5XX_XY_CFG0, val); - if (error) - return error; - - if (prop.max_x > max_x_hw) { + if (prop->max_x > IQS5XX_XY_RES_MAX) { dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n", - prop.max_x, max_x_hw); + prop->max_x, IQS5XX_XY_RES_MAX); return -EINVAL; - } else if (prop.max_x == 0) { - error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x); + } else if (prop->max_x != max_x) { + error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x); if (error) return error; - - input_abs_set_max(iqs5xx->input, - prop.swap_x_y ? ABS_MT_POSITION_Y : - ABS_MT_POSITION_X, - max_x); - } else { - max_x = (u16)prop.max_x; } - if (prop.max_y > max_y_hw) { + if (prop->max_y > IQS5XX_XY_RES_MAX) { dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n", - prop.max_y, max_y_hw); + prop->max_y, IQS5XX_XY_RES_MAX); return -EINVAL; - } else if (prop.max_y == 0) { - error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y); + } else if (prop->max_y != max_y) { + error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y); if (error) return error; - - input_abs_set_max(iqs5xx->input, - prop.swap_x_y ? ABS_MT_POSITION_X : - ABS_MT_POSITION_Y, - max_y); - } else { - max_y = (u16)prop.max_y; } - /* - * Write horizontal and vertical resolution to the device in case its - * original defaults were overridden or swapped as per the properties - * specified in the device tree. - */ - error = iqs5xx_write_word(client, - prop.swap_x_y ? IQS5XX_Y_RES : IQS5XX_X_RES, - max_x); - if (error) - return error; - - error = iqs5xx_write_word(client, - prop.swap_x_y ? IQS5XX_X_RES : IQS5XX_Y_RES, - max_y); - if (error) - return error; - error = input_mt_init_slots(iqs5xx->input, IQS5XX_NUM_CONTACTS, INPUT_MT_DIRECT); if (error) @@ -760,10 +702,10 @@ static irqreturn_t iqs5xx_irq(int irq, void *data) input_mt_slot(input, i); if (input_mt_report_slot_state(input, MT_TOOL_FINGER, pressure != 0)) { - input_report_abs(input, ABS_MT_POSITION_X, - be16_to_cpu(touch_data->abs_x)); - input_report_abs(input, ABS_MT_POSITION_Y, - be16_to_cpu(touch_data->abs_y)); + touchscreen_report_pos(iqs5xx->input, &iqs5xx->prop, + be16_to_cpu(touch_data->abs_x), + be16_to_cpu(touch_data->abs_y), + true); input_report_abs(input, ABS_MT_PRESSURE, pressure); } } -- cgit v1.2.3 From 050fac7f056b23764a69fb1fcf3b4e4e90eb61b0 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:40:51 -0800 Subject: Input: iqs5xx - eliminate unnecessary register read Instead of relying on firmware to enable important register fields and performing read-modify-write operations to additionally enable the fields the driver cares about, it's much simpler just to write all of the pertinent fields explicitly. This avoids an unnecessary register read operation at start-up and makes way for the iqs5xx_read_byte() helper to be dropped. Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-8-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 127687ac160e..35da66bfd87f 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -47,10 +47,13 @@ #define IQS5XX_SUSPEND BIT(0) #define IQS5XX_RESUME 0 -#define IQS5XX_SW_INPUT_EVENT 0x10 -#define IQS5XX_SETUP_COMPLETE 0x40 -#define IQS5XX_EVENT_MODE 0x01 -#define IQS5XX_TP_EVENT 0x04 +#define IQS5XX_SETUP_COMPLETE BIT(6) +#define IQS5XX_WDT BIT(5) +#define IQS5XX_ALP_REATI BIT(3) +#define IQS5XX_REATI BIT(2) + +#define IQS5XX_TP_EVENT BIT(2) +#define IQS5XX_EVENT_MODE BIT(0) #define IQS5XX_PROD_NUM 0x0000 #define IQS5XX_SYS_INFO0 0x000F @@ -187,11 +190,6 @@ static int iqs5xx_read_word(struct i2c_client *client, u16 reg, u16 *val) return 0; } -static int iqs5xx_read_byte(struct i2c_client *client, u16 reg, u8 *val) -{ - return iqs5xx_read_burst(client, reg, val, sizeof(*val)); -} - static int iqs5xx_write_burst(struct i2c_client *client, u16 reg, const void *val, u16 len) { @@ -561,7 +559,6 @@ static int iqs5xx_dev_init(struct i2c_client *client) struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); struct iqs5xx_dev_id_info *dev_id_info; int error; - u8 val; u8 buf[sizeof(*dev_id_info) + 1]; error = iqs5xx_read_burst(client, IQS5XX_PROD_NUM, @@ -628,18 +625,14 @@ static int iqs5xx_dev_init(struct i2c_client *client) if (error) return error; - error = iqs5xx_read_byte(client, IQS5XX_SYS_CFG0, &val); - if (error) - return error; - - val |= IQS5XX_SETUP_COMPLETE; - val &= ~IQS5XX_SW_INPUT_EVENT; - error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0, val); + error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0, + IQS5XX_SETUP_COMPLETE | IQS5XX_WDT | + IQS5XX_ALP_REATI | IQS5XX_REATI); if (error) return error; - val = IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE; - error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1, val); + error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1, + IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE); if (error) return error; -- cgit v1.2.3 From 8e6a8b0c9fe98b905a2ae4f9f91296eb4f82b9ae Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:41:38 -0800 Subject: Input: iqs5xx - allow more time for ATI to complete After the device is initialized, it runs ATI (calibration) during which it cannot readily respond to I2C communication. To keep the open and close callbacks from writing to the device too soon, the driver waits 100 ms before returning from probe. The vendor reports that ATI may actually take up to 250 ms to run (including margin), so increase the delay accordingly. Update the comments to clarify the reason for the delay as well. Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-9-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 35da66bfd87f..b5287301a293 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -643,13 +643,12 @@ static int iqs5xx_dev_init(struct i2c_client *client) iqs5xx->bl_status = dev_id_info->bl_status; /* - * Closure of the first communication window that appears following the - * release of reset appears to kick off an initialization period during - * which further communication is met with clock stretching. The return - * from this function is delayed so that further communication attempts - * avoid this period. + * The following delay allows ATI to complete before the open and close + * callbacks are free to elicit I2C communication. Any attempts to read + * from or write to the device during this time may face extended clock + * stretching and prompt the I2C controller to report an error. */ - msleep(100); + msleep(250); return 0; } -- cgit v1.2.3 From ce996aa30ef1d20bb8c84aa78087bb07b4745317 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 24 Jan 2021 20:44:26 -0800 Subject: Input: iqs5xx - allow device to be a wake-up source Avoid placing the device in suspend mode (from which it cannot generate interrupts) if it is defined as a wake-up source. The device is still permitted to enter a low-power sensing mode on its own. Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611002626-5889-11-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index b5287301a293..05e0c6ff217b 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -985,7 +985,7 @@ static int __maybe_unused iqs5xx_suspend(struct device *dev) struct input_dev *input = iqs5xx->input; int error = 0; - if (!input) + if (!input || device_may_wakeup(dev)) return error; mutex_lock(&input->mutex); @@ -1004,7 +1004,7 @@ static int __maybe_unused iqs5xx_resume(struct device *dev) struct input_dev *input = iqs5xx->input; int error = 0; - if (!input) + if (!input || device_may_wakeup(dev)) return error; mutex_lock(&input->mutex); -- cgit v1.2.3 From 3bdffa8ffb45c4f217d31a9250e90432ac31b35e Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Sun, 24 Jan 2021 20:53:53 -0800 Subject: Input: Add N64 controller driver This adds support for the four built-in controller ports on the Nintendo 64 console. The N64 controller includes an analog stick, a d-pad, and several buttons. No module support as the target has only 8mb ram. Signed-off-by: Lauri Kasanen Link: https://lore.kernel.org/r/20210115133408.0acd70163b582b77ad0a029b@gmx.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/Kconfig | 7 + drivers/input/joystick/Makefile | 2 +- drivers/input/joystick/n64joy.c | 345 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 drivers/input/joystick/n64joy.c (limited to 'drivers/input') diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index b080f0cfb068..5e38899058c1 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -382,4 +382,11 @@ config JOYSTICK_FSIA6B To compile this driver as a module, choose M here: the module will be called fsia6b. +config JOYSTICK_N64 + bool "N64 controller" + depends on MACH_NINTENDO64 + help + Say Y here if you want enable support for the four + built-in controller ports on the Nintendo 64 console. + endif diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index 58232b3057d3..31d720c9e493 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o +obj-$(CONFIG_JOYSTICK_N64) += n64joy.o obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o @@ -37,4 +38,3 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o - diff --git a/drivers/input/joystick/n64joy.c b/drivers/input/joystick/n64joy.c new file mode 100644 index 000000000000..8bcc529942bc --- /dev/null +++ b/drivers/input/joystick/n64joy.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for the four N64 controllers. + * + * Copyright (c) 2021 Lauri Kasanen + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Lauri Kasanen "); +MODULE_DESCRIPTION("Driver for N64 controllers"); +MODULE_LICENSE("GPL"); + +#define PIF_RAM 0x1fc007c0 + +#define SI_DRAM_REG 0 +#define SI_READ_REG 1 +#define SI_WRITE_REG 4 +#define SI_STATUS_REG 6 + +#define SI_STATUS_DMA_BUSY BIT(0) +#define SI_STATUS_IO_BUSY BIT(1) + +#define N64_CONTROLLER_ID 0x0500 + +#define MAX_CONTROLLERS 4 + +static const char *n64joy_phys[MAX_CONTROLLERS] = { + "n64joy/port0", + "n64joy/port1", + "n64joy/port2", + "n64joy/port3", +}; + +struct n64joy_priv { + u64 si_buf[8] ____cacheline_aligned; + struct timer_list timer; + struct mutex n64joy_mutex; + struct input_dev *n64joy_dev[MAX_CONTROLLERS]; + u32 __iomem *reg_base; + u8 n64joy_opened; +}; + +struct joydata { + unsigned int: 16; /* unused */ + unsigned int err: 2; + unsigned int: 14; /* unused */ + + union { + u32 data; + + struct { + unsigned int a: 1; + unsigned int b: 1; + unsigned int z: 1; + unsigned int start: 1; + unsigned int up: 1; + unsigned int down: 1; + unsigned int left: 1; + unsigned int right: 1; + unsigned int: 2; /* unused */ + unsigned int l: 1; + unsigned int r: 1; + unsigned int c_up: 1; + unsigned int c_down: 1; + unsigned int c_left: 1; + unsigned int c_right: 1; + signed int x: 8; + signed int y: 8; + }; + }; +}; + +static void n64joy_write_reg(u32 __iomem *reg_base, const u8 reg, const u32 value) +{ + writel(value, reg_base + reg); +} + +static u32 n64joy_read_reg(u32 __iomem *reg_base, const u8 reg) +{ + return readl(reg_base + reg); +} + +static void n64joy_wait_si_dma(u32 __iomem *reg_base) +{ + while (n64joy_read_reg(reg_base, SI_STATUS_REG) & + (SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY)) + cpu_relax(); +} + +static void n64joy_exec_pif(struct n64joy_priv *priv, const u64 in[8]) +{ + unsigned long flags; + + dma_cache_wback_inv((unsigned long) in, 8 * 8); + dma_cache_inv((unsigned long) priv->si_buf, 8 * 8); + + local_irq_save(flags); + + n64joy_wait_si_dma(priv->reg_base); + + barrier(); + n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(in)); + barrier(); + n64joy_write_reg(priv->reg_base, SI_WRITE_REG, PIF_RAM); + barrier(); + + n64joy_wait_si_dma(priv->reg_base); + + barrier(); + n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(priv->si_buf)); + barrier(); + n64joy_write_reg(priv->reg_base, SI_READ_REG, PIF_RAM); + barrier(); + + n64joy_wait_si_dma(priv->reg_base); + + local_irq_restore(flags); +} + +static const u64 polldata[] ____cacheline_aligned = { + 0xff010401ffffffff, + 0xff010401ffffffff, + 0xff010401ffffffff, + 0xff010401ffffffff, + 0xfe00000000000000, + 0, + 0, + 1 +}; + +static void n64joy_poll(struct timer_list *t) +{ + const struct joydata *data; + struct n64joy_priv *priv = container_of(t, struct n64joy_priv, timer); + struct input_dev *dev; + u32 i; + + n64joy_exec_pif(priv, polldata); + + data = (struct joydata *) priv->si_buf; + + for (i = 0; i < MAX_CONTROLLERS; i++) { + if (!priv->n64joy_dev[i]) + continue; + + dev = priv->n64joy_dev[i]; + + /* d-pad */ + input_report_key(dev, BTN_DPAD_UP, data[i].up); + input_report_key(dev, BTN_DPAD_DOWN, data[i].down); + input_report_key(dev, BTN_DPAD_LEFT, data[i].left); + input_report_key(dev, BTN_DPAD_RIGHT, data[i].right); + + /* c buttons */ + input_report_key(dev, BTN_FORWARD, data[i].c_up); + input_report_key(dev, BTN_BACK, data[i].c_down); + input_report_key(dev, BTN_LEFT, data[i].c_left); + input_report_key(dev, BTN_RIGHT, data[i].c_right); + + /* matching buttons */ + input_report_key(dev, BTN_START, data[i].start); + input_report_key(dev, BTN_Z, data[i].z); + + /* remaining ones: a, b, l, r */ + input_report_key(dev, BTN_0, data[i].a); + input_report_key(dev, BTN_1, data[i].b); + input_report_key(dev, BTN_2, data[i].l); + input_report_key(dev, BTN_3, data[i].r); + + input_report_abs(dev, ABS_X, data[i].x); + input_report_abs(dev, ABS_Y, data[i].y); + + input_sync(dev); + } + + mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16)); +} + +static int n64joy_open(struct input_dev *dev) +{ + struct n64joy_priv *priv = input_get_drvdata(dev); + int err; + + err = mutex_lock_interruptible(&priv->n64joy_mutex); + if (err) + return err; + + if (!priv->n64joy_opened) { + /* + * We could use the vblank irq, but it's not important if + * the poll point slightly changes. + */ + timer_setup(&priv->timer, n64joy_poll, 0); + mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16)); + } + + priv->n64joy_opened++; + + mutex_unlock(&priv->n64joy_mutex); + return err; +} + +static void n64joy_close(struct input_dev *dev) +{ + struct n64joy_priv *priv = input_get_drvdata(dev); + + mutex_lock(&priv->n64joy_mutex); + if (!--priv->n64joy_opened) + del_timer_sync(&priv->timer); + mutex_unlock(&priv->n64joy_mutex); +} + +static const u64 __initconst scandata[] ____cacheline_aligned = { + 0xff010300ffffffff, + 0xff010300ffffffff, + 0xff010300ffffffff, + 0xff010300ffffffff, + 0xfe00000000000000, + 0, + 0, + 1 +}; + +/* + * The target device is embedded and RAM-constrained. We save RAM + * by initializing in __init code that gets dropped late in boot. + * For the same reason there is no module or unloading support. + */ +static int __init n64joy_probe(struct platform_device *pdev) +{ + const struct joydata *data; + struct n64joy_priv *priv; + struct input_dev *dev; + int err = 0; + u32 i, j, found = 0; + + priv = kzalloc(sizeof(struct n64joy_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + mutex_init(&priv->n64joy_mutex); + + priv->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (!priv->reg_base) { + err = -EINVAL; + goto fail; + } + + /* The controllers are not hotpluggable, so we can scan in init */ + n64joy_exec_pif(priv, scandata); + + data = (struct joydata *) priv->si_buf; + + for (i = 0; i < MAX_CONTROLLERS; i++) { + if (!data[i].err && data[i].data >> 16 == N64_CONTROLLER_ID) { + found++; + + dev = priv->n64joy_dev[i] = input_allocate_device(); + if (!priv->n64joy_dev[i]) { + err = -ENOMEM; + goto fail; + } + + input_set_drvdata(dev, priv); + + dev->name = "N64 controller"; + dev->phys = n64joy_phys[i]; + dev->id.bustype = BUS_HOST; + dev->id.vendor = 0; + dev->id.product = data[i].data >> 16; + dev->id.version = 0; + dev->dev.parent = &pdev->dev; + + dev->open = n64joy_open; + dev->close = n64joy_close; + + /* d-pad */ + input_set_capability(dev, EV_KEY, BTN_DPAD_UP); + input_set_capability(dev, EV_KEY, BTN_DPAD_DOWN); + input_set_capability(dev, EV_KEY, BTN_DPAD_LEFT); + input_set_capability(dev, EV_KEY, BTN_DPAD_RIGHT); + /* c buttons */ + input_set_capability(dev, EV_KEY, BTN_LEFT); + input_set_capability(dev, EV_KEY, BTN_RIGHT); + input_set_capability(dev, EV_KEY, BTN_FORWARD); + input_set_capability(dev, EV_KEY, BTN_BACK); + /* matching buttons */ + input_set_capability(dev, EV_KEY, BTN_START); + input_set_capability(dev, EV_KEY, BTN_Z); + /* remaining ones: a, b, l, r */ + input_set_capability(dev, EV_KEY, BTN_0); + input_set_capability(dev, EV_KEY, BTN_1); + input_set_capability(dev, EV_KEY, BTN_2); + input_set_capability(dev, EV_KEY, BTN_3); + + for (j = 0; j < 2; j++) + input_set_abs_params(dev, ABS_X + j, + S8_MIN, S8_MAX, 0, 0); + + err = input_register_device(dev); + if (err) { + input_free_device(dev); + goto fail; + } + } + } + + pr_info("%u controller(s) connected\n", found); + + if (!found) + return -ENODEV; + + return 0; +fail: + for (i = 0; i < MAX_CONTROLLERS; i++) { + if (!priv->n64joy_dev[i]) + continue; + input_unregister_device(priv->n64joy_dev[i]); + } + return err; +} + +static struct platform_driver n64joy_driver = { + .driver = { + .name = "n64joy", + }, +}; + +static int __init n64joy_init(void) +{ + return platform_driver_probe(&n64joy_driver, n64joy_probe); +} + +module_init(n64joy_init); -- cgit v1.2.3 From 7a6a53b2b1a3e68b69cd75a74783f4d8fd5b6fb5 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Mon, 25 Jan 2021 10:13:14 -0800 Subject: Input: iqs5xx - initialize an uninitialized variable If execution jumps to the err_kfree label, error_bl is evaluated before it is initialized. Fix this by initializing it to zero. Fixes: 2539da6677b6 ("Input: iqs5xx - preserve bootloader errors") Reported-by: kernel test robot Signed-off-by: Jeff LaBundy Link: https://lore.kernel.org/r/1611592500-32209-1-git-send-email-jeff@labundy.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 05e0c6ff217b..54f30038dca4 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -852,7 +852,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) { struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); - int error, error_bl; + int error, error_bl = 0; u8 *pmap; if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE) -- cgit v1.2.3 From a374c19f7f15e3b2c85b3d8753c63e16dbb22d2e Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 5 Feb 2021 11:50:52 -0800 Subject: Input: zinitix - remove unneeded semicolon Eliminate the following coccicheck warning: ./drivers/input/touchscreen/zinitix.c:164:31-32: Unneeded semicolon Reported-by: Abaci Robot Signed-off-by: Yang Li Link: https://lore.kernel.org/r/1612319443-115831-1-git-send-email-yang.lee@linux.alibaba.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zinitix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index a3e3adbabc67..f64d88170fac 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -161,7 +161,7 @@ static int zinitix_read_data(struct i2c_client *client, ret = i2c_master_recv(client, (u8 *)values, length); if (ret != length) - return ret < 0 ? ret : -EIO; ; + return ret < 0 ? ret : -EIO; return 0; } -- cgit v1.2.3 From ede6747c2f8975892ab98bed94357dc8c35d790c Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 11 Feb 2021 12:50:37 -0800 Subject: Input: elants_i2c - detect enum overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an enum value were to get added without updating this switch statement, the unreachable() annotation would trigger undefined behavior, causing execution to fall through the end of the function, into the next one. Make the error handling more robust for an unexpected enum value, by doing BUG() instead of unreachable(). Fixes the following objtool warning: drivers/input/touchscreen/elants_i2c.o: warning: objtool: elants_i2c_initialize() falls through to next function elants_i2c_resume() Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Josh Poimboeuf Reviewed-by: Michał Mirosław Link: https://lore.kernel.org/r/59e2e82d1e40df11ab38874c03556a31c6b2f484.1612974132.git.jpoimboe@redhat.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elants_i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 6f57ec579f00..4c2b579f6c8b 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -656,8 +656,7 @@ static int elants_i2c_initialize(struct elants_data *ts) error = elants_i2c_query_ts_info_ektf(ts); break; default: - unreachable(); - break; + BUG(); } if (error) -- cgit v1.2.3 From b0b7d2815839024e5181bd2572f5d8d4f65363b3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 16 Feb 2021 20:30:45 -0800 Subject: Input: sur40 - fix an error code in sur40_probe() If v4l2_ctrl_handler_setup() fails then probe() should return an error code instead of returning success. Fixes: cee1e3e2ef39 ("media: add video control handlers using V4L2 control framework") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/YBKFkbATXa5fA3xj@mwanda Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 620cdd7d214a..12f2562b0141 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -787,6 +787,7 @@ static int sur40_probe(struct usb_interface *interface, dev_err(&interface->dev, "Unable to register video controls."); v4l2_ctrl_handler_free(&sur40->hdl); + error = sur40->hdl.error; goto err_unreg_v4l2; } -- cgit v1.2.3 From 0958351e93fa0ac142f6dd8bd844441594f30a57 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 16 Feb 2021 20:29:05 -0800 Subject: Input: elo - fix an error code in elo_connect() If elo_setup_10() fails then this should return an error code instead of success. Fixes: fae3006e4b42 ("Input: elo - add support for non-pressure-sensitive touchscreens") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/YBKFd5CvDu+jVmfW@mwanda Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index e0bacd34866a..96173232e53f 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -341,8 +341,10 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) switch (elo->id) { case 0: /* 10-byte protocol */ - if (elo_setup_10(elo)) + if (elo_setup_10(elo)) { + err = -EIO; goto fail3; + } break; -- cgit v1.2.3 From 182d679b2298d62bf42bb14b12a8067b8e17b617 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 17 Feb 2021 12:21:10 -0800 Subject: Input: joydev - prevent potential read overflow in ioctl The problem here is that "len" might be less than "joydev->nabs" so the loops which verfy abspam[i] and keypam[] might read beyond the buffer. Fixes: 999b874f4aa3 ("Input: joydev - validate axis/button maps before clobbering current ones") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/YCyzR8WvFRw4HWw6@mwanda [dtor: additional check for len being even in joydev_handle_JSIOCSBTNMAP] Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joydev.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index a2b5fbba2d3b..430dc6975004 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -456,7 +456,7 @@ static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev, if (IS_ERR(abspam)) return PTR_ERR(abspam); - for (i = 0; i < joydev->nabs; i++) { + for (i = 0; i < len && i < joydev->nabs; i++) { if (abspam[i] > ABS_MAX) { retval = -EINVAL; goto out; @@ -480,6 +480,9 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev, int i; int retval = 0; + if (len % sizeof(*keypam)) + return -EINVAL; + len = min(len, sizeof(joydev->keypam)); /* Validate the map. */ @@ -487,7 +490,7 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev, if (IS_ERR(keypam)) return PTR_ERR(keypam); - for (i = 0; i < joydev->nkey; i++) { + for (i = 0; i < (len / 2) && i < joydev->nkey; i++) { if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) { retval = -EINVAL; goto out; -- cgit v1.2.3 From b2e3543b5e193c2be802ae2db0a8ae82ec8c0f66 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 19 Feb 2021 10:39:16 -0800 Subject: Input: add missing dependencies on CONFIG_HAS_IOMEM devm_ioremap_resource() is only guaranteed to be present if CONFIG_HAS_IOMEM is set. Reported-by: kernel test robot Link: https://lore.kernel.org/r/YCyauGyqxut69JNz@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 4 ++-- drivers/input/serio/Kconfig | 2 +- drivers/input/touchscreen/Kconfig | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 94eab82086b2..32d15809ae58 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -685,7 +685,7 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4+ keypad support" - depends on OF || ARCH_OMAP2PLUS + depends on (OF && HAS_IOMEM) || ARCH_OMAP2PLUS select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP4+ keypad. @@ -773,7 +773,7 @@ config KEYBOARD_CAP11XX config KEYBOARD_BCM tristate "Broadcom keypad driver" - depends on OF && HAVE_CLK + depends on OF && HAVE_CLK && HAS_IOMEM select INPUT_MATRIXKMAP default ARCH_BCM_CYGNUS help diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 0754744b9ce5..f39b7b3f7942 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -255,7 +255,7 @@ config SERIO_ARC_PS2 config SERIO_APBPS2 tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller" - depends on OF + depends on OF && HAS_IOMEM help Say Y here if you want support for GRLIB APBPS2 peripherals used to connect to PS/2 keyboard and/or mouse. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f012fe746df0..d1b6ef9fd8d4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -607,7 +607,7 @@ config TOUCHSCREEN_MTOUCH config TOUCHSCREEN_IMX6UL_TSC tristate "Freescale i.MX6UL touchscreen controller" - depends on (OF && GPIOLIB) || COMPILE_TEST + depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM help Say Y here if you have a Freescale i.MX6UL, and want to use the internal touchscreen controller. -- cgit v1.2.3 From b5d6e7ab7fe7d186878142e9fc1a05e4c3b65eb9 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Fri, 19 Feb 2021 10:37:13 -0800 Subject: Input: i8042 - add ASUS Zenbook Flip to noselftest list After commit 77b425399f6d ("Input: i8042 - use chassis info to skip selftest on Asus laptops"), all modern Asus laptops have the i8042 selftest disabled. It has done by using chassys type "10" (laptop). The Asus Zenbook Flip suffers from similar suspend/resume issues, but it _sometimes_ work and sometimes it doesn't. Setting noselftest makes it work reliably. In this case, we need to add chassis type "31" (convertible) in order to avoid selftest in this device. Reported-by: Ludvig Norgren Guldhag Signed-off-by: Marcos Paulo de Souza Link: https://lore.kernel.org/r/20210219164638.761-1-mpdesouza@suse.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a4c9b9652560..d3fb1bc66f43 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -582,6 +582,10 @@ static const struct dmi_system_id i8042_dmi_noselftest_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ }, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + }, }, { } }; -- cgit v1.2.3 From 836f308cb5c72d48e2dff8d3e64c3adb94f4710d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 19 Feb 2021 10:36:48 -0800 Subject: Input: zinitix - fix return type of zinitix_init_touch() zinitix_init_touch() returns error code or 0 for success and therefore return type must be int, not bool. Fixes: 26822652c85e ("Input: add zinitix touchscreen driver") Reported-by: kernel test robot Reported-by: Jiapeng Chong Link: https://lore.kernel.org/r/YC8z2bXc3Oy8pABa@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zinitix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index f64d88170fac..3b636beb583c 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -190,7 +190,7 @@ static int zinitix_write_cmd(struct i2c_client *client, u16 reg) return 0; } -static bool zinitix_init_touch(struct bt541_ts_data *bt541) +static int zinitix_init_touch(struct bt541_ts_data *bt541) { struct i2c_client *client = bt541->client; int i; -- cgit v1.2.3 From 0ce1ac23149c6da939a5926c098c270c58c317a0 Mon Sep 17 00:00:00 2001 From: Ronald Tschalär Date: Fri, 19 Feb 2021 11:10:51 -0800 Subject: Input: applespi - don't wait for responses to commands indefinitely. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The response to a command may never arrive or it may be corrupted (and hence dropped) for some reason. While exceedingly rare, when it did happen it blocked all further commands. One way to fix this was to do a suspend/resume. However, recovering automatically seems like a nicer option. Hence this puts a time limit (1 sec) on how long we're willing to wait for a response, after which we assume it got lost. Signed-off-by: Ronald Tschalär Link: https://lore.kernel.org/r/20210217190718.11035-1-ronald@innovation.ch Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/applespi.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index d22223154177..27e87c45edf2 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -409,7 +410,7 @@ struct applespi_data { unsigned int cmd_msg_cntr; /* lock to protect the above parameters and flags below */ spinlock_t cmd_msg_lock; - bool cmd_msg_queued; + ktime_t cmd_msg_queued; enum applespi_evt_type cmd_evt_type; struct led_classdev backlight_info; @@ -729,7 +730,7 @@ static void applespi_msg_complete(struct applespi_data *applespi, wake_up_all(&applespi->drain_complete); if (is_write_msg) { - applespi->cmd_msg_queued = false; + applespi->cmd_msg_queued = 0; applespi_send_cmd_msg(applespi); } @@ -771,8 +772,16 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi) return 0; /* check whether send is in progress */ - if (applespi->cmd_msg_queued) - return 0; + if (applespi->cmd_msg_queued) { + if (ktime_ms_delta(ktime_get(), applespi->cmd_msg_queued) < 1000) + return 0; + + dev_warn(&applespi->spi->dev, "Command %d timed out\n", + applespi->cmd_evt_type); + + applespi->cmd_msg_queued = 0; + applespi->write_active = false; + } /* set up packet */ memset(packet, 0, APPLESPI_PACKET_SIZE); @@ -869,7 +878,7 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi) return sts; } - applespi->cmd_msg_queued = true; + applespi->cmd_msg_queued = ktime_get_coarse(); applespi->write_active = true; return 0; @@ -1921,7 +1930,7 @@ static int __maybe_unused applespi_resume(struct device *dev) applespi->drain = false; applespi->have_cl_led_on = false; applespi->have_bl_level = 0; - applespi->cmd_msg_queued = false; + applespi->cmd_msg_queued = 0; applespi->read_active = false; applespi->write_active = false; -- cgit v1.2.3 From e64123949e6c9581c97fc14594f1cf34bf1d87a8 Mon Sep 17 00:00:00 2001 From: Ronald Tschalär Date: Fri, 19 Feb 2021 11:12:00 -0800 Subject: Input: applespi - fix occasional crc errors under load. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason, when the system is under heavy CPU load, the read following the write sometimes occurs unusually quickly, resulting in the read data not being quite ready and hence a bad packet getting read. Adding another delay after reading the status message appears to fix this. Signed-off-by: Ronald Tschalär Link: https://lore.kernel.org/r/20210217190718.11035-2-ronald@innovation.ch Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/applespi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index 27e87c45edf2..eda1b23002b5 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -749,6 +749,8 @@ static void applespi_async_write_complete(void *context) applespi->tx_status, APPLESPI_STATUS_SIZE); + udelay(SPI_RW_CHG_DELAY_US); + if (!applespi_check_write_status(applespi, applespi->wr_m.status)) { /* * If we got an error, we presumably won't get the expected -- cgit v1.2.3 From 820c8727956da82b7a841c299fabb2fdca9a37d4 Mon Sep 17 00:00:00 2001 From: Philip Chen Date: Mon, 22 Feb 2021 21:05:04 -0800 Subject: Input: cros-ec-keyb - expose function row physical map to userspace The top-row keys in a keyboard usually have dual functionalities. E.g. A function key "F1" is also an action key "Browser back". Therefore, when an application receives an action key code from a top-row key press, the application needs to know how to correlate the action key code with the function key code and do the conversion whenever necessary. Since the userpace already knows the key scanlines (row/column) associated with a received key code. Essentially, the userspace only needs a mapping between the key row/column and the matching physical location in the top row. So, enhance the cros-ec-keyb driver to create such a mapping and expose it to userspace in the form of a function_row_physmap attribute. The attribute would be a space separated ordered list of row/column codes for the keys in the function row, in a left-to-right order. The attribute will only be present when the device has a custom design for the top-row keys. Signed-off-by: Philip Chen Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20210115122412.v7.2.I6542d7d9d0b246e7079bb16b41e697b2ac4b4e39@changeid Signed-off-by: Dmitry Torokhov --- .../ABI/testing/sysfs-driver-input-cros-ec-keyb | 6 ++ drivers/input/keyboard/cros_ec_keyb.c | 79 ++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb (limited to 'drivers/input') diff --git a/Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb b/Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb new file mode 100644 index 000000000000..c7afc2328045 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb @@ -0,0 +1,6 @@ +What: /sys/class/input/input(x)/device/function_row_physmap +Date: January 2021 +Contact: Philip Chen +Description: A space separated list of scancodes for the top row keys, + ordered by the physical positions of the keys, from left + to right. diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index b379ed762878..38457d9641bd 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -27,6 +27,8 @@ #include +#define MAX_NUM_TOP_ROW_KEYS 15 + /** * struct cros_ec_keyb - Structure representing EC keyboard device * @@ -42,6 +44,9 @@ * @idev: The input device for the matrix keys. * @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @notifier: interrupt event notifier for transport devices + * @function_row_physmap: An array of the encoded rows/columns for the top + * row function keys, in an order from left to right + * @num_function_row_keys: The number of top row keys in a custom keyboard */ struct cros_ec_keyb { unsigned int rows; @@ -58,6 +63,9 @@ struct cros_ec_keyb { struct input_dev *idev; struct input_dev *bs_idev; struct notifier_block notifier; + + u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS]; + size_t num_function_row_keys; }; /** @@ -527,6 +535,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) struct input_dev *idev; const char *phys; int err; + struct property *prop; + const __be32 *p; + u16 *physmap; + u32 key_pos; + int row, col; err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) @@ -578,6 +591,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) ckdev->idev = idev; cros_ec_keyb_compute_valid_keys(ckdev); + physmap = ckdev->function_row_physmap; + of_property_for_each_u32(dev->of_node, "function-row-physmap", + prop, p, key_pos) { + if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) { + dev_warn(dev, "Only support up to %d top row keys\n", + MAX_NUM_TOP_ROW_KEYS); + break; + } + row = KEY_ROW(key_pos); + col = KEY_COL(key_pos); + *physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + physmap++; + ckdev->num_function_row_keys++; + } + err = input_register_device(ckdev->idev); if (err) { dev_err(dev, "cannot register input device\n"); @@ -587,6 +615,51 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) return 0; } +static ssize_t function_row_physmap_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t size = 0; + int i; + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + u16 *physmap = ckdev->function_row_physmap; + + for (i = 0; i < ckdev->num_function_row_keys; i++) + size += scnprintf(buf + size, PAGE_SIZE - size, + "%s%02X", size ? " " : "", physmap[i]); + if (size) + size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); + + return size; +} + +static DEVICE_ATTR_RO(function_row_physmap); + +static struct attribute *cros_ec_keyb_attrs[] = { + &dev_attr_function_row_physmap.attr, + NULL, +}; + +static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj, + struct attribute *attr, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + + if (attr == &dev_attr_function_row_physmap.attr && + !ckdev->num_function_row_keys) + return 0; + + return attr->mode; +} + +static const struct attribute_group cros_ec_keyb_attr_group = { + .is_visible = cros_ec_keyb_attr_is_visible, + .attrs = cros_ec_keyb_attrs, +}; + + static int cros_ec_keyb_probe(struct platform_device *pdev) { struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); @@ -617,6 +690,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) return err; } + err = devm_device_add_group(dev, &cros_ec_keyb_attr_group); + if (err) { + dev_err(dev, "failed to create attributes. err=%d\n", err); + return err; + } + ckdev->notifier.notifier_call = cros_ec_keyb_work; err = blocking_notifier_chain_register(&ckdev->ec->event_notifier, &ckdev->notifier); -- cgit v1.2.3 From 2896a27fdcd0c1a0cdd45f865085fe99fcf68154 Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Mon, 22 Feb 2021 20:58:17 -0800 Subject: Input: alps - fix spelling of "positive" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s/postive/positive/ Signed-off-by: Bhaskar Chowdhury Acked-by: Randy Dunlap Reviewed-by: Pali Rohár Link: https://lore.kernel.org/r/20210222075439.32201-1-unixbhaskar@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index b067bfd2699c..4a6b33bbe7ea 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -986,7 +986,7 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, case V7_PACKET_ID_TWO: mt[1].x &= ~0x000F; mt[1].y |= 0x000F; - /* Detect false-postive touches where x & y report max value */ + /* Detect false-positive touches where x & y report max value */ if (mt[1].y == 0x7ff && mt[1].x == 0xff0) { mt[1].x = 0; /* y gets set to 0 at the end of this function */ -- cgit v1.2.3 From 4c47097f8514e4b35a31e04e33172d0193cb38ed Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Fri, 5 Feb 2021 12:05:07 -0800 Subject: Input: aiptek - convert sysfs sprintf/snprintf family to sysfs_emit Fix the following coccicheck warning: ./drivers/input/tablet/aiptek.c:1629:8-16: WARNING: use scnprintf or sprintf. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/1612419191-1078-1-git-send-email-jiapeng.chong@linux.alibaba.com Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/aiptek.c | 80 ++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index e08b0ef078e8..fcb1b646436a 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1036,9 +1036,9 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%dx%d\n", - input_abs_get_max(aiptek->inputdev, ABS_X) + 1, - input_abs_get_max(aiptek->inputdev, ABS_Y) + 1); + return sysfs_emit(buf, "%dx%d\n", + input_abs_get_max(aiptek->inputdev, ABS_X) + 1, + input_abs_get_max(aiptek->inputdev, ABS_Y) + 1); } /* These structs define the sysfs files, param #1 is the name of the @@ -1064,9 +1064,8 @@ static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(pointer_mode_map, - aiptek->curSetting.pointerMode)); + return sysfs_emit(buf, "%s\n", map_val_to_str(pointer_mode_map, + aiptek->curSetting.pointerMode)); } static ssize_t @@ -1101,9 +1100,8 @@ static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attri { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(coordinate_mode_map, - aiptek->curSetting.coordinateMode)); + return sysfs_emit(buf, "%s\n", map_val_to_str(coordinate_mode_map, + aiptek->curSetting.coordinateMode)); } static ssize_t @@ -1143,9 +1141,8 @@ static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute * { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(tool_mode_map, - aiptek->curSetting.toolMode)); + return sysfs_emit(buf, "%s\n", map_val_to_str(tool_mode_map, + aiptek->curSetting.toolMode)); } static ssize_t @@ -1174,10 +1171,9 @@ static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *att struct aiptek *aiptek = dev_get_drvdata(dev); if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) { - return snprintf(buf, PAGE_SIZE, "disable\n"); + return sysfs_emit(buf, "disable\n"); } else { - return snprintf(buf, PAGE_SIZE, "%d\n", - aiptek->curSetting.xTilt); + return sysfs_emit(buf, "%d\n", aiptek->curSetting.xTilt); } } @@ -1216,10 +1212,9 @@ static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *att struct aiptek *aiptek = dev_get_drvdata(dev); if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) { - return snprintf(buf, PAGE_SIZE, "disable\n"); + return sysfs_emit(buf, "disable\n"); } else { - return snprintf(buf, PAGE_SIZE, "%d\n", - aiptek->curSetting.yTilt); + return sysfs_emit(buf, "%d\n", aiptek->curSetting.yTilt); } } @@ -1257,7 +1252,7 @@ static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); + return sysfs_emit(buf, "%d\n", aiptek->curSetting.jitterDelay); } static ssize_t @@ -1286,8 +1281,7 @@ static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_at { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", - aiptek->curSetting.programmableDelay); + return sysfs_emit(buf, "%d\n", aiptek->curSetting.programmableDelay); } static ssize_t @@ -1316,7 +1310,7 @@ static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attri { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); + return sysfs_emit(buf, "%ld\n", aiptek->eventCount); } static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL); @@ -1355,7 +1349,7 @@ static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_at default: return 0; } - return snprintf(buf, PAGE_SIZE, retMsg); + return sysfs_emit(buf, retMsg); } static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL); @@ -1375,9 +1369,8 @@ static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(stylus_button_map, - aiptek->curSetting.stylusButtonUpper)); + return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonUpper)); } static ssize_t @@ -1406,9 +1399,8 @@ static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(stylus_button_map, - aiptek->curSetting.stylusButtonLower)); + return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonLower)); } static ssize_t @@ -1444,9 +1436,8 @@ static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(mouse_button_map, - aiptek->curSetting.mouseButtonLeft)); + return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonLeft)); } static ssize_t @@ -1474,9 +1465,8 @@ static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(mouse_button_map, - aiptek->curSetting.mouseButtonMiddle)); + return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonMiddle)); } static ssize_t @@ -1504,9 +1494,8 @@ static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - map_val_to_str(mouse_button_map, - aiptek->curSetting.mouseButtonRight)); + return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonRight)); } static ssize_t @@ -1535,10 +1524,9 @@ static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *att struct aiptek *aiptek = dev_get_drvdata(dev); if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) { - return snprintf(buf, PAGE_SIZE, "disable\n"); + return sysfs_emit(buf, "disable\n"); } else { - return snprintf(buf, PAGE_SIZE, "%d\n", - aiptek->curSetting.wheel); + return sysfs_emit(buf, "%d\n", aiptek->curSetting.wheel); } } @@ -1568,8 +1556,7 @@ static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *a /* There is nothing useful to display, so a one-line manual * is in order... */ - return snprintf(buf, PAGE_SIZE, - "Write anything to this file to program your tablet.\n"); + return sysfs_emit(buf, "Write anything to this file to program your tablet.\n"); } static ssize_t @@ -1600,7 +1587,7 @@ static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *a { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); + return sysfs_emit(buf, "0x%04x\n", aiptek->features.odmCode); } static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL); @@ -1613,7 +1600,7 @@ static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); + return sysfs_emit(buf, "0x%04x\n", aiptek->features.modelCode); } static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL); @@ -1626,8 +1613,7 @@ static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *at { struct aiptek *aiptek = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%04x\n", - aiptek->features.firmwareCode); + return sysfs_emit(buf, "%04x\n", aiptek->features.firmwareCode); } static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL); -- cgit v1.2.3