From nobody Sun Feb 8 14:57:05 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 469B8190462; Tue, 3 Feb 2026 16:06:04 +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=1770134766; cv=none; b=TZMWXzAxL/So3FpScoEDuN6jbltRZKa/vfUq7ZJu+jQ0zDJMrGdNwRoQefA5Lc4lxfFagsEHiYz+342+QMnOCDLHFExiuK00NwRitjKdpgcnzVFlhZoRG31dr89IOTJw0R1bZ1YwkWFA8MxGWUR0mqmUtTnf3W4Ua87lNhOtsg8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770134766; c=relaxed/simple; bh=jCJiyRHuuKJBPzYahAXx7huO18KrkcOqRN3zYjFMfq0=; h=From:To:Cc:Date:Message-ID:In-Reply-To:References:MIME-Version: Subject; b=AFIk3dABVEWwdynZyCLnkHcOTNoh0ihjMGKUfMt/2Q6R/QQHBGed8i1KVbQNuI+Ue20vdjnXmOWf0D3NN9j9j4ZpG1iJNeq2rl5Hk2a8RqVSD8vvSUCBRj2g2vZNo4trmsV/z5iLZ04By34/7kE9TBZX/irxw9Y4TdzRBU1dSPg= 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=OF78wZqX; 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="OF78wZqX" 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=eTJbAHAyZW9VsvHuKNy01JGBVwJyYv46g0ikfNP6MV0=; b=OF78wZqXHJFb/SnY3uC3f2CteR s1L6l9XfAMErL+uqJ/RQBQ56aCw+em6U2HF8FquTmT0T4Ku/fgCY6rWH7qaO6UwHmk0Z0+O9eI8h0 5dHAnf3WAZBeqOKTVaKSHNklIZu+XBlzHRjNpfu7bPk6WmjzHIZvDhFRbPaALizYp48A=; Received: from modemcable168.174-80-70.mc.videotron.ca ([70.80.174.168]:53840 helo=pettiford.lan) by mail.hugovil.com with esmtpa (Exim 4.92) (envelope-from ) id 1vnIfc-0000uz-Pp; Tue, 03 Feb 2026 10:50:29 -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: Tue, 3 Feb 2026 10:49:47 -0500 Message-ID: <20260203155023.536103-2-hugo@hugovil.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260203155023.536103-1-hugo@hugovil.com> References: <20260203155023.536103-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 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 | 88 +++++++++++++++++++ 1 file changed, 88 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..b382c8caa096d --- /dev/null +++ b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml @@ -0,0 +1,88 @@ +# 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 + + 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 + + linux,no-autorepeat: + type: boolean + description: Do not enable autorepeat feature. + + gpio-activelow: + type: boolean + description: + Force GPIO polarity to active low. + In the absence of this property GPIOs are treated as active high. + + 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: + - | + 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 Sun Feb 8 14:57:05 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 B879E3C199A; Tue, 3 Feb 2026 16:06:07 +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=1770134769; cv=none; b=MLNzzKx5w3cL6ZUxkoKZmAqaxVMQNiLGkvuH5gJrJz9lzwq8bU6KZc53+nnMPLosVC6tAtro8EN9RyuMdKYuo/bacAE4wL0eUn05OafPaso+N9ebdfHojQkDOH06/nLJbo9e0UipQqBrKP7bNWIh6wmMQp5qFksLu81Sdt0C5KE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770134769; c=relaxed/simple; bh=TKjCVlUSEkl9IyWV17if8/5W01dd5I/czTABwqWRuXQ=; h=From:To:Cc:Date:Message-ID:In-Reply-To:References:MIME-Version: Subject; b=dZk6J82AYSiscF1FyCRar3S05A2RajZgKLOA072+OuksDdTS0EdxwXW/jM4oHLLIHHx3V+SSVgBHRQ/7d4okyo8rzLJQXtWXv7Kpv0Cy+AlDJo1/VWXnhcoMLtHsSTnP/a7ifH3KABumqhZKbcK3bjJcQYk2z5K1dXHwJy/jLcU= 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=CzBbtdSy; 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="CzBbtdSy" 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=TQqlnaPGBe1etfIPdtuZePXraUizbrhN2DcKeJye69k=; b=CzBbtdSyxYhhQoUQCIuZcbEkrN trosgrxHkxWwLYrWvhCsMLIWu/tEcEuteBmotWVOoRcKyeBW6oWby1i7FCYGYBLVPEVnP7At8tjRY 9z/xydUnmmwli9lSW3UVIICtxGbBk657SVmsJuqnPnLC6z3LcnogTpS1A7xm4xa9q8YY=; Received: from modemcable168.174-80-70.mc.videotron.ca ([70.80.174.168]:53840 helo=pettiford.lan) by mail.hugovil.com with esmtpa (Exim 4.92) (envelope-from ) id 1vnIfe-0000uz-W1; Tue, 03 Feb 2026 10:50:31 -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: Tue, 3 Feb 2026 10:49:48 -0500 Message-ID: <20260203155023.536103-3-hugo@hugovil.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260203155023.536103-1-hugo@hugovil.com> References: <20260203155023.536103-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 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 | 223 ++++++++++++++++++++ 4 files changed, 245 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..f9d6f5ecfa8dc --- /dev/null +++ b/drivers/input/keyboard/charlieplex_keypad.c @@ -0,0 +1,223 @@ +// 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) +{ + bool active_low; + int nkeys; + 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; + + nkeys =3D (keypad->nlines * keypad->nlines) - keypad->nlines; + + active_low =3D device_property_read_bool(&pdev->dev, "gpio-activelow"); + + for (i =3D 0; i < keypad->nlines; i++) { + gpiod_set_consumer_name(keypad->line_gpios->desc[i], "charlieplex_kbd_li= ne"); + + if (active_low ^ gpiod_is_active_low(keypad->line_gpios->desc[i])) + gpiod_toggle_active_low(keypad->line_gpios->desc[i]); + } + + 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, "linux,no-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