From nobody Thu Apr 2 22:25:04 2026 Received: from mail.hugovil.com (mail.hugovil.com [162.243.120.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 22CE33624C1; Fri, 13 Feb 2026 17:14:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.243.120.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771002879; cv=none; b=huRl1WB90tycWVCEj5oBT6eMr5neQnjjaEsdOmB9JOudqGIDu7TcVAtJ1rtpiwpn8cDNsg2Gq9G92PYQnzrQMG6StbFBKx26p1AJigr1EyM/NC8b+6F1A1PFm85jIFLYs8Z9l85amUVQA+ZzYDpw4W4PTQ8dS/op6LrXvXgFTXw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771002879; c=relaxed/simple; bh=Wv+4y1tAcGqJOTyA1fZuV0d60O1SZSd4XH11J7U9Juk=; h=From:To:Cc:Date:Message-ID:In-Reply-To:References:MIME-Version: Subject; b=gIqgQRU766oChMkjVKx5NXknFoXaqUBhtDTDy4XMeAaQ+TyeFHhFoOz13YNvNacz6sYZEUROOsVwZ/e8y4pQeJoqN+ft19+XjzxdxB5aNcGJL6b3tQT4IGnolTNoLOd5EM/AIOzZKQYhZ0rlLLpakY9Y9vUade4H6P+z/98biFM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hugovil.com; spf=pass smtp.mailfrom=hugovil.com; dkim=pass (1024-bit key) header.d=hugovil.com header.i=@hugovil.com header.b=StOhYP7f; arc=none smtp.client-ip=162.243.120.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hugovil.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hugovil.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=hugovil.com header.i=@hugovil.com header.b="StOhYP7f" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=hugovil.com ; s=x; h=Subject:Content-Transfer-Encoding:MIME-Version:Message-ID:Date:Cc:To :From:subject:date:message-id:reply-to; bh=+Y2RgLXDgNIKLMeOTPhP2GoKWeQQcPvO3uERG8Kvy84=; b=StOhYP7fdjobizygYrXN8MxU79 3exp+6jYDioO5V743jIeSpVSwZSxwmITbjUt4U2ny5jCa1cmibaROhxqAwXLGNVykYLQvGumciI9r sk+wX3EWvEqGO6CHMNBTrFFr5oeATfL2iDO+efmWEv014CBZtWiWrI2iMAA0B+X4/ksc=; Received: from modemcable168.174-80-70.mc.videotron.ca ([70.80.174.168]:49782 helo=pettiford.lan) by mail.hugovil.com with esmtpa (Exim 4.92) (envelope-from ) id 1vqwkV-0007VX-NE; Fri, 13 Feb 2026 12:14:36 -0500 From: Hugo Villeneuve To: hvilleneuve@dimonoff.com, dmitry.torokhov@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, hugo@hugovil.com Date: Fri, 13 Feb 2026 12:14:25 -0500 Message-ID: <20260213171431.2228814-2-hugo@hugovil.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260213171431.2228814-1-hugo@hugovil.com> References: <20260213171431.2228814-1-hugo@hugovil.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 70.80.174.168 X-SA-Exim-Mail-From: hugo@hugovil.com X-Spam-Level: X-Spam-Report: * -1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP Subject: [PATCH v2 1/2] dt-bindings: input: add GPIO charlieplex keypad X-SA-Exim-Version: 4.2.1 (built Wed, 08 May 2019 21:11:16 +0000) X-SA-Exim-Scanned: Yes (on mail.hugovil.com) Content-Type: text/plain; charset="utf-8" From: Hugo Villeneuve Add DT bindings for GPIO charlieplex keypad. Signed-off-by: Hugo Villeneuve --- .../input/gpio-charlieplex-keypad.yaml | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/gpio-charlieple= x-keypad.yaml diff --git a/Documentation/devicetree/bindings/input/gpio-charlieplex-keypa= d.yaml b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.ya= ml new file mode 100644 index 0000000000000..1672491a75a85 --- /dev/null +++ b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/input/gpio-charlieplex-keypad.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO charlieplex keypad + +maintainers: + - Hugo Villeneuve + +description: + The charlieplex keypad supports N^2)-N different key combinations (where= N is + the number of lines). Key presses and releases are detected by configuri= ng + only one line as output at a time, and reading other line states. This p= rocess + is repeated for each line. + This mechanism doesn't allow to detect simultaneous key presses. + +allOf: + - $ref: input.yaml# + - $ref: /schemas/input/matrix-keymap.yaml# + +properties: + compatible: + const: gpio-charlieplex-keypad + + autorepeat: true + + line-scan-delay-us: + description: + Delay, measured in microseconds, that is needed + before we can scan keypad after activating one line. + default: 0 + + line-gpios: + description: + List of GPIOs used as lines. The gpio specifier for this property + depends on the gpio controller to which these lines are connected. + + linux,keymap: true + + debounce-delay-ms: + description: Debounce interval in milliseconds. + default: 5 + + poll-interval: true + + wakeup-source: true + +required: + - compatible + - line-gpios + - linux,keymap + - poll-interval + +additionalProperties: false + +examples: + - | + #include + + charlieplex-keypad { + compatible =3D "gpio-charlieplex-keypad"; + debounce-delay-ms =3D <20>; + poll-interval =3D <5>; + line-scan-delay-us =3D <2>; + + line-gpios =3D <&gpio2 25 0 + &gpio2 26 0 + &gpio2 27 0>; + + /* MATRIX_KEY(output, input, key-code) */ + linux,keymap =3D < + MATRIX_KEY(0, 1, KEY_F1) + MATRIX_KEY(0, 2, KEY_F2) + MATRIX_KEY(1, 0, KEY_F3) + MATRIX_KEY(1, 2, KEY_F4) + MATRIX_KEY(2, 0, KEY_F5) + MATRIX_KEY(2, 1, KEY_F6) + >; + }; --=20 2.47.3 From nobody Thu Apr 2 22:25:04 2026 Received: from mail.hugovil.com (mail.hugovil.com [162.243.120.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60ECD36606F; Fri, 13 Feb 2026 17:14:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.243.120.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771002881; cv=none; b=I6XaZSU9FYbdvt/RQKXWPddjae/ZvmkvaXQQ3tBf4HTuFPvfHM80jAVjilP3etGvMpk9e2b77KDg2s/rKsgObsAn6Qvpo7zFp3+xMK7zJLzfr6gfbZ1kVQOX2FMKPrRbeUY7amGa0lh3xCAuoySFC1LKmd02dYGzZB7N/BV6gLI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771002881; c=relaxed/simple; bh=PGAH9rCxX7q2eQB3IVbofw/qNUc6fHrr2ONLETY6iEU=; h=From:To:Cc:Date:Message-ID:In-Reply-To:References:MIME-Version: Subject; b=k81F0X7rferqhoWSkFPDnuBdDGpDhHLPup0jh+0QcMUitKSlW6mJx6a48vgY/6ZSeh7+Vil46EPeLFyr0HmHfOy4tXcoMFn5InLrKxz6mKKG8psgcNjAyn5TAVlShT4iDQ7DDKrydtRXiaRz7jFiBFs+o/MUkJ81sz3Taah7U9g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hugovil.com; spf=pass smtp.mailfrom=hugovil.com; dkim=pass (1024-bit key) header.d=hugovil.com header.i=@hugovil.com header.b=kov4yeMd; arc=none smtp.client-ip=162.243.120.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hugovil.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hugovil.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=hugovil.com header.i=@hugovil.com header.b="kov4yeMd" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=hugovil.com ; s=x; h=Subject:Content-Transfer-Encoding:MIME-Version:Message-ID:Date:Cc:To :From:subject:date:message-id:reply-to; bh=2weegKCMFOEi+LSDYi9P5QCz9jexBGAsJwZMFbqjKas=; b=kov4yeMd1RkKNsThT02tvgp43k mFZpYkGK2QerI90/M+OniDAgPlk7VtusFQYSLRt2k3Bp6DQlqPl1YMZENDWBLx3f7Nx2al5ZWlP4p 0ZEXwku+htAFJ0TDpOaPNNfWTk1+69KDKaHaSFh0UEOa6X/e4F5BizN6wwwDtnQA8uZE=; Received: from modemcable168.174-80-70.mc.videotron.ca ([70.80.174.168]:49782 helo=pettiford.lan) by mail.hugovil.com with esmtpa (Exim 4.92) (envelope-from ) id 1vqwkX-0007VX-Ae; Fri, 13 Feb 2026 12:14:38 -0500 From: Hugo Villeneuve To: hvilleneuve@dimonoff.com, dmitry.torokhov@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, hugo@hugovil.com Date: Fri, 13 Feb 2026 12:14:26 -0500 Message-ID: <20260213171431.2228814-3-hugo@hugovil.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260213171431.2228814-1-hugo@hugovil.com> References: <20260213171431.2228814-1-hugo@hugovil.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 70.80.174.168 X-SA-Exim-Mail-From: hugo@hugovil.com X-Spam-Level: X-Spam-Report: * -1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP Subject: [PATCH v2 2/2] Input: charlieplex_keypad: add GPIO charlieplex keypad X-SA-Exim-Version: 4.2.1 (built Wed, 08 May 2019 21:11:16 +0000) X-SA-Exim-Scanned: Yes (on mail.hugovil.com) Content-Type: text/plain; charset="utf-8" From: Hugo Villeneuve Add support for GPIO-based charlieplex keypad, allowing to control N^2-N keys using N GPIO lines. Reuse matrix keypad keymap to simplify, even if there is no concept of rows and columns in this type of keyboard. Signed-off-by: Hugo Villeneuve --- MAINTAINERS | 7 + drivers/input/keyboard/Kconfig | 14 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/charlieplex_keypad.c | 213 ++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 drivers/input/keyboard/charlieplex_keypad.c diff --git a/MAINTAINERS b/MAINTAINERS index 9ed6d11a77466..0b2d71f32b400 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5765,6 +5765,13 @@ S: Maintained F: Documentation/hwmon/powerz.rst F: drivers/hwmon/powerz.c =20 +CHARLIEPLEX KEYPAD DRIVER +M: Hugo Villeneuve +S: Supported +W: http://www.mosaic-industries.com/embedded-systems/microcontroller-proje= cts/electronic-circuits/matrix-keypad-scan-decode +F: Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml +F: drivers/input/keyboard/charlieplex_keypad.c + CHECKPATCH M: Andy Whitcroft M: Joe Perches diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2ff4fef322c24..ae54b4b7e2d8a 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -289,6 +289,20 @@ config KEYBOARD_MATRIX To compile this driver as a module, choose M here: the module will be called matrix_keypad. =20 +config KEYBOARD_CHARLIEPLEX + tristate "GPIO driven chearlieplex keypad support" + depends on GPIOLIB || COMPILE_TEST + select INPUT_MATRIXKMAP + help + Enable support for GPIO driven charlieplex keypad. A charlieplex + keypad allows to use fewer GPIO lines to interface to key switches. + For example, an N lines charlieplex keypad can be used to interface + to N^2-N different key switches. However, this type of keypad + cannot detect more than one key press at a time. + + To compile this driver as a module, choose M here: the + module will be called charlieplex_keypad. + config KEYBOARD_HIL_OLD tristate "HP HIL keyboard support (simple driver)" depends on GSC || HP300 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makef= ile index 2d906e14f3e27..40b5cf5d374d2 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_LOCOMO) +=3D locomokbd.o obj-$(CONFIG_KEYBOARD_LPC32XX) +=3D lpc32xx-keys.o obj-$(CONFIG_KEYBOARD_MAPLE) +=3D maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) +=3D matrix_keypad.o +obj-$(CONFIG_KEYBOARD_CHARLIEPLEX) +=3D charlieplex_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) +=3D max7359_keypad.o obj-$(CONFIG_KEYBOARD_MAX7360) +=3D max7360-keypad.o obj-$(CONFIG_KEYBOARD_MPR121) +=3D mpr121_touchkey.o diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/ke= yboard/charlieplex_keypad.c new file mode 100644 index 0000000000000..81e8b6b96dab1 --- /dev/null +++ b/drivers/input/keyboard/charlieplex_keypad.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driven charlieplex keypad driver + * + * Copyright (c) 2025 Hugo Villeneuve + * + * Based on matrix_keyboard.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct charlieplex_keypad { + struct input_dev *input_dev; + struct gpio_descs *line_gpios; + unsigned int nlines; + unsigned int line_scan_delay_us; + unsigned int debounce_threshold; + unsigned int debounce_count; + int debounce_code; + int current_code; +}; + +static void charlieplex_keypad_report_key(struct input_dev *input) +{ + struct charlieplex_keypad *keypad =3D input_get_drvdata(input); + const unsigned short *keycodes =3D input->keycode; + + if (keypad->current_code > 0) { + input_event(input, EV_MSC, MSC_SCAN, keypad->current_code); + input_report_key(input, keycodes[keypad->current_code], 0); + } + + if (keypad->debounce_code) { + input_event(input, EV_MSC, MSC_SCAN, keypad->debounce_code); + input_report_key(input, keycodes[keypad->debounce_code], 1); + } + + input_sync(input); + keypad->current_code =3D keypad->debounce_code; +} + +static void charlieplex_keypad_check_switch_change(struct input_dev *input, + int code) +{ + struct charlieplex_keypad *keypad =3D input_get_drvdata(input); + + if (code !=3D keypad->debounce_code) { + keypad->debounce_count =3D 0; + keypad->debounce_code =3D code; + } else if (keypad->debounce_count < keypad->debounce_threshold) { + keypad->debounce_count++; + + if (keypad->debounce_count >=3D keypad->debounce_threshold && + keypad->debounce_code !=3D keypad->current_code) + charlieplex_keypad_report_key(input); + } +} + +static void charlieplex_keypad_poll(struct input_dev *input) +{ + struct charlieplex_keypad *keypad =3D input_get_drvdata(input); + int oline; + int code; + + for (code =3D 0, oline =3D 0; oline < keypad->nlines; oline++) { + DECLARE_BITMAP(values, MATRIX_MAX_ROWS); + int iline; + int rc; + + /* Activate only one line as output at a time. */ + gpiod_direction_output(keypad->line_gpios->desc[oline], 1); + + if (keypad->line_scan_delay_us) + fsleep(keypad->line_scan_delay_us); + + /* Read input on all other lines. */ + rc =3D gpiod_get_array_value_cansleep(keypad->line_gpios->ndescs, + keypad->line_gpios->desc, + keypad->line_gpios->info, values); + if (rc) + return; + + for (iline =3D 0; iline < keypad->nlines; iline++) { + if (iline =3D=3D oline) + continue; /* Do not read active output line. */ + + /* Check if GPIO is asserted. */ + if (test_bit(iline, values)) { + code =3D MATRIX_SCAN_CODE(oline, iline, + get_count_order(keypad->nlines)); + /* + * Exit loop immediately since we cannot detect + * more than one key press at a time. + */ + break; + } + } + + gpiod_direction_input(keypad->line_gpios->desc[oline]); + + if (code) + break; + } + + charlieplex_keypad_check_switch_change(input, code); +} + +static int charlieplex_keypad_init_gpio(struct platform_device *pdev, + struct charlieplex_keypad *keypad) +{ + int i; + + keypad->line_gpios =3D devm_gpiod_get_array(&pdev->dev, "line", GPIOD_IN); + if (IS_ERR(keypad->line_gpios)) + return PTR_ERR(keypad->line_gpios); + + keypad->nlines =3D keypad->line_gpios->ndescs; + + if (keypad->nlines > MATRIX_MAX_ROWS) + return -EINVAL; + + for (i =3D 0; i < keypad->nlines; i++) + gpiod_set_consumer_name(keypad->line_gpios->desc[i], "charlieplex_kbd_li= ne"); + + return 0; +} + +static int charlieplex_keypad_probe(struct platform_device *pdev) +{ + struct charlieplex_keypad *keypad; + unsigned int debounce_interval_ms; + unsigned int poll_interval_ms; + struct input_dev *input_dev; + int err; + + keypad =3D devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input_dev =3D devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + keypad->input_dev =3D input_dev; + + device_property_read_u32(&pdev->dev, "poll-interval", &poll_interval_ms); + device_property_read_u32(&pdev->dev, "debounce-delay-ms", &debounce_inter= val_ms); + device_property_read_u32(&pdev->dev, "line-scan-delay-us", &keypad->line_= scan_delay_us); + + keypad->current_code =3D -1; + keypad->debounce_code =3D -1; + keypad->debounce_threshold =3D DIV_ROUND_UP(debounce_interval_ms, poll_in= terval_ms); + + err =3D charlieplex_keypad_init_gpio(pdev, keypad); + if (err) + return err; + + input_dev->name =3D pdev->name; + input_dev->id.bustype =3D BUS_HOST; + + err =3D matrix_keypad_build_keymap(NULL, NULL, keypad->nlines, + keypad->nlines, NULL, input_dev); + if (err) + dev_err_probe(&pdev->dev, -ENOMEM, "failed to build keymap\n"); + + if (device_property_read_bool(&pdev->dev, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + err =3D input_setup_polling(input_dev, charlieplex_keypad_poll); + if (err) + dev_err_probe(&pdev->dev, err, "unable to set up polling\n"); + + input_set_poll_interval(input_dev, poll_interval_ms); + + input_set_drvdata(input_dev, keypad); + + err =3D input_register_device(keypad->input_dev); + if (err) + return err; + + platform_set_drvdata(pdev, keypad); + + return 0; +} + +static const struct of_device_id charlieplex_keypad_dt_match[] =3D { + { .compatible =3D "gpio-charlieplex-keypad" }, + { } +}; +MODULE_DEVICE_TABLE(of, charlieplex_keypad_dt_match); + +static struct platform_driver charlieplex_keypad_driver =3D { + .probe =3D charlieplex_keypad_probe, + .driver =3D { + .name =3D "charlieplex-keypad", + .of_match_table =3D of_match_ptr(charlieplex_keypad_dt_match), + }, +}; +module_platform_driver(charlieplex_keypad_driver); + +MODULE_AUTHOR("Hugo Villeneuve "); +MODULE_DESCRIPTION("GPIO driven charlieplex keypad driver"); +MODULE_LICENSE("GPL"); --=20 2.47.3