Merge pull request #10478 from chewitt/amlogic-upstream

Amlogic: bump kernel and rework tm16xx driver package to be a system service
This commit is contained in:
Rudi Heitbaum
2025-09-12 19:20:51 +10:00
committed by GitHub
69 changed files with 4571 additions and 632 deletions

View File

@@ -41,17 +41,6 @@ show_config() {
config_message+="\n - Local Ccache:\t\t\t ${LOCAL_CCACHE:-no}"
config_message+="\n - CONFIG_SHELL:\t\t\t ${CONFIG_SHELL:-auto}"
# Image Filsystem
config_message+="\n\n Image Filesystems:"
config_message+="\n $dashes$dashes"
config_message+="\n - Flash Size (MiB): \t\t\t $SYSTEM_SIZE"
config_message+="\n - Storage Size (MiB): \t\t\t $STORAGE_SIZE"
config_message+="\n - SquashFS Compression Method: \t $SQUASHFS_COMPRESSION"
if [ -n "${SQUASHFS_COMPRESSION_OPTION}" ]; then
config_message+="\n - SquashFS Compression Options: \t $SQUASHFS_COMPRESSION_OPTION"
fi
# Misc. hardware configuration
config_message+="\n\n Misc. hardware configuration:"
@@ -60,6 +49,7 @@ show_config() {
config_message+="\n - ALSA support:\t\t\t ${ALSA_SUPPORT}"
config_message+="\n - Pulseaudio support:\t\t\t ${PULSEAUDIO_SUPPORT}"
config_message+="\n - Bluetooth support:\t\t\t ${BLUETOOTH_SUPPORT}"
config_message+="\n - VFD panel support:\t\t\t ${VFD_SUPPORT}"
for config_driver in ${ADDITIONAL_DRIVERS}; do
config_message+="\n - Include driver:\t\t\t ${config_driver}"

View File

@@ -27,6 +27,8 @@
DISTRO_BOOTLABEL="LEIOT"
DISTRO_DISKLABEL="STORAGE"
# VFD panel boot message (use UPPERCASE and padding)
VFD_MESSAGE=" LEIoT"
### BUILDSYSTEM SETTINGS ####

View File

@@ -27,6 +27,8 @@
DISTRO_BOOTLABEL="LIBREELEC"
DISTRO_DISKLABEL="STORAGE"
# VFD panel boot message (use UPPERCASE and padding)
VFD_MESSAGE=" LIBREELEC"
### BUILDSYSTEM SETTINGS ####

View File

@@ -1,33 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2025-present Team LibreELEC (https://libreelec.tv)
PKG_NAME="tm16xx"
PKG_VERSION="9148a4e12ba6cf4b375a7fd352667f7f27823426"
PKG_SHA256="e8fbbc5c8c36b3eb42f1f8d7c907106ccf3a2b5af4b2382c6ca4ea63e07fde70"
PKG_LICENSE="GPLv2"
PKG_SITE="https://github.com/jefflessard/tm16xx-display/"
PKG_URL="https://github.com/jefflessard/tm16xx-display/archive/${PKG_VERSION}.tar.gz"
PKG_LONGDESC="Linux kernel driver for auxiliary displays using TM1628 compatible controllers"
PKG_IS_KERNEL_PKG="yes"
pre_make_target() {
unset LDFLAGS
}
make_target() {
kernel_make V=1 KDIR=$(kernel_path) module
}
makeinstall_target() {
mkdir -p ${INSTALL}/$(get_full_module_dir)/${PKG_NAME}
cp *.ko ${INSTALL}/$(get_full_module_dir)/${PKG_NAME}
mkdir -p ${INSTALL}/usr/sbin
cp display-service ${INSTALL}/usr/sbin
cp display-utils ${INSTALL}/usr/sbin
mkdir -p ${INSTALL}/usr/lib/systemd/system
cp display.service ${INSTALL}/usr/lib/systemd/system
}
post_install() {
enable_service display.service
}

View File

@@ -16,8 +16,8 @@ PKG_PATCH_DIRS="${LINUX}"
case "${LINUX}" in
amlogic)
PKG_VERSION="86731a2a651e58953fc949573895f2fa6d456841" # 6.16-rc3
PKG_SHA256="008b00968a8bfc0627580b82a2d30c7304336a4f92a58e80cdbc2d4723e01840"
PKG_VERSION="4645fefac0b24d509b962c096b0327e87f34b1d2" # 6.16.5
PKG_SHA256="45daac22ef696b17041b0ef1584332a255c6f3d26bd604f7052b1f9e65bf13f0"
PKG_URL="https://github.com/torvalds/linux/archive/${PKG_VERSION}.tar.gz"
PKG_SOURCE_NAME="linux-${LINUX}-${PKG_VERSION}.tar.gz"
PKG_PATCH_DIRS="default rtlwifi/6.17"
@@ -147,6 +147,27 @@ pre_make_target() {
${PKG_BUILD}/scripts/config --disable CONFIG_WIREGUARD
fi
# disable vfd support if not enabled
if [ ! "${VFD_SUPPORT}" = yes ]; then
${PKG_BUILD}/scripts/config --disable CONFIG_PANEL_CHANGE_MESSAGE
else
# enable the module and set distro boot message
${PKG_BUILD}/scripts/config --enable CONFIG_AUXDISPLAY
${PKG_BUILD}/scripts/config --enable CONFIG_LINEDISPLAY
${PKG_BUILD}/scripts/config --enable CONFIG_TM16XX
${PKG_BUILD}/scripts/config --enable CONFIG_TM16XX_KEYPAD
${PKG_BUILD}/scripts/config --enable CONFIG_TM16XX_I2C
${PKG_BUILD}/scripts/config --enable CONFIG_TM16XX_SPI
${PKG_BUILD}/scripts/config --enable CONFIG_PANEL_CHANGE_MESSAGE
${PKG_BUILD}/scripts/config --set-str CONFIG_PANEL_BOOT_MESSAGE "${VFD_MESSAGE}"
${PKG_BUILD}/scripts/config --enable CONFIG_INPUT_MATRIXKMAP
# enable led activity triggers
${PKG_BUILD}/scripts/config --enable CONFIG_LEDS_TRIGGER_TIMER # Colon
${PKG_BUILD}/scripts/config --enable CONFIG_LEDS_TRIGGER_NETDEV # LAN/WLAN
${PKG_BUILD}/scripts/config --enable CONFIG_USB_LEDS_TRIGGER_USBPORT # USB
${PKG_BUILD}/scripts/config --enable CONFIG_MMC # SD
fi
if [ "${TARGET_ARCH}" = "x86_64" ]; then
# copy some extra firmware to linux tree
mkdir -p ${PKG_BUILD}/external-firmware

View File

@@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2025-present Team LibreELEC (https://libreelec.tv)
PKG_NAME="tm16xx-display"
PKG_VERSION="e990a0cc2377fb957973dd406db6ab6fdf9c2a5a" #v4-feedback
PKG_SHA256="2b9c53f87641c7498d384d97b4e3605eda8a6a0b76e2b6ad4c916551829e6207"
PKG_LICENSE="GPLv2"
PKG_SITE="https://github.com/jefflessard/tm16xx-display/"
PKG_URL="https://github.com/jefflessard/tm16xx-display/archive/${PKG_VERSION}.tar.gz"
PKG_LONGDESC="Linux kernel driver utilities for TM16XX compatible controllers"
PKG_IS_KERNEL_PKG="yes"
PKG_TOOLCHAIN="manual"
post_makeinstall_target() {
mkdir -p ${INSTALL}/usr/sbin
cp display-service ${INSTALL}/usr/sbin
mkdir -p ${INSTALL}/usr/lib/systemd/system
cp display.service ${INSTALL}/usr/lib/systemd/system
}
post_install() {
enable_service display.service
}

View File

@@ -9,3 +9,7 @@ PKG_URL=""
PKG_DEPENDS_TARGET="toolchain ${ADDITIONAL_PACKAGES}"
PKG_SECTION="virtual"
PKG_LONGDESC="misc-packages: Metapackage for miscellaneous packages"
if [ "${VFD_SUPPORT}" = "yes" ]; then
PKG_DEPENDS_TARGET+=" tm16xx-display"
fi

View File

@@ -1,7 +1,7 @@
From 9a711d3aca9f7bd053caefec4f1bef07ba1a4817 Mon Sep 17 00:00:00 2001
From a1bbdc06d9709b09b40bc973eaf01be7bb2c857f Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sat, 13 Apr 2019 05:41:51 +0000
Subject: [PATCH 01/37] LOCAL: set meson-gx cma pool to 896MB
Subject: [PATCH 01/54] LOCAL: set meson-gx cma pool to 896MB
This change sets the CMA pool to a larger 896MB! value for vdec use

View File

@@ -1,7 +1,7 @@
From 5979e28bfa5986d47ba62f147feb5d9b83f16e6d Mon Sep 17 00:00:00 2001
From d340a41dc7009e1713862fd604c83d3b32da8a76 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 14 Aug 2019 19:58:14 +0000
Subject: [PATCH 02/37] LOCAL: set meson-g12 cma pool to 896MB
Subject: [PATCH 02/54] LOCAL: set meson-g12 cma pool to 896MB
This change sets the CMA pool to a larger 896MB! value for vdec use

View File

@@ -1,7 +1,7 @@
From 3df5b83cf844e053806a57afd5a4af8d11617c5d Mon Sep 17 00:00:00 2001
From 0abf689abf81dc2a68f5a7b59367b4a026797692 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sat, 13 Apr 2019 05:45:18 +0000
Subject: [PATCH 03/37] LOCAL: arm64: fix Kodi sysinfo CPU information
Subject: [PATCH 03/54] LOCAL: arm64: fix Kodi sysinfo CPU information
This allows the CPU information to show in the Kodi sysinfo screen, e.g.

View File

@@ -1,7 +1,7 @@
From cacea1a7dcf9e69c3d1cfc1026a05a9332bf9837 Mon Sep 17 00:00:00 2001
From 0f80ea4c86330ceb491fa0c6fd096386d7cf5038 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 3 Nov 2016 15:29:23 +0100
Subject: [PATCH 04/37] LOCAL: arm64: meson: add Amlogic Meson GX PM Suspend
Subject: [PATCH 04/54] LOCAL: arm64: meson: add Amlogic Meson GX PM Suspend
The Amlogic Meson GX SoCs uses a non-standard argument to the
PSCI CPU_SUSPEND call to enter system suspend.

View File

@@ -1,7 +1,7 @@
From ac95c04240bf6d1f12158f819bb9c0a0d06d6653 Mon Sep 17 00:00:00 2001
From 30b51e1a9d0c2eb84ea5430bfc9eb68f557295e9 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 3 Nov 2016 15:29:25 +0100
Subject: [PATCH 05/37] LOCAL: arm64: dts: meson: add support for GX PM and
Subject: [PATCH 05/54] LOCAL: arm64: dts: meson: add support for GX PM and
Virtual RTC
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>

View File

@@ -1,7 +1,7 @@
From 3198831b6d71337b85e5011fc820ea13057ab3a6 Mon Sep 17 00:00:00 2001
From 5d68f74491915ee2eee4f4a2d0e7faf4c618e85b Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 21 Jan 2021 01:35:36 +0000
Subject: [PATCH 06/37] LOCAL: arm64: dts: meson: add rtc/vrtc aliases to
Subject: [PATCH 06/54] LOCAL: arm64: dts: meson: add rtc/vrtc aliases to
Khadas VIM
Add aliases to ensure the vrtc time (which normally proves first) is /dev/rtc1

View File

@@ -1,7 +1,7 @@
From 596472232f2a08c7dd62597c9c041be4333e22b0 Mon Sep 17 00:00:00 2001
From 892b686fea1524983532028b064b991acb4a996a Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sat, 6 Nov 2021 13:01:08 +0000
Subject: [PATCH 07/37] LOCAL: arm64: dts: meson: add rtc/vrtc aliases to
Subject: [PATCH 07/54] LOCAL: arm64: dts: meson: add rtc/vrtc aliases to
Khadas VIM2
Add aliases to ensure the vrtc time (which normally proves first) is /dev/rtc1

View File

@@ -1,7 +1,7 @@
From a8f90df94245a52d5a0aff58a640b5c86e0ed83c Mon Sep 17 00:00:00 2001
From 06af057b5f5f54dd41505fdb3f3a8e48a6092830 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Mon, 1 Feb 2021 19:27:40 +0000
Subject: [PATCH 08/37] LOCAL: arm64: dts: meson: add rtc/vrtc aliases to Minix
Subject: [PATCH 08/54] LOCAL: arm64: dts: meson: add rtc/vrtc aliases to Minix
NEO U9-H
Add node aliases to prevent meson-vrtc from claiming /dev/rtc0

View File

@@ -1,7 +1,7 @@
From e751b7cfeaca07cef1b14cebbd4dc567ed50ed37 Mon Sep 17 00:00:00 2001
From 80e4f03703c050a8d03f7ea0c1138c52da66e2ea Mon Sep 17 00:00:00 2001
From: Anssi Hannula <anssi.hannula@iki.fi>
Date: Sun, 17 Apr 2022 04:37:48 +0000
Subject: [PATCH 09/37] LOCAL: ASoC: meson: assign internal PCM
Subject: [PATCH 09/54] LOCAL: ASoC: meson: assign internal PCM
chmap/ELD/IEC958 kctls to device 0
On SoC sound devices utilizing codec2codec DAI links with an HDMI codec the kctls

View File

@@ -1,7 +1,7 @@
From 3de630c16f0ddbaa6442ebc3b6938bc1c4526b34 Mon Sep 17 00:00:00 2001
From 417a84f1112d735c667a29041d3a876bc9ebc3d4 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 5 Jan 2023 15:16:46 +0000
Subject: [PATCH 10/37] LOCAL: media: meson: vdec: disable MPEG1/MPEG2 hardware
Subject: [PATCH 10/54] LOCAL: media: meson: vdec: disable MPEG1/MPEG2 hardware
decoding
The MPEG1/2 decoder is broken and nobody has volunteered to poke

View File

@@ -1,7 +1,7 @@
From 2411a183fc6f2806727cf9dcfe62dcd5e1199ce9 Mon Sep 17 00:00:00 2001
From 31d7d59be91e2b2f82ca5e55abd8fb7c2757fdd7 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sat, 3 May 2025 15:18:07 +0000
Subject: [PATCH 17/37] FROMLIST(v1): arm64: dts: amlogic: sm1-bananapi: lower
Subject: [PATCH 11/54] FROMGIT(6.18): arm64: dts: amlogic: sm1-bananapi: lower
SD card speed for stability
Users report being able to boot (u-boot) from SD card but kernel

View File

@@ -0,0 +1,91 @@
From de13bf61677213530ae8ac7813efea3c6991525d Mon Sep 17 00:00:00 2001
From: Anand Moon <linux.amoon@gmail.com>
Date: Mon, 25 Aug 2025 12:21:41 +0530
Subject: [PATCH 12/54] FROMGIT(6.18): arm64: dts: amlogic: Add cache
information to the Amlogic GXBB and GXL SoC
As per S905 and S905X datasheet add missing cache information to
the Amlogic GXBB and GXL SoC.
- Each Cortex-A53 core has 32KB of L1 instruction cache available and
32KB of L1 data cache available.
- Along with 512KB Unified L2 cache.
Cache memory significantly reduces the time it takes for the CPU
to access data and instructions, leading to faster program execution
and overall system responsiveness.
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 27 +++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index 260628cf218e..1a7333c7da96 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -95,6 +95,12 @@ cpu0: cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x0 0x0>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 0>;
#cooling-cells = <2>;
@@ -105,6 +111,12 @@ cpu1: cpu@1 {
compatible = "arm,cortex-a53";
reg = <0x0 0x1>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 0>;
#cooling-cells = <2>;
@@ -115,6 +127,12 @@ cpu2: cpu@2 {
compatible = "arm,cortex-a53";
reg = <0x0 0x2>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 0>;
#cooling-cells = <2>;
@@ -125,6 +143,12 @@ cpu3: cpu@3 {
compatible = "arm,cortex-a53";
reg = <0x0 0x3>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 0>;
#cooling-cells = <2>;
@@ -134,6 +158,9 @@ l2: l2-cache0 {
compatible = "cache";
cache-level = <2>;
cache-unified;
+ cache-size = <0x80000>; /* L2. 512 KB */
+ cache-line-size = <64>;
+ cache-sets = <512>;
};
};
--
2.34.1

View File

@@ -0,0 +1,81 @@
From 16c9fa28fcb4a8193fedd2841b317d1384c5be63 Mon Sep 17 00:00:00 2001
From: Anand Moon <linux.amoon@gmail.com>
Date: Mon, 25 Aug 2025 12:21:45 +0530
Subject: [PATCH 13/54] FROMGIT(6.18): arm64: dts: amlogic: Add cache
information to the Amlogic GXM SoCS
As per the GXM datasheet add missing cache information to the Amlogic GXM
SoC.
- Each Cortex-A53 core has 32KB of L1 instruction cache available and
32KB of L1 data cache available.
- Along with 512KB Unified L2 cache.
Cache memory significantly reduces the time it takes for the CPU
to access data and instructions, leading to faster program execution
and overall system responsiveness.
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
index 411cc312fc62..514c9bea6423 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
@@ -64,6 +64,12 @@ cpu4: cpu@100 {
reg = <0x0 0x100>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 1>;
#cooling-cells = <2>;
@@ -75,6 +81,12 @@ cpu5: cpu@101 {
reg = <0x0 0x101>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 1>;
#cooling-cells = <2>;
@@ -86,6 +98,12 @@ cpu6: cpu@102 {
reg = <0x0 0x102>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 1>;
#cooling-cells = <2>;
@@ -97,6 +115,12 @@ cpu7: cpu@103 {
reg = <0x0 0x103>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
clocks = <&scpi_dvfs 1>;
#cooling-cells = <2>;
--
2.34.1

View File

@@ -0,0 +1,91 @@
From 8f6a387b7ad84170a309872f72b586f4c8a38d31 Mon Sep 17 00:00:00 2001
From: Anand Moon <linux.amoon@gmail.com>
Date: Mon, 25 Aug 2025 12:21:43 +0530
Subject: [PATCH 14/54] FROMGIT(6.18): arm64: dts: amlogic: Add cache
information to the Amlogic G12A SoCS
As per the S905X2 datasheet add missing cache information to the Amlogic
G12A SoC.
- Each Cortex-A53 core has 32KB of L1 instruction cache available and
32KB of L1 data cache available.
- Along with 512KB Unified L2 cache.
Cache memory significantly reduces the time it takes for the CPU
to access data and instructions, leading to faster program execution
and overall system responsiveness.
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-g12a.dtsi | 27 +++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi
index deee61dbe074..1321ad95923d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-g12a.dtsi
@@ -17,6 +17,12 @@ cpu0: cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x0 0x0>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -26,6 +32,12 @@ cpu1: cpu@1 {
compatible = "arm,cortex-a53";
reg = <0x0 0x1>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -35,6 +47,12 @@ cpu2: cpu@2 {
compatible = "arm,cortex-a53";
reg = <0x0 0x2>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -44,6 +62,12 @@ cpu3: cpu@3 {
compatible = "arm,cortex-a53";
reg = <0x0 0x3>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -52,6 +76,9 @@ l2: l2-cache0 {
compatible = "cache";
cache-level = <2>;
cache-unified;
+ cache-size = <0x80000>; /* L2. 512 KB */
+ cache-line-size = <64>;
+ cache-sets = <512>;
};
};
--
2.34.1

View File

@@ -0,0 +1,142 @@
From fd85ad679733c0affc0fa89606a3c6f7e7d1280f Mon Sep 17 00:00:00 2001
From: Anand Moon <linux.amoon@gmail.com>
Date: Mon, 25 Aug 2025 12:21:50 +0530
Subject: [PATCH 15/54] FROMGIT(6.18): arm64: dts: amlogic: Add cache
information to the Amlogic S922X SoC
As per S922X datasheet add missing cache information to the Amlogic
S922X SoC.
- Each Cortex-A53 core has 32 KB of instruction cache and
32 KB of L1 data cache available.
- Each Cortex-A73 core has 64 KB of L1 instruction cache and
64 KB of L1 data cache available.
- The little (A53) cluster has 256 KB of unified L2 cache available.
- The big (A73) cluster has 1 MB of unified L2 cache available.
Cache memory significantly reduces the time it takes for the CPU
to access data and instructions, leading to faster program execution
and overall system responsiveness.
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-g12b.dtsi | 62 ++++++++++++++++++---
1 file changed, 55 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi
index 86e6ceb31d5e..f04efa828256 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-g12b.dtsi
@@ -49,7 +49,13 @@ cpu0: cpu@0 {
reg = <0x0 0x0>;
enable-method = "psci";
capacity-dmips-mhz = <592>;
- next-level-cache = <&l2>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
+ next-level-cache = <&l2_cache_l>;
#cooling-cells = <2>;
};
@@ -59,7 +65,13 @@ cpu1: cpu@1 {
reg = <0x0 0x1>;
enable-method = "psci";
capacity-dmips-mhz = <592>;
- next-level-cache = <&l2>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
+ next-level-cache = <&l2_cache_l>;
#cooling-cells = <2>;
};
@@ -69,7 +81,13 @@ cpu100: cpu@100 {
reg = <0x0 0x100>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
- next-level-cache = <&l2>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
+ next-level-cache = <&l2_cache_l>;
#cooling-cells = <2>;
};
@@ -79,7 +97,13 @@ cpu101: cpu@101 {
reg = <0x0 0x101>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
- next-level-cache = <&l2>;
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
+ next-level-cache = <&l2_cache_l>;
#cooling-cells = <2>;
};
@@ -89,7 +113,13 @@ cpu102: cpu@102 {
reg = <0x0 0x102>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
- next-level-cache = <&l2>;
+ d-cache-line-size = <64>;
+ d-cache-size = <0x10000>;
+ d-cache-sets = <64>;
+ i-cache-line-size = <64>;
+ i-cache-size = <0x10000>;
+ i-cache-sets = <64>;
+ next-level-cache = <&l2_cache_b>;
#cooling-cells = <2>;
};
@@ -99,14 +129,32 @@ cpu103: cpu@103 {
reg = <0x0 0x103>;
enable-method = "psci";
capacity-dmips-mhz = <1024>;
- next-level-cache = <&l2>;
+ d-cache-line-size = <64>;
+ d-cache-size = <0x10000>;
+ d-cache-sets = <64>;
+ i-cache-line-size = <64>;
+ i-cache-size = <0x10000>;
+ i-cache-sets = <64>;
+ next-level-cache = <&l2_cache_b>;
#cooling-cells = <2>;
};
- l2: l2-cache0 {
+ l2_cache_l: l2-cache-cluster0 {
compatible = "cache";
cache-level = <2>;
cache-unified;
+ cache-size = <0x40000>; /* L2. 256 KB */
+ cache-line-size = <64>;
+ cache-sets = <512>;
+ };
+
+ l2_cache_b: l2-cache-cluster1 {
+ compatible = "cache";
+ cache-level = <2>;
+ cache-unified;
+ cache-size = <0x100000>; /* L2. 1MB */
+ cache-line-size = <64>;
+ cache-sets = <512>;
};
};
};
--
2.34.1

View File

@@ -0,0 +1,91 @@
From 3db85f2107f75df070f04c8a4c1291de62ae9f41 Mon Sep 17 00:00:00 2001
From: Anand Moon <linux.amoon@gmail.com>
Date: Mon, 25 Aug 2025 12:21:42 +0530
Subject: [PATCH 16/54] FROMGIT(6.18): arm64: dts: amlogic: Add cache
information to the Amlogic SM1 SoC
As per S905X3 datasheet add missing cache information to the Amlogic
SM1 SoC. ARM Cortex-A55 CPU uses unified L3 cache instead of L2 cache.
- Each Cortex-A55 core has 32KB of L1 instruction cache available and
32KB of L1 data cache available.
- Along with 256KB Unified L2 cache.
Cache memory significantly reduces the time it takes for the CPU
to access data and instructions, leading to faster program execution
and overall system responsiveness.
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-sm1.dtsi | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi b/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi
index 966ebb19cc55..e5db8ce94062 100644
--- a/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-sm1.dtsi
@@ -55,6 +55,12 @@ cpu0: cpu@0 {
compatible = "arm,cortex-a55";
reg = <0x0 0x0>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -64,6 +70,12 @@ cpu1: cpu@1 {
compatible = "arm,cortex-a55";
reg = <0x0 0x1>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -73,6 +85,12 @@ cpu2: cpu@2 {
compatible = "arm,cortex-a55";
reg = <0x0 0x2>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -82,6 +100,12 @@ cpu3: cpu@3 {
compatible = "arm,cortex-a55";
reg = <0x0 0x3>;
enable-method = "psci";
+ d-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <32>;
+ i-cache-line-size = <32>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <32>;
next-level-cache = <&l2>;
#cooling-cells = <2>;
};
@@ -90,6 +114,9 @@ l2: l2-cache0 {
compatible = "cache";
cache-level = <2>;
cache-unified;
+ cache-size = <0x40000>; /* L2. 256 KB */
+ cache-line-size = <64>;
+ cache-sets = <256>;
};
};
--
2.34.1

View File

@@ -1,7 +1,7 @@
From 4c895bf75fbf67654d02032cfbdfa57fa4777b20 Mon Sep 17 00:00:00 2001
From 8708229747c8a0ee991f8910f2de1a6c5bb18f8c Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Wed, 9 Apr 2025 23:44:22 +0200
Subject: [PATCH 11/37] FROMLIST(v1): drm/meson: fix resource cleanup in
Subject: [PATCH 17/54] FROMLIST(v1): drm/meson: fix resource cleanup in
meson_drv_bind_master() on error
meson_drv_bind_master() does not free resources in the order they are

View File

@@ -1,7 +1,7 @@
From 6745bff6c6c1ee1746c955504a3ea32d12ec8d79 Mon Sep 17 00:00:00 2001
From 4357de91175470f8215a944344682f4c74a6166e Mon Sep 17 00:00:00 2001
From: Zhang Kunbo <zhangkunbo@huawei.com>
Date: Wed, 6 Nov 2024 02:45:48 +0000
Subject: [PATCH 12/37] FROMLIST(v1): drm/meson: Avoid use-after-free issues
Subject: [PATCH 18/54] FROMLIST(v1): drm/meson: Avoid use-after-free issues
with crtc
It's dangerous to call drm_crtc_init_with_planes() whose second

View File

@@ -1,7 +1,7 @@
From 4babe8c6a677ed0f21cf518c8ce78caecc8963b8 Mon Sep 17 00:00:00 2001
From d8c2c6a0c399bfa68a30f34e7a75785df3a9ace5 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Fri, 7 Feb 2025 04:29:08 +0000
Subject: [PATCH 13/37] FROMLIST(v2): media: si2168: increase cmd execution
Subject: [PATCH 19/54] FROMLIST(v2): media: si2168: increase cmd execution
timeout value
Testing with a MyGica T230C v2 USB device (0572:c68a) shows occasional

View File

@@ -1,7 +1,7 @@
From 3eb58e53708f534a47f4cccbd44efa36d5584a5c Mon Sep 17 00:00:00 2001
From 1a27ead3707a6b7148adefdf2c0a68a37097cd69 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Mon, 22 Nov 2021 09:15:21 +0000
Subject: [PATCH 14/37] FROMLIST(v1): media: meson: vdec: esparser: check
Subject: [PATCH 20/54] FROMLIST(v1): media: meson: vdec: esparser: check
parsing state with hardware write pointer
Also check the hardware write pointer to check if ES Parser has stalled.

View File

@@ -1,7 +1,7 @@
From 61874bc3835b2326bb40bae200888657e7d38fa3 Mon Sep 17 00:00:00 2001
From 48ea645056a1d006d46dd4bb13320d2e0e5dac19 Mon Sep 17 00:00:00 2001
From: Benjamin Roszak <benjamin545@gmail.com>
Date: Mon, 23 Jan 2023 10:56:46 +0000
Subject: [PATCH 15/37] FROMLIST(v2): media: meson: vdec: implement 10bit
Subject: [PATCH 21/54] FROMLIST(v2): media: meson: vdec: implement 10bit
bitstream handling
In order to support 10bit bitstream decoding, buffers and MMU

View File

@@ -1,7 +1,7 @@
From befb64847f9d38e20a38ed9ca3e351a4d80e9d05 Mon Sep 17 00:00:00 2001
From 0e174d88c295f45627b09163ea74f52ad25259b1 Mon Sep 17 00:00:00 2001
From: Maxime Jourdan <mjourdan@baylibre.com>
Date: Mon, 23 Jan 2023 11:07:04 +0000
Subject: [PATCH 16/37] FROMLIST(v2): media: meson: vdec: add HEVC decode codec
Subject: [PATCH 22/54] FROMLIST(v2): media: meson: vdec: add HEVC decode codec
Add initial HEVC codec for the Amlogic GXBB/GXL/GXM SoCs using
the common "HEVC" decoder driver.

View File

@@ -1,7 +1,7 @@
From a1f197da3061f0ccf210e7b69c478e31377196c4 Mon Sep 17 00:00:00 2001
From ca6474af65e4e4b9150cce40ef16fee0985538fc Mon Sep 17 00:00:00 2001
From: Andreas Baierl <ichgeh@imkreisrum.de>
Date: Tue, 2 Apr 2024 14:22:52 +0000
Subject: [PATCH 18/37] WIP: media: meson: vdec: reintroduce wiggle room
Subject: [PATCH 23/54] WIP: media: meson: vdec: reintroduce wiggle room
Without the wiggle room, it happens that matching offsets can't be found.
This results in non-matches and afterwards in frame drops in userspace apps.

View File

@@ -1,7 +1,7 @@
From e0ebd51471f1153d3d1dc53b9a645fde537b1411 Mon Sep 17 00:00:00 2001
From a87972cd735bd7a0360094e4e81095aaff4c76bf Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Tue, 14 Mar 2023 01:13:15 +0000
Subject: [PATCH 19/37] WIP: media: meson: vdec: fix memory leak of 'new_frame'
Subject: [PATCH 24/54] WIP: media: meson: vdec: fix memory leak of 'new_frame'
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <error27@gmail.com>

View File

@@ -1,7 +1,7 @@
From 32d8e8ef41de29cfede75a4eed0f33fa4d326057 Mon Sep 17 00:00:00 2001
From 2b570713704be6a255eae94dd4e3f4b40a60024a Mon Sep 17 00:00:00 2001
From: Andreas Baierl <ichgeh@imkreisrum.de>
Date: Thu, 20 Feb 2025 23:59:14 +0000
Subject: [PATCH 20/37] WIP: media: meson: vdec: fix
Subject: [PATCH 25/54] WIP: media: meson: vdec: fix
V4L2_BUF_FLAG_{KEY|P|B}FRAME
ffmpeg needs the keyframe flag to be set correctly, else

View File

@@ -1,7 +1,7 @@
From 2efb62e0eaeeb3d2c84b7d12c94a5ba99c257b06 Mon Sep 17 00:00:00 2001
From c9aa6d082e3cfd6c90648519cf6a00a188552d59 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sun, 26 May 2024 12:53:07 +0000
Subject: [PATCH 21/37] WIP: arm64: dts: meson: add Odroid-C2 HiFi-Shield
Subject: [PATCH 26/54] WIP: arm64: dts: meson: add Odroid-C2 HiFi-Shield
boards
Add experimental device-tree files for Odroid C2 with HiFi-Shield+ (pcm5102a)

View File

@@ -1,7 +1,7 @@
From 21708d1bcb84d0d8260c143746df5bff8dbe7a77 Mon Sep 17 00:00:00 2001
From c0d104a2891ffdeb6c0746636a4b993ae639613c Mon Sep 17 00:00:00 2001
From: Da Xue <da@libre.computer>
Date: Tue, 8 Aug 2023 01:00:15 -0400
Subject: [PATCH 22/37] WIP: net: phy: meson-gxl: implement
Subject: [PATCH 27/54] WIP: net: phy: meson-gxl: implement
meson_gxl_phy_resume()
While testing the suspend/resume functionality, we found the ethernet

View File

@@ -1,7 +1,7 @@
From 71facc1a41966252fd3368b8a6cfc02d3e91d85a Mon Sep 17 00:00:00 2001
From 00dc3a05e11e2930fa13bac35765a3ce6c596808 Mon Sep 17 00:00:00 2001
From: Dongjin Kim <tobetter@gmail.com>
Date: Thu, 10 Sep 2020 11:01:33 +0900
Subject: [PATCH 23/37] WIP: drm/meson: add support for 2560x1440 resolution
Subject: [PATCH 28/54] WIP: drm/meson: add support for 2560x1440 resolution
output
Add support for Quad HD (QHD) 2560x1440 resolution output. Timings

View File

@@ -1,7 +1,7 @@
From 5d95e031c6517a910173b80c50b800ffdc6493fa Mon Sep 17 00:00:00 2001
From 161d4ce0a257f861369c51dbe06f8291c41943c6 Mon Sep 17 00:00:00 2001
From: Luke Lu <luke.lu@libre.computer>
Date: Mon, 21 Aug 2023 10:50:04 +0000
Subject: [PATCH 24/37] WIP: drm/meson: do setup after resumption to fix hdmi
Subject: [PATCH 29/54] WIP: drm/meson: do setup after resumption to fix hdmi
output
Some HDMI displays connected to gxl-based boards go black after

View File

@@ -1,7 +1,7 @@
From b31529fd0ef42632387ce46def44a4e847d88ce1 Mon Sep 17 00:00:00 2001
From 2786ea5c4c7517cd23fdef585261a6ed18349058 Mon Sep 17 00:00:00 2001
From: Luke Lu <luke.lu@libre.computer>
Date: Wed, 13 Dec 2023 03:47:44 +0000
Subject: [PATCH 25/37] WIP: drm/meson: poweron/off dw_hdmi only if dw_hdmi
Subject: [PATCH 30/54] WIP: drm/meson: poweron/off dw_hdmi only if dw_hdmi
enabled
dw_hdmi_poweron() assumes that hdmi->curr_conn is valid. Calling

View File

@@ -1,7 +1,7 @@
From b2f06cfdd0e7d396d51aa733597f2a6631a4dc8c Mon Sep 17 00:00:00 2001
From 4527300ce59f7832cf0f572e95841bf4e709f09b Mon Sep 17 00:00:00 2001
From: Da Xue <da@libre.computer>
Date: Sun, 22 Jun 2025 17:46:21 -0400
Subject: [PATCH 26/37] WIP: mmc: meson-gx-mmc: add delay during poweroff
Subject: [PATCH 31/54] WIP: mmc: meson-gx-mmc: add delay during poweroff
---
drivers/mmc/host/meson-gx-mmc.c | 1 +

View File

@@ -1,7 +1,7 @@
From 59614ff80eb272009bc6bdfcb47e8df6ec93a2a7 Mon Sep 17 00:00:00 2001
From 5930c4938e460edb1077c4b98a05e871e5a1329f Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Tue, 18 Jan 2022 15:09:12 +0000
Subject: [PATCH 27/37] WIP: arm64: dts: meson: set p212/p23x/q20x SDIO to
Subject: [PATCH 32/54] WIP: arm64: dts: meson: set p212/p23x/q20x SDIO to
100MHz
Amlogic datasheets describe 50MHz max-frequency for SDIO on GXL/GXM but

View File

@@ -1,29 +0,0 @@
From bb0a8ba6c365a8dd979f2e3d040f565a1d1f36b0 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Sun, 20 Feb 2022 08:23:12 +0000
Subject: [PATCH 32/37] WIP: dt-bindings: vendor-prefixes: add Titan Micro
Electronics
Add a vendor prefix for Shenzhen Titan Micro Electronics Co., Ltd.
Signed-off-by: Heiner Kallweit <christianshewitt@gmail.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 5d2a7a8d3ac6..32f8f24ec809 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1548,6 +1548,8 @@ patternProperties:
description: Texas Instruments
"^tianma,.*":
description: Tianma Micro-electronics Co., Ltd.
+ "^titanmec,.*":
+ description: Shenzhen Titan Micro Electronics Co., Ltd.
"^tlm,.*":
description: Trusted Logic Mobility
"^tmt,.*":
--
2.34.1

View File

@@ -1,7 +1,7 @@
From 087bb82aefa86c31f4ea92774452392763a4c83b Mon Sep 17 00:00:00 2001
From 2c0f812be6825d1cff2ee697e6ec0b261c04a02f Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Tue, 18 Jan 2022 15:18:32 +0000
Subject: [PATCH 28/37] WIP: arm64: dts: meson: remove SDIO node from Khadas
Subject: [PATCH 33/54] WIP: arm64: dts: meson: remove SDIO node from Khadas
VIM1
Now that SDIO 100MHz max-frequency is inherited from the p212 dtsi we

View File

@@ -1,29 +0,0 @@
From ea1f663261cea2fe53626fd2d96c5e8b4d475e28 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 12 Jun 2025 10:25:29 +0000
Subject: [PATCH 33/37] WIP: dt-bindings: vendor-prefixes: add Fuda Hisi
Microelectronics
Add the "fdhisi" prefix for Fuda Hisi Microelectronics Co, Ltd.
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 32f8f24ec809..d02615496b2b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -531,6 +531,8 @@ patternProperties:
description: Fastrax Oy
"^fcs,.*":
description: Fairchild Semiconductor
+ "^fdhisi,.*":
+ description: Fuzhou Fuda Hisi Microelectronics Co., Ltd.
"^feixin,.*":
description: Shenzhen Feixin Photoelectic Co., Ltd
"^feiyang,.*":
--
2.34.1

View File

@@ -1,7 +1,7 @@
From d05fd0dbca2f4e7233ca5c1fb9fc8ca5575d4dbc Mon Sep 17 00:00:00 2001
From b367cb5a336587e37d6aeaad57359af4c9ed4d48 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 19 Jan 2022 06:45:06 +0000
Subject: [PATCH 29/37] WIP: arm64: dts: meson: add UHS SDIO capabilities to
Subject: [PATCH 34/54] WIP: arm64: dts: meson: add UHS SDIO capabilities to
p212/p23x/q20x
Add UHS capabilities to the SDIO node to enable 100MHz speeds.

View File

@@ -1,159 +0,0 @@
From 793c581ca351bfa08e185c3625ede56cc4cabf3f Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 12 Jun 2025 10:30:51 +0000
Subject: [PATCH 34/37] WIP: dt-bindings: auxdisplay: add Titan Micro
Electronics TM16XX
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add documentation for auxiliary displays based on TM16XX and compatible
LED controllers.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../bindings/auxdisplay/tm16xx.yaml | 131 ++++++++++++++++++
1 file changed, 131 insertions(+)
create mode 100644 Documentation/devicetree/bindings/auxdisplay/tm16xx.yaml
diff --git a/Documentation/devicetree/bindings/auxdisplay/tm16xx.yaml b/Documentation/devicetree/bindings/auxdisplay/tm16xx.yaml
new file mode 100644
index 000000000000..80b54572926c
--- /dev/null
+++ b/Documentation/devicetree/bindings/auxdisplay/tm16xx.yaml
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/auxdisplay/tm16xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TM16XX and Compatible Auxiliary Display Driver
+
+maintainers:
+ - Jean-François Lessard <jefflessard3@gmail.com>
+
+description: |
+ Bindings for auxiliary displays based on TM16XX and compatible LED controllers.
+
+properties:
+ compatible:
+ enum:
+ - titanmec,tm1618
+ - titanmec,tm1620
+ - titanmec,tm1628
+ - titanmec,tm1650
+ - fdhisi,fd620
+ - fdhisi,fd628
+ - fdhisi,fd650
+ - fdhisi,fd6551
+ - fdhisi,fd655
+ - princeton,pt6964
+ - hbs,hbs658
+
+ reg:
+ maxItems: 1
+
+ tm16xx,digits:
+ description: Array of grid indexes for each digit
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ items:
+ minimum: 0
+ maximum: 7
+ minItems: 1
+ maxItems: 8
+
+ tm16xx,segment-mapping:
+ description: Array specifying segment mapping (must be exactly 7 elements)
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ items:
+ minimum: 0
+ maximum: 7
+ minItems: 7
+ maxItems: 7
+
+ tm16xx,transposed:
+ description: |
+ Optional boolean flag indicating if the device output is transposed.
+ If not present, the default value is false.
+ $ref: /schemas/types.yaml#/definitions/flag
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^led@[0-7],[0-7]$":
+ $ref: /schemas/leds/common.yaml#
+ properties:
+ reg:
+ description: Grid and segment indexes
+ required:
+ - reg
+
+required:
+ - compatible
+ - reg
+ - tm16xx,digits
+ - tm16xx,segment-mapping
+
+additionalProperties: true
+
+examples:
+ - |
+ display_client: i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@24 {
+ compatible = "titanmec,tm1650";
+ reg = <0x24>;
+ tm16xx,digits = /bits/ 8 <0 1 2 3>;
+ tm16xx,segment-mapping = /bits/ 8 <0 1 2 3 4 5 6>;
+
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@4,0 {
+ reg = <4 0>;
+ function = "lan";
+ };
+
+ led@4,1 {
+ reg = <4 1>;
+ function = "wlan";
+ };
+ };
+ };
+ - |
+ display_client: spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ compatible = "titanmec,tm1628";
+ reg = <0>;
+ tm16xx,transposed;
+ tm16xx,digits = /bits/ 8 <1 2 3 4>;
+ tm16xx,segment-mapping = /bits/ 8 <0 1 2 3 4 5 6>;
+
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@0,2 {
+ reg = <0 2>;
+ function = "usb";
+ };
+
+ led@0,3 {
+ reg = <0 3>;
+ function = "power";
+ };
+ };
+ };
--
2.34.1

View File

@@ -1,100 +0,0 @@
From 536382633e9e399907fe06f34f426184965b5ab1 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 11 Jun 2025 11:23:44 +0000
Subject: [PATCH 35/37] WIP: arm64: dts: meson-gxl-s905w-tx3-mini: support the
fd628 display
The TX3-mini has an FD628 display. Add support using the tm166xx
kernel driver and userspace tools [0].
[0] https://github.com/jefflessard/tm16xx-display
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../dts/amlogic/meson-gxl-s905w-tx3-mini.dts | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts
index 6705c2082a78..94cae3a59554 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts
@@ -10,6 +10,8 @@
#include "meson-gxl-s905x.dtsi"
#include "meson-gx-p23x-q20x.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
/ {
compatible = "oranth,tx3-mini", "amlogic,s905w", "amlogic,meson-gxl";
@@ -19,6 +21,67 @@ memory@0 {
device_type = "memory";
reg = <0x0 0x0 0x0 0x40000000>; /* 1 GiB or 2 GiB */
};
+
+ display_client: spi {
+ compatible = "spi-gpio";
+ sck-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio 75 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio_ao 4 GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ compatible = "fdhisi,fd628";
+ reg = <0x0>;
+ spi-3wire;
+ spi-lsb-first;
+ spi-rx-delay-us = <1>;
+ spi-max-frequency = <500000>;
+
+ tm16xx,digits = [03 02 01 00];
+ tm16xx,segment-mapping = [03 04 05 00 01 02 06];
+
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@4,0 {
+ reg = <4 0>;
+ function = LED_FUNCTION_ALARM;
+ };
+
+ led@4,1 {
+ reg = <4 1>;
+ function = LED_FUNCTION_USB;
+ };
+
+ led@4,2 {
+ reg = <4 2>;
+ function = "play";
+ };
+
+ led@4,3 {
+ reg = <4 3>;
+ function = "pause";
+ };
+
+ led@4,4 {
+ reg = <4 4>;
+ function = "colon";
+ };
+
+ led@4,5 {
+ reg = <4 5>;
+ function = LED_FUNCTION_LAN;
+ };
+
+ led@4,6 {
+ reg = <4 6>;
+ function = LED_FUNCTION_WLAN;
+ };
+ };
+ };
};
&ir {
--
2.34.1

View File

@@ -1,7 +1,7 @@
From 595ae18972bfb55b20eecf724cf73d3c3b133910 Mon Sep 17 00:00:00 2001
From b8baf3c9ff435223796c3c2c7191727486e61ed2 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 9 Feb 2023 09:59:58 +0000
Subject: [PATCH 30/37] WIP: dt-bindings: arm: amlogic: add support for Tanix
Subject: [PATCH 35/54] WIP: dt-bindings: arm: amlogic: add support for Tanix
TX9 Pro
The Oranth Tanix TX9 Pro is an Android STB using the Amlogic S912 chip

View File

@@ -1,7 +1,7 @@
From 08187f15dc8ca4b29e2b847b3c5ed0026191480e Mon Sep 17 00:00:00 2001
From d112d904b1613118e5c2b9e0e9e874678de1f261 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 9 Feb 2023 10:01:14 +0000
Subject: [PATCH 31/37] WIP: arm64: dts: meson: add initial device-tree for
Subject: [PATCH 36/54] WIP: arm64: dts: meson: add initial device-tree for
Tanix TX9 Pro
Oranth Tanix TX9 Pro is based on the Amlogic Q200 reference design with

View File

@@ -1,99 +0,0 @@
From 6bf5753f848d4c9e66d5e55929b307715ee1d5c5 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 9 Feb 2023 10:11:39 +0000
Subject: [PATCH 36/37] WIP: arm64: dts: meson-gxm-tx9-pro: support the fd628
display
The TX9-Pro has an FD628 display. Add support using the tm166xx
kernel driver and userspace tools [0].
[0] https://github.com/jefflessard/tm16xx-display
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../boot/dts/amlogic/meson-gxm-tx9-pro.dts | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts
index 9a62176cfe5a..08603b035868 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts
@@ -9,6 +9,7 @@
#include "meson-gxm.dtsi"
#include "meson-gx-p23x-q20x.dtsi"
#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
/ {
compatible = "oranth,tx9-pro", "amlogic,s912", "amlogic,meson-gxm";
@@ -37,6 +38,67 @@ button {
gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_LOW>;
};
};
+
+ display_client: spi {
+ compatible = "spi-gpio";
+ sck-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio 75 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio 53 GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ compatible = "fdhisi,fd628";
+ reg = <0x0>;
+ spi-3wire;
+ spi-lsb-first;
+ spi-rx-delay-us = <1>;
+ spi-max-frequency = <500000>;
+
+ tm16xx,digits = [00 01 02 03];
+ tm16xx,segment-mapping = [03 01 02 06 04 05 00];
+
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@4,0 {
+ reg = <4 0>;
+ function = LED_FUNCTION_ALARM;
+ };
+
+ led@4,1 {
+ reg = <4 1>;
+ function = LED_FUNCTION_USB;
+ };
+
+ led@4,2 {
+ reg = <4 2>;
+ function = "play";
+ };
+
+ led@4,3 {
+ reg = <4 3>;
+ function = "pause";
+ };
+
+ led@4,4 {
+ reg = <4 4>;
+ function = "colon";
+ };
+
+ led@4,5 {
+ reg = <4 5>;
+ function = LED_FUNCTION_LAN;
+ };
+
+ led@4,6 {
+ reg = <4 6>;
+ function = LED_FUNCTION_WLAN;
+ };
+ };
+ };
};
&ethmac {
--
2.34.1

View File

@@ -1,100 +0,0 @@
From 7f3cc57dc50d31414a284761cfb5372942229571 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 11 Jun 2025 11:47:31 +0000
Subject: [PATCH 37/37] WIP: arm64: dts: meson-g12a-x96-max: support the fd628
display
The X96-Max has an FD628 display. Add support using the tm166xx
kernel driver and userspace tools [0].
[0] https://github.com/jefflessard/tm16xx-display
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../boot/dts/amlogic/meson-g12a-x96-max.dts | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts b/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts
index 5ab460a3e637..879276d6f6e9 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts
@@ -7,6 +7,7 @@
#include "meson-g12a.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/meson-g12a-gpio.h>
#include <dt-bindings/sound/meson-g12a-tohdmitx.h>
@@ -54,6 +55,68 @@ hdmi_connector_in: endpoint {
};
};
+ display_client: spi {
+ compatible = "spi-gpio";
+ sck-gpios = <&gpio 64 GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio 63 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio_ao 10 GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ compatible = "fdhisi,fd628";
+ reg = <0x0>;
+ spi-3wire;
+ spi-lsb-first;
+ spi-rx-delay-us = <1>;
+ spi-max-frequency = <500000>;
+
+ tm16xx,transposed;
+ tm16xx,digits = [00 01 02 03];
+ tm16xx,segment-mapping = [00 01 02 03 04 05 06];
+
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@4,0 {
+ reg = <4 0>;
+ function = "apps";
+ };
+
+ led@4,1 {
+ reg = <4 1>;
+ function = "setup";
+ };
+
+ led@4,2 {
+ reg = <4 2>;
+ function = LED_FUNCTION_USB;
+ };
+
+ led@4,3 {
+ reg = <4 3>;
+ function = LED_FUNCTION_SD;
+ };
+
+ led@4,4 {
+ reg = <4 4>;
+ function = "colon";
+ };
+
+ led@4,5 {
+ reg = <4 5>;
+ function = "hdmi";
+ };
+
+ led@4,6 {
+ reg = <4 6>;
+ function = "video";
+ };
+ };
+ };
+
emmc_pwrseq: emmc-pwrseq {
compatible = "mmc-pwrseq-emmc";
reset-gpios = <&gpio BOOT_12 GPIO_ACTIVE_LOW>;
--
2.34.1

View File

@@ -0,0 +1,131 @@
From 151262abde300e5b8c417f02b75e6c27cd79805c Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 3 Sep 2025 04:49:02 +0000
Subject: [PATCH 37/54] WIP: net: phy: icplus: add support for IP1001 variants
Experimental patch that:
- Adds #define IP1001_PHY_ID_ALT 0x02430d91
- Adds #define IP100C_PHY_ID 0x02430d98
- Adds variants to the icplus_driver[] array
- Adds variants to the MDIO device table
The IP1001 ALT device uses the same init/config as the existing device,
while the IP1001C has some init differences.
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
drivers/net/phy/icplus.c | 69 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
index c0c4f19cfb6a..4b5f593e0943 100644
--- a/drivers/net/phy/icplus.c
+++ b/drivers/net/phy/icplus.c
@@ -26,7 +26,7 @@
#include <asm/irq.h>
#include <linux/uaccess.h>
-MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IC1001 PHY drivers");
+MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IP1001/IP1001C PHY drivers");
MODULE_AUTHOR("Michael Barkowski");
MODULE_LICENSE("GPL");
@@ -64,6 +64,9 @@ MODULE_LICENSE("GPL");
#define IP175C_PHY_ID 0x02430d80
#define IP1001_PHY_ID 0x02430d90
+#define IP1001_PHY_ID_ALT 0x02430d91 // Additional identifier for IP1001
+#define IP1001C_PHY_ID 0x02430d98 // New: IP1001C identifier
+
#define IP101A_PHY_ID 0x02430c54
/* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin
@@ -182,6 +185,52 @@ static int ip1001_config_init(struct phy_device *phydev)
return 0;
}
+/* IP1001C specific config_init */
+static int ip1001c_config_init(struct phy_device *phydev)
+{
+ int val, ret;
+
+ /* Enable Auto Power Saving mode (same as IP1001) */
+ val = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2);
+ if (val < 0)
+ return val;
+ val |= IP1001_APS_ON;
+ ret = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, val);
+ if (ret < 0)
+ return ret;
+
+ /* RGMII delay settings, similar to IP1001, if required for IP1001C */
+ if (phy_interface_is_rgmii(phydev)) {
+ val = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
+ if (val < 0)
+ return val;
+
+ val &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ val |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL);
+ else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ val |= IP1001_RXPHASE_SEL;
+ else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ val |= IP1001_TXPHASE_SEL;
+
+ ret = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* IP1001C specific workaround: force 1000M/Master/Full-Duplex */
+ ret = phy_write(phydev, MII_BMCR, 0x0140); /* Full Duplex */
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MII_CTRL1000, 0x1800); /* 1000M Master */
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int ip175c_read_status(struct phy_device *phydev)
{
if (phydev->mdio.addr == 4) /* WAN port */
@@ -590,6 +639,22 @@ static struct phy_driver icplus_driver[] = {
.soft_reset = genphy_soft_reset,
.suspend = genphy_suspend,
.resume = genphy_resume,
+}, {
+ PHY_ID_MATCH_MODEL(IP1001_PHY_ID_ALT),
+ .name = "ICPlus IP1001",
+ /* PHY_GBIT_FEATURES */
+ .config_init = ip1001_config_init,
+ .soft_reset = genphy_soft_reset,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+}, {
+ PHY_ID_MATCH_MODEL(IP1001C_PHY_ID),
+ .name = "ICPlus IP1001C",
+ /* PHY_GBIT_FEATURES */
+ .config_init = ip1001c_config_init,
+ .soft_reset = genphy_soft_reset,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
}, {
.name = "ICPlus IP101A",
.match_phy_device = ip101a_match_phy_device,
@@ -628,6 +693,8 @@ module_phy_driver(icplus_driver);
static const struct mdio_device_id __maybe_unused icplus_tbl[] = {
{ PHY_ID_MATCH_MODEL(IP175C_PHY_ID) },
{ PHY_ID_MATCH_MODEL(IP1001_PHY_ID) },
+ { PHY_ID_MATCH_MODEL(IP1001_PHY_ID_ALT) },
+ { PHY_ID_MATCH_MODEL(IP1001C_PHY_ID) },
{ PHY_ID_MATCH_EXACT(IP101A_PHY_ID) },
{ }
};
--
2.34.1

View File

@@ -0,0 +1,54 @@
From 5ddfb27c0b310e3431336250c2ccf2fda3d8ddb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Mon, 1 Sep 2025 12:16:39 -0400
Subject: [PATCH 38/54] TEST: device property: Add scoped fwnode child node
iterators
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add scoped versions of fwnode child node iterators that automatically
handle reference counting cleanup using the __free() attribute:
- fwnode_for_each_child_node_scoped()
- fwnode_for_each_available_child_node_scoped()
These macros follow the same pattern as existing scoped iterators in the
kernel, ensuring fwnode references are automatically released when the
iterator variable goes out of scope. This prevents resource leaks and
eliminates the need for manual cleanup in error paths.
The implementation mirrors the non-scoped variants but uses
__free(fwnode_handle) for automatic resource management, providing a
safer and more convenient interface for drivers iterating over firmware
node children.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
include/linux/property.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/linux/property.h b/include/linux/property.h
index f718dd4789e5..8d119ee9f04a 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -175,6 +175,16 @@ struct fwnode_handle *fwnode_get_next_available_child_node(
for (child = fwnode_get_next_available_child_node(fwnode, NULL); child;\
child = fwnode_get_next_available_child_node(fwnode, child))
+#define fwnode_for_each_child_node_scoped(fwnode, child) \
+ for (struct fwnode_handle *child __free(fwnode_handle) = \
+ fwnode_get_next_child_node(fwnode, NULL); \
+ child; child = fwnode_get_next_child_node(fwnode, child))
+
+#define fwnode_for_each_available_child_node_scoped(fwnode, child) \
+ for (struct fwnode_handle *child __free(fwnode_handle) = \
+ fwnode_get_next_available_child_node(fwnode, NULL); \
+ child; child = fwnode_get_next_available_child_node(fwnode, child))
+
struct fwnode_handle *device_get_next_child_node(const struct device *dev,
struct fwnode_handle *child);
--
2.34.1

View File

@@ -0,0 +1,41 @@
From cf44b702a20d1cbd422d5116e3fe164852ca92f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Mon, 1 Sep 2025 12:18:40 -0400
Subject: [PATCH 39/54] TEST: i2c: core: Use
fwnode_for_each_child_node_scoped()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace the manual __free(fwnode_handle) iterator declaration with the
new scoped iterator macro for cleaner, less error-prone code.
This eliminates the need for explicit iterator variable declaration with
the cleanup attribute, making the code more consistent with other scoped
iterator usage patterns in the kernel.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
drivers/i2c/i2c-core-slave.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c
index 7ee6b992b835..02ca55c2246b 100644
--- a/drivers/i2c/i2c-core-slave.c
+++ b/drivers/i2c/i2c-core-slave.c
@@ -112,10 +112,9 @@ bool i2c_detect_slave_mode(struct device *dev)
struct fwnode_handle *fwnode = dev_fwnode(dev);
if (is_of_node(fwnode)) {
- struct fwnode_handle *child __free(fwnode_handle) = NULL;
u32 reg;
- fwnode_for_each_child_node(fwnode, child) {
+ fwnode_for_each_child_node_scoped(fwnode, child) {
fwnode_property_read_u32(child, "reg", &reg);
if (reg & I2C_OWN_SLAVE_ADDRESS)
return true;
--
2.34.1

View File

@@ -0,0 +1,113 @@
From cd95e8fd35c9fb37de5723a464d5fcc831c58ec1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 30 Aug 2025 20:31:38 -0400
Subject: [PATCH 40/54] TEST: auxdisplay: linedisp: encapsulate container_of
usage within to_linedisp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace direct container_of() calls with a to_linedisp() helper function
throughout the line-display auxdisplay library module. This abstraction
prepares for upcoming dual-mode support where linedisp context retrieval
will need to handle both dedicated child devices and attached parent
auxdisplay devices.
No functional changes in this patch.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
drivers/auxdisplay/line-display.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index 8590a4cd21e0..e44341b1ea22 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -31,6 +31,11 @@
#define DEFAULT_SCROLL_RATE (HZ / 2)
+static struct linedisp *to_linedisp(struct device *dev)
+{
+ return container_of(dev, struct linedisp, dev);
+}
+
/**
* linedisp_scroll() - scroll the display by a character
* @t: really a pointer to the private data structure
@@ -133,7 +138,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
static ssize_t message_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
return sysfs_emit(buf, "%s\n", linedisp->message);
}
@@ -152,7 +157,7 @@ static ssize_t message_show(struct device *dev, struct device_attribute *attr,
static ssize_t message_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
int err;
err = linedisp_display(linedisp, buf, count);
@@ -164,7 +169,7 @@ static DEVICE_ATTR_RW(message);
static ssize_t scroll_step_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
}
@@ -173,7 +178,7 @@ static ssize_t scroll_step_ms_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
unsigned int ms;
int err;
@@ -195,7 +200,7 @@ static DEVICE_ATTR_RW(scroll_step_ms);
static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
struct linedisp_map *map = linedisp->map;
memcpy(buf, &map->map, map->size);
@@ -205,7 +210,7 @@ static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, c
static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
struct linedisp_map *map = linedisp->map;
if (count != map->size)
@@ -232,7 +237,7 @@ static struct attribute *linedisp_attrs[] = {
static umode_t linedisp_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
struct linedisp_map *map = linedisp->map;
umode_t mode = attr->mode;
@@ -263,7 +268,7 @@ static DEFINE_IDA(linedisp_id);
static void linedisp_release(struct device *dev)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
kfree(linedisp->map);
kfree(linedisp->message);
--
2.34.1

View File

@@ -0,0 +1,92 @@
From a03fdbc61651abaf17e48cd4a867e8858685078d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sun, 31 Aug 2025 13:53:01 -0400
Subject: [PATCH 41/54] TEST: auxdisplay: linedisp: display static message when
length <= display size
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently, when a message shorter than the display size is written, the
content wraps around (e.g., "123" on a 4-digit display shows "1231")
without scrolling, which is confusing and unintuitive.
Change behavior to display short messages statically with space padding
(e.g. "123 ") while only scrolling messages longer than the display width.
This provides more natural behavior that aligns with user expectations
and current linedisp_display() kernel-doc.
The scroll logic is also consolidated into a helper function for clarity.
No API changes are introduced.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
drivers/auxdisplay/line-display.c | 29 +++++++++++++++++++++--------
1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index e44341b1ea22..ea23c43bb7b2 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -36,6 +36,11 @@ static struct linedisp *to_linedisp(struct device *dev)
return container_of(dev, struct linedisp, dev);
}
+static inline bool should_scroll(struct linedisp *linedisp)
+{
+ return linedisp->message_len > linedisp->num_chars && linedisp->scroll_rate;
+}
+
/**
* linedisp_scroll() - scroll the display by a character
* @t: really a pointer to the private data structure
@@ -67,7 +72,7 @@ static void linedisp_scroll(struct timer_list *t)
linedisp->scroll_pos %= linedisp->message_len;
/* rearm the timer */
- if (linedisp->message_len > num_chars && linedisp->scroll_rate)
+ if (should_scroll(linedisp))
mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
}
@@ -118,8 +123,16 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
linedisp->message_len = count;
linedisp->scroll_pos = 0;
- /* update the display */
- linedisp_scroll(&linedisp->timer);
+ if (should_scroll(linedisp)) {
+ /* display scrolling message */
+ linedisp_scroll(&linedisp->timer);
+ } else {
+ /* display static message */
+ memset(linedisp->buf, ' ', linedisp->num_chars);
+ memcpy(linedisp->buf, linedisp->message,
+ umin(linedisp->num_chars, linedisp->message_len));
+ linedisp->ops->update(linedisp);
+ }
return 0;
}
@@ -186,12 +199,12 @@ static ssize_t scroll_step_ms_store(struct device *dev,
if (err)
return err;
+ timer_delete_sync(&linedisp->timer);
+
linedisp->scroll_rate = msecs_to_jiffies(ms);
- if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
- timer_delete_sync(&linedisp->timer);
- if (linedisp->scroll_rate)
- linedisp_scroll(&linedisp->timer);
- }
+
+ if (should_scroll(linedisp))
+ linedisp_scroll(&linedisp->timer);
return count;
}
--
2.34.1

View File

@@ -0,0 +1,54 @@
From 9101338af83f9e74bad4163ad4d6569cb812699f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 30 Aug 2025 20:35:34 -0400
Subject: [PATCH 42/54] TEST: auxdisplay: linedisp: add num_chars sysfs
attribute
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add a read-only 'num_chars' sysfs attribute to report display digit count.
The num_chars attribute provides essential capability information to
userspace applications that need to know display dimensions before writing
messages, complementing the existing message and scroll controls.
No functional changes to existing behavior.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
drivers/auxdisplay/line-display.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index ea23c43bb7b2..abeed8812088 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -211,6 +211,16 @@ static ssize_t scroll_step_ms_store(struct device *dev,
static DEVICE_ATTR_RW(scroll_step_ms);
+static ssize_t num_chars_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct linedisp *linedisp = to_linedisp(dev);
+
+ return sysfs_emit(buf, "%u\n", linedisp->num_chars);
+}
+
+static DEVICE_ATTR_RO(num_chars);
+
static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct linedisp *linedisp = to_linedisp(dev);
@@ -242,6 +252,7 @@ static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
static struct attribute *linedisp_attrs[] = {
&dev_attr_message.attr,
&dev_attr_scroll_step_ms.attr,
+ &dev_attr_num_chars.attr,
&dev_attr_map_seg7.attr,
&dev_attr_map_seg14.attr,
NULL
--
2.34.1

View File

@@ -0,0 +1,281 @@
From 15c9ea76e000b860749d9c27e0d374cd0bcbe40a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 30 Aug 2025 20:41:39 -0400
Subject: [PATCH 43/54] TEST: auxdisplay: linedisp: support attribute
attachment to auxdisplay devices
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Enable linedisp library integration into existing kernel devices (like LED
class) to provide a uniform 7-segment userspace API without creating
separate child devices, meeting the consistent interface while maintaining
coherent device hierarchies.
This allows uniform 7-segment API across all drivers while solving device
proliferation and fragmented userspace interfaces.
The provided attributes appear in two locations depending on usage:
1. On linedisp.N child devices (legacy linedisp_register())
2. On the parent auxdisplay device (new linedisp_attach())
Functionality is identical in both modes.
Existing consumers of linedisp_register() are unaffected. The new API
enables drivers like TM16XX to integrate 7-segment display functionality
seamlessly within their LED class device hierarchy.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
drivers/auxdisplay/line-display.c | 160 +++++++++++++++++++++++++++++-
drivers/auxdisplay/line-display.h | 4 +
2 files changed, 161 insertions(+), 3 deletions(-)
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index abeed8812088..1176d46f0b24 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -6,20 +6,23 @@
* Author: Paul Burton <paul.burton@mips.com>
*
* Copyright (C) 2021 Glider bv
+ * Copyright (C) 2025 Jean-François Lessard
*/
#ifndef CONFIG_PANEL_BOOT_MESSAGE
#include <generated/utsrelease.h>
#endif
-#include <linux/container_of.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/idr.h>
#include <linux/jiffies.h>
#include <linux/kstrtox.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/timer.h>
@@ -31,9 +34,72 @@
#define DEFAULT_SCROLL_RATE (HZ / 2)
+struct linedisp_attachment {
+ struct list_head list;
+ struct device *device;
+ struct linedisp *linedisp;
+ bool owns_device; /* true for child device mode, false for attached mode */
+};
+
+static LIST_HEAD(linedisp_attachments);
+static DEFINE_SPINLOCK(linedisp_attachments_lock);
+
+static int create_attachment(struct device *dev, struct linedisp *linedisp, bool owns_device)
+{
+ struct linedisp_attachment *attachment;
+
+ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL);
+ if (!attachment)
+ return -ENOMEM;
+
+ attachment->device = dev;
+ attachment->linedisp = linedisp;
+ attachment->owns_device = owns_device;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+ list_add(&attachment->list, &linedisp_attachments);
+
+ return 0;
+}
+
+static struct linedisp *delete_attachment(struct device *dev, bool owns_device)
+{
+ struct linedisp_attachment *attachment;
+ struct linedisp *linedisp;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
+ if (attachment->device == dev &&
+ attachment->owns_device == owns_device)
+ break;
+ }
+
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
+ return NULL;
+
+ linedisp = attachment->linedisp;
+ list_del(&attachment->list);
+ kfree(attachment);
+
+ return linedisp;
+}
+
static struct linedisp *to_linedisp(struct device *dev)
{
- return container_of(dev, struct linedisp, dev);
+ struct linedisp_attachment *attachment;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
+ if (attachment->device == dev)
+ break;
+ }
+
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
+ return NULL;
+
+ return attachment->linedisp;
}
static inline bool should_scroll(struct linedisp *linedisp)
@@ -349,6 +415,87 @@ static int linedisp_init_map(struct linedisp *linedisp)
#define LINEDISP_INIT_TEXT "Linux " UTS_RELEASE " "
#endif
+/**
+ * linedisp_attach - attach a character line display
+ * @linedisp: pointer to character line display structure
+ * @dev: pointer of the device to attach to
+ * @num_chars: the number of characters that can be displayed
+ * @ops: character line display operations
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
+ unsigned int num_chars, const struct linedisp_ops *ops)
+{
+ int err;
+
+ memset(linedisp, 0, sizeof(*linedisp));
+ linedisp->ops = ops;
+ linedisp->num_chars = num_chars;
+ linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
+
+ linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
+ if (!linedisp->buf)
+ return -ENOMEM;
+
+ /* initialise a character mapping, if required */
+ err = linedisp_init_map(linedisp);
+ if (err)
+ goto out_free_buf;
+
+ /* initialise a timer for scrolling the message */
+ timer_setup(&linedisp->timer, linedisp_scroll, 0);
+
+ err = create_attachment(dev, linedisp, false);
+ if (err)
+ goto out_del_timer;
+
+ /* add attribute groups to target device */
+ err = device_add_groups(dev, linedisp_groups);
+ if (err)
+ goto out_del_attach;
+
+ /* display a default message */
+ err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
+ if (err)
+ goto out_rem_groups;
+
+ return 0;
+
+out_rem_groups:
+ device_remove_groups(dev, linedisp_groups);
+out_del_attach:
+ delete_attachment(dev, false);
+out_del_timer:
+ timer_delete_sync(&linedisp->timer);
+out_free_buf:
+ kfree(linedisp->buf);
+ return err;
+}
+EXPORT_SYMBOL_NS_GPL(linedisp_attach, "LINEDISP");
+
+/**
+ * linedisp_detach - detach a character line display
+ * @dev: pointer of the device to detach from, that was previously
+ * attached with linedisp_attach()
+ */
+void linedisp_detach(struct device *dev)
+{
+ struct linedisp *linedisp = delete_attachment(dev, false);
+
+ if (!linedisp)
+ return;
+
+ timer_delete_sync(&linedisp->timer);
+
+ device_remove_groups(dev, linedisp_groups);
+
+ kfree(linedisp->map);
+ kfree(linedisp->message);
+ kfree(linedisp->buf);
+}
+EXPORT_SYMBOL_NS_GPL(linedisp_detach, "LINEDISP");
+
/**
* linedisp_register - register a character line display
* @linedisp: pointer to character line display structure
@@ -391,10 +538,14 @@ int linedisp_register(struct linedisp *linedisp, struct device *parent,
/* initialise a timer for scrolling the message */
timer_setup(&linedisp->timer, linedisp_scroll, 0);
- err = device_add(&linedisp->dev);
+ err = create_attachment(&linedisp->dev, linedisp, true);
if (err)
goto out_del_timer;
+ err = device_add(&linedisp->dev);
+ if (err)
+ goto out_del_attach;
+
/* display a default message */
err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
if (err)
@@ -404,6 +555,8 @@ int linedisp_register(struct linedisp *linedisp, struct device *parent,
out_del_dev:
device_del(&linedisp->dev);
+out_del_attach:
+ delete_attachment(&linedisp->dev, true);
out_del_timer:
timer_delete_sync(&linedisp->timer);
out_put_device:
@@ -420,6 +573,7 @@ EXPORT_SYMBOL_NS_GPL(linedisp_register, "LINEDISP");
void linedisp_unregister(struct linedisp *linedisp)
{
device_del(&linedisp->dev);
+ delete_attachment(&linedisp->dev, true);
timer_delete_sync(&linedisp->timer);
put_device(&linedisp->dev);
}
diff --git a/drivers/auxdisplay/line-display.h b/drivers/auxdisplay/line-display.h
index 4348d7a2f69a..36853b639711 100644
--- a/drivers/auxdisplay/line-display.h
+++ b/drivers/auxdisplay/line-display.h
@@ -6,6 +6,7 @@
* Author: Paul Burton <paul.burton@mips.com>
*
* Copyright (C) 2021 Glider bv
+ * Copyright (C) 2025 Jean-François Lessard
*/
#ifndef _LINEDISP_H
@@ -81,6 +82,9 @@ struct linedisp {
unsigned int id;
};
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
+ unsigned int num_chars, const struct linedisp_ops *ops);
+void linedisp_detach(struct device *dev);
int linedisp_register(struct linedisp *linedisp, struct device *parent,
unsigned int num_chars, const struct linedisp_ops *ops);
void linedisp_unregister(struct linedisp *linedisp);
--
2.34.1

View File

@@ -0,0 +1,129 @@
From 6a205b4070ad6e129f80087d02baf0c7afd4b7e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sun, 31 Aug 2025 00:23:51 -0400
Subject: [PATCH 44/54] TEST: docs: ABI: auxdisplay: document linedisp library
sysfs attributes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add ABI documentation for sysfs attributes provided by the line-display
auxdisplay library module. These attributes enable text message display and
configuration on character-based auxdisplay devices.
Documents previously undocumented attributes:
- message, scroll_step_ms (introduced in v5.16)
- map_seg7, map_seg14 (introduced in v6.9)
Documents newly added attribute:
- num_chars (targeted for v6.18)
The line-display library is used by multiple auxdisplay drivers and
can expose these attributes either on linedisp.N child devices or
directly on parent auxdisplay devices.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
.../ABI/testing/sysfs-auxdisplay-linedisp | 90 +++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-auxdisplay-linedisp
diff --git a/Documentation/ABI/testing/sysfs-auxdisplay-linedisp b/Documentation/ABI/testing/sysfs-auxdisplay-linedisp
new file mode 100644
index 000000000000..63c47f19275e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-auxdisplay-linedisp
@@ -0,0 +1,90 @@
+What: /sys/.../message
+Date: October 2021
+KernelVersion: 5.16
+Description:
+ Controls the text message displayed on character line displays.
+
+ Reading returns the current message with a trailing newline.
+ Writing updates the displayed message. Messages longer than the
+ display width will automatically scroll. Trailing newlines in
+ input are automatically trimmed.
+
+ Writing an empty string clears the display.
+
+ Example:
+ echo "Hello World" > message
+ cat message # Returns "Hello World\n"
+
+What: /sys/.../scroll_step_ms
+Date: October 2021
+KernelVersion: 5.16
+Description:
+ Controls the scrolling speed for messages longer than the display
+ width, specified in milliseconds per scroll step.
+
+ Setting to 0 disables scrolling. Default is 500ms.
+
+ Example:
+ echo "250" > scroll_step_ms # 4Hz scrolling
+ cat scroll_step_ms # Returns "250\n"
+
+What: /sys/.../num_chars
+Date: November 2025
+KernelVersion: 6.18
+Contact: Jean-François Lessard <jefflessard3@gmail.com>
+Description:
+ Read-only attribute showing the character width capacity of
+ the line display device. Messages longer than this will scroll.
+
+ Example:
+ cat num_chars # Returns "16\n" for 16-char display
+
+What: /sys/.../map_seg7
+Date: January 2024
+KernelVersion: 6.9
+Description:
+ Read/write binary blob representing the ASCII-to-7-segment
+ display conversion table used by the linedisp driver, as defined
+ by struct seg7_conversion_map in <linux/map_to_7segment.h>.
+
+ Only visible on displays with 7-segment capability.
+
+ This attribute is not human-readable. Writes must match the
+ struct size exactly, else -EINVAL is returned; reads return the
+ entire mapping as a binary blob.
+
+ This interface and its implementation match existing conventions
+ used in segment-mapped display drivers since 2005.
+
+ ABI note: This style of binary sysfs attribute *is an exception*
+ to current "one value per file, text only" sysfs rules, for
+ historical compatibility and driver uniformity. New drivers are
+ discouraged from introducing additional binary sysfs ABIs.
+
+ Reference interface guidance:
+ - include/uapi/linux/map_to_7segment.h
+
+What: /sys/.../map_seg14
+Date: January 2024
+KernelVersion: 6.9
+Description:
+ Read/write binary blob representing the ASCII-to-14-segment
+ display conversion table used by the linedisp driver, as defined
+ by struct seg14_conversion_map in <linux/map_to_14segment.h>.
+
+ Only visible on displays with 14-segment capability.
+
+ This attribute is not human-readable. Writes must match the
+ struct size exactly, else -EINVAL is returned; reads return the
+ entire mapping as a binary blob.
+
+ This interface and its implementation match existing conventions
+ used by segment-mapped display drivers since 2005.
+
+ ABI note: This style of binary sysfs attribute *is an exception*
+ to current "one value per file, text only" sysfs rules, for
+ historical compatibility and driver uniformity. New drivers are
+ discouraged from introducing additional binary sysfs ABIs.
+
+ Reference interface guidance:
+ - include/uapi/linux/map_to_14segment.h
--
2.34.1

View File

@@ -0,0 +1,84 @@
From 0fadb57e2d1119fb45654fce943165d89961774c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 16 Aug 2025 16:17:06 -0400
Subject: [PATCH 45/54] TEST: dt-bindings: vendor-prefixes: Add fdhisi,
titanmec, princeton, winrise, wxicore
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add vendor prefixes of chip manufacturers supported by the TM16xx 7-segment
LED matrix display controllers driver:
- fdhisi: Fuzhou Fuda Hisi Microelectronics Co., Ltd.
- titanmec: Shenzhen Titan Micro Electronics Co., Ltd.
- princeton: Princeton Technology Corp.
- winrise: Shenzhen Winrise Technology Co., Ltd.
- wxicore: Wuxi i-Core Electronics Co., Ltd.
The titanmec prefix is based on the company's domain name titanmec.com.
The remaining prefixes are based on company names, as these manufacturers
either lack active .com domains or their .com domains are occupied by
unrelated businesses.
The fdhisi and titanmec prefixes were originally identified by
Andreas Färber.
CC: Andreas Färber <afaerber@suse.de>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 5d2a7a8d3ac6..20b20ab8df47 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -531,6 +531,8 @@ patternProperties:
description: Fastrax Oy
"^fcs,.*":
description: Fairchild Semiconductor
+ "^fdhisi,.*":
+ description: Fuzhou Fuda Hisi Microelectronics Co., Ltd.
"^feixin,.*":
description: Shenzhen Feixin Photoelectic Co., Ltd
"^feiyang,.*":
@@ -1216,6 +1218,8 @@ patternProperties:
description: Prime View International (PVI)
"^primux,.*":
description: Primux Trading, S.L.
+ "^princeton,.*":
+ description: Princeton Technology Corp.
"^probox2,.*":
description: PROBOX2 (by W2COMP Co., Ltd.)
"^pri,.*":
@@ -1548,6 +1552,8 @@ patternProperties:
description: Texas Instruments
"^tianma,.*":
description: Tianma Micro-electronics Co., Ltd.
+ "^titanmec,.*":
+ description: Shenzhen Titan Micro Electronics Co., Ltd.
"^tlm,.*":
description: Trusted Logic Mobility
"^tmt,.*":
@@ -1705,6 +1711,8 @@ patternProperties:
description: Wingtech Technology Co., Ltd.
"^winlink,.*":
description: WinLink Co., Ltd
+ "^winrise,.*":
+ description: Shenzhen Winrise Technology Co., Ltd.
"^winsen,.*":
description: Winsen Corp.
"^winstar,.*":
@@ -1721,6 +1729,8 @@ patternProperties:
description: Wobo
"^wolfvision,.*":
description: WolfVision GmbH
+ "^wxicore,.*":
+ description: Wuxi i-Core Electronics Co., Ltd.
"^x-powers,.*":
description: X-Powers
"^xen,.*":
--
2.34.1

View File

@@ -0,0 +1,41 @@
From 7bf0f1ab8318fb1219f1140c53b01be6e7228533 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Mon, 1 Sep 2025 11:57:15 -0400
Subject: [PATCH 46/54] TEST: dt-bindings: leds: add default-brightness
property to common.yaml
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add default-brightness property to leds/common.yaml to establish a single
canonical definition for LED brightness initialization.
The property is currently defined locally in leds/leds-pwm.yaml and is
needed by auxdisplay/titanmec,tm16xx.yaml. Properties should be defined
in only one location to avoid type inconsistencies across bindings.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
Documentation/devicetree/bindings/leds/common.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml
index 3e8319e44339..96bd7fd0f053 100644
--- a/Documentation/devicetree/bindings/leds/common.yaml
+++ b/Documentation/devicetree/bindings/leds/common.yaml
@@ -173,6 +173,12 @@ properties:
led-max-microamp.
$ref: /schemas/types.yaml#/definitions/uint32
+ default-brightness:
+ description:
+ Brightness to be set if LED's default state is on. Used only during
+ initialization. If the option is not set then max brightness is used.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
panic-indicator:
description:
This property specifies that the LED should be used, if at all possible,
--
2.34.1

View File

@@ -0,0 +1,529 @@
From 78fa203f005c5d8f0947bc6c404720c61a1f8c0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Mon, 1 Sep 2025 12:03:37 -0400
Subject: [PATCH 47/54] TEST: dt-bindings: auxdisplay: add Titan Micro
Electronics TM16xx
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add documentation for TM16xx-compatible 7-segment LED display controllers
with keyscan.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
.../bindings/auxdisplay/titanmec,tm16xx.yaml | 485 ++++++++++++++++++
MAINTAINERS | 5 +
2 files changed, 490 insertions(+)
create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
diff --git a/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml b/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
new file mode 100644
index 000000000000..9b143baa8188
--- /dev/null
+++ b/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
@@ -0,0 +1,485 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/auxdisplay/titanmec,tm16xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Auxiliary displays based on TM16xx and compatible LED controllers
+
+maintainers:
+ - Jean-François Lessard <jefflessard3@gmail.com>
+
+description: |
+ LED matrix controllers used in auxiliary display devices that drive individual
+ LED icons and 7-segment digit groups through a grid/segment addressing scheme.
+ Controllers manage a matrix of LEDs organized as grids (columns/banks in
+ vendor datasheets) and segments (rows/bit positions in vendor datasheets).
+ Maximum brightness and grid/segment indices are controller-specific.
+ Controller-specific maximum are validated in the driver.
+
+ The controller is agnostic of the display layout. Board-specific LED wiring is
+ described through child nodes that specify grid/segment coordinates for
+ individual icons and segment mapping for 7-segment digits.
+
+ The bindings use separate 'leds' and 'digits' containers to accommodate
+ different addressing schemes:
+ - LEDs use 2-cell addressing (grid, segment) for matrix coordinates
+ - Digits use 1-cell addressing with explicit segment mapping
+
+ The controller node exposes a logical LED-like control for the aggregate
+ display brightness. Child nodes describe individual icons and 7-seg digits.
+ The top-level control supports only label and brightness-related properties
+ and does not support other common LED properties such as color or function.
+ Child LED nodes use the standard LED binding.
+
+ Optional keypad scanning is supported when both 'linux,keymap' and
+ 'poll-interval' properties are specified.
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - fdhisi,fd628
+ - princeton,pt6964
+ - wxicore,aip1628
+ - const: titanmec,tm1628
+ - items:
+ - enum:
+ - wxicore,aip1618
+ - const: titanmec,tm1618
+ - items:
+ - enum:
+ - fdhisi,fd650
+ - wxicore,aip650
+ - const: titanmec,tm1650
+ - enum:
+ - fdhisi,fd620
+ - fdhisi,fd655
+ - fdhisi,fd6551
+ - titanmec,tm1618
+ - titanmec,tm1620
+ - titanmec,tm1628
+ - titanmec,tm1638
+ - titanmec,tm1650
+ - winrise,hbs658
+
+ reg:
+ maxItems: 1
+
+ label:
+ description:
+ The label for the top-level LED. If omitted, the label is taken from the
+ node name (excluding the unit address). It has to uniquely identify a
+ device, i.e. no other LED class device can be assigned the same label.
+
+ max-brightness:
+ minimum: 0 # 0=off
+ maximum: 8 # Maximum across all TM16xx controllers
+ description:
+ Normally the maximum brightness is determined by the hardware and this
+ property is not required. This property is used to put a software limit
+ on the brightness apart from what the driver says, as it could happen
+ that a LED can be made so bright that it gets damaged or causes damage
+ due to restrictions in a specific system, such as mounting conditions.
+
+ default-brightness:
+ minimum: 0 # 0=off
+ maximum: 8 # Maximum across all TM16xx controllers
+ description:
+ Brightness to be set if LED's default state is on. Used only during
+ initialization. If the option is not set then max brightness is used.
+
+ digits:
+ type: object
+ description: Container for 7-segment digit group definitions
+ additionalProperties: false
+
+ properties:
+ "#address-cells":
+ const: 1
+ "#size-cells":
+ const: 0
+
+ patternProperties:
+ "^digit@[0-9]+$":
+ type: object
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description:
+ Digit position identifier numbered sequentially left-to-right,
+ with reg=0 representing the leftmost digit position as displayed
+ to the user.
+ maxItems: 1
+
+ segments:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ description: |
+ Array of grid/segment coordinate pairs for each 7-segment position.
+ Each entry is <grid segment> mapping to standard 7-segment positions
+ in order: a, b, c, d, e, f, g
+
+ Standard 7-segment layout:
+ aaa
+ f b
+ f b
+ ggg
+ e c
+ e c
+ ddd
+ items:
+ items:
+ - description: Grid index
+ - description: Segment index
+ minItems: 7
+ maxItems: 7
+
+ required:
+ - reg
+ - segments
+
+ leds:
+ type: object
+ description: Container for individual LED icon definitions
+ additionalProperties: false
+
+ properties:
+ "#address-cells":
+ const: 2
+ "#size-cells":
+ const: 0
+
+ patternProperties:
+ "^led@[0-9]+,[0-9]+$":
+ type: object
+ $ref: /schemas/leds/common.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description:
+ Grid and segment indices as <grid segment> of this individual LED icon
+
+ required:
+ - reg
+
+dependencies:
+ poll-interval:
+ - linux,keymap
+ linux,keymap:
+ - poll-interval
+ autorepeat:
+ - linux,keymap
+ - poll-interval
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: /schemas/leds/common.yaml#
+ properties:
+ color: false
+ function: false
+ function-enumerator: false
+ - $ref: /schemas/input/input.yaml#
+ - $ref: /schemas/input/matrix-keymap.yaml#
+ # SPI controllers require 3-wire (combined MISO/MOSI line)
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - fdhisi,fd620
+ - fdhisi,fd628
+ - princeton,pt6964
+ - titanmec,tm1618
+ - titanmec,tm1620
+ - titanmec,tm1628
+ - titanmec,tm1638
+ - wxicore,aip1618
+ - wxicore,aip1628
+ then:
+ $ref: /schemas/spi/spi-peripheral-props.yaml#
+ properties:
+ spi-3wire: true
+ required:
+ - spi-3wire
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/leds/common.h>
+
+ // I2C example: Magicsee N5 TV box with fd655 controller
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@24 {
+ reg = <0x24>;
+ compatible = "fdhisi,fd655";
+
+ digits {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ digit@0 {
+ reg = <0>;
+ segments = <4 3>, <4 4>, <4 5>, <4 0>, <4 1>, <4 2>, <4 6>;
+ };
+
+ digit@1 {
+ reg = <1>;
+ segments = <3 3>, <3 4>, <3 5>, <3 0>, <3 1>, <3 2>, <3 6>;
+ };
+
+ digit@2 {
+ reg = <2>;
+ segments = <2 3>, <2 4>, <2 5>, <2 0>, <2 1>, <2 2>, <2 6>;
+ };
+
+ digit@3 {
+ reg = <3>;
+ segments = <1 3>, <1 4>, <1 5>, <1 0>, <1 1>, <1 2>, <1 6>;
+ };
+ };
+
+ leds {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@0,0 {
+ reg = <0 0>;
+ function = LED_FUNCTION_ALARM;
+ };
+
+ led@0,1 {
+ reg = <0 1>;
+ function = LED_FUNCTION_USB;
+ };
+
+ led@0,2 {
+ reg = <0 2>;
+ function = "play";
+ };
+
+ led@0,3 {
+ reg = <0 3>;
+ function = "pause";
+ };
+
+ led@0,4 {
+ reg = <0 4>;
+ function = "colon";
+ };
+
+ led@0,5 {
+ reg = <0 5>;
+ function = LED_FUNCTION_LAN;
+ };
+
+ led@0,6 {
+ reg = <0 6>;
+ function = LED_FUNCTION_WLAN;
+ };
+ };
+ };
+ };
+
+ - |
+ #include <dt-bindings/input/input.h>
+
+ // SPI example: TM1638 module with keypad support
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ reg = <0>;
+ compatible = "titanmec,tm1638";
+ spi-3wire;
+ spi-lsb-first;
+ spi-max-frequency = <500000>;
+
+ label = "tm1638";
+ default-brightness = <2>;
+ max-brightness = <4>;
+ poll-interval = <100>;
+ linux,keymap = <MATRIX_KEY(2, 0, KEY_F1)
+ MATRIX_KEY(2, 2, KEY_F2)
+ MATRIX_KEY(2, 4, KEY_F3)
+ MATRIX_KEY(2, 6, KEY_F4)
+ MATRIX_KEY(2, 1, KEY_F5)
+ MATRIX_KEY(2, 3, KEY_F6)
+ MATRIX_KEY(2, 5, KEY_F7)
+ MATRIX_KEY(2, 7, KEY_F8)>;
+
+ digits {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ digit@0 {
+ reg = <0>;
+ segments = <7 0>, <7 1>, <7 2>, <7 3>, <7 4>, <7 5>, <7 6>;
+ };
+
+ digit@1 {
+ reg = <1>;
+ segments = <6 0>, <6 1>, <6 2>, <6 3>, <6 4>, <6 5>, <6 6>;
+ };
+
+ digit@2 {
+ reg = <2>;
+ segments = <5 0>, <5 1>, <5 2>, <5 3>, <5 4>, <5 5>, <5 6>;
+ };
+
+ digit@3 {
+ reg = <3>;
+ segments = <4 0>, <4 1>, <4 2>, <4 3>, <4 4>, <4 5>, <4 6>;
+ };
+
+ digit@4 {
+ reg = <4>;
+ segments = <3 0>, <3 1>, <3 2>, <3 3>, <3 4>, <3 5>, <3 6>;
+ };
+
+ digit@5 {
+ reg = <5>;
+ segments = <2 0>, <2 1>, <2 2>, <2 3>, <2 4>, <2 5>, <2 6>;
+ };
+
+ digit@6 {
+ reg = <6>;
+ segments = <1 0>, <1 1>, <1 2>, <1 3>, <1 4>, <1 5>, <1 6>;
+ };
+
+ digit@7 {
+ reg = <7>;
+ segments = <0 0>, <0 1>, <0 2>, <0 3>, <0 4>, <0 5>, <0 6>;
+ };
+ };
+
+ leds {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@0,7 {
+ reg = <0 7>;
+ };
+
+ led@1,7 {
+ reg = <1 7>;
+ };
+
+ led@2,7 {
+ reg = <2 7>;
+ };
+
+ led@3,7 {
+ reg = <3 7>;
+ };
+
+ led@4,7 {
+ reg = <4 7>;
+ };
+
+ led@5,7 {
+ reg = <5 7>;
+ };
+
+ led@6,7 {
+ reg = <6 7>;
+ };
+
+ led@7,7 {
+ reg = <7 7>;
+ };
+ };
+ };
+ };
+
+ - |
+ #include <dt-bindings/leds/common.h>
+
+ // SPI example: X96 Max with transposed layout (fd628 with tm1628 fallback)
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ reg = <0>;
+ compatible = "fdhisi,fd628", "titanmec,tm1628";
+ spi-3wire;
+ spi-lsb-first;
+ spi-max-frequency = <500000>;
+
+ digits {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ digit@0 {
+ reg = <0>;
+ segments = <0 3>, <1 3>, <2 3>, <3 3>, <4 3>, <5 3>, <6 3>;
+ };
+
+ digit@1 {
+ reg = <1>;
+ segments = <0 2>, <1 2>, <2 2>, <3 2>, <4 2>, <5 2>, <6 2>;
+ };
+
+ digit@2 {
+ reg = <2>;
+ segments = <0 1>, <1 1>, <2 1>, <3 1>, <4 1>, <5 1>, <6 1>;
+ };
+
+ digit@3 {
+ reg = <3>;
+ segments = <0 0>, <1 0>, <2 0>, <3 0>, <4 0>, <5 0>, <6 0>;
+ };
+ };
+
+ leds {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ led@0,4 {
+ reg = <0 4>;
+ function = "apps";
+ };
+
+ led@1,4 {
+ reg = <1 4>;
+ function = "setup";
+ };
+
+ led@2,4 {
+ reg = <2 4>;
+ function = LED_FUNCTION_USB;
+ };
+
+ led@3,4 {
+ reg = <3 4>;
+ function = LED_FUNCTION_SD;
+ };
+
+ led@4,4 {
+ reg = <4 4>;
+ function = "colon";
+ };
+
+ led@5,4 {
+ reg = <5 4>;
+ function = "hdmi";
+ };
+
+ led@6,4 {
+ reg = <6 4>;
+ function = "video";
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index c0b444e5fd5a..06b68aa9b6f8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25043,6 +25043,11 @@ W: http://sourceforge.net/projects/tlan/
F: Documentation/networking/device_drivers/ethernet/ti/tlan.rst
F: drivers/net/ethernet/ti/tlan.*
+TM16XX-COMPATIBLE LED CONTROLLERS DISPLAY DRIVER
+M: Jean-François Lessard <jefflessard3@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
+
TMIO/SDHI MMC DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
L: linux-mmc@vger.kernel.org
--
2.34.1

View File

@@ -0,0 +1,726 @@
From b6c377ca8269ab4e4359700eb7a596acfdea9933 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 16 Aug 2025 20:52:21 -0400
Subject: [PATCH 48/54] TEST: auxdisplay: Add TM16xx 7-segment LED matrix
display controllers driver
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add driver for TM16xx family LED controllers and compatible chips from
multiple vendors including Titan Micro, Fuda Hisi, i-Core, Princeton, and
Winrise. These controllers drive 7-segment digits and individual LED icons
through either I2C or SPI buses.
Successfully tested on various ARM TV boxes including H96 Max, Magicsee N5,
Tanix TX3 Mini, Tanix TX6, X92, and X96 Max across different SoC platforms
(Rockchip, Amlogic, Allwinner).
Acked-by: Paolo Sabatino <paolo.sabatino@gmail.com> # As primary user, integrated tm16xx into Armbian rockchip64
Acked-by: Christian Hewitt <christianshewitt@gmail.com> # As primary user, integrated tm16xx into LibreElec
Tested-by: Boris Gjenero <boris.gjenero@gmail.com> # Tested on X92
Tested-by: Paolo Sabatino <paolo.sabatino@gmail.com> # Tested on H96 Max (XY_RK3328)
Tested-by: Christian Hewitt <christianshewitt@gmail.com> # Tested on X96 Max, Tanix TX3 Mini
Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> # Tested on Tanix TX3 Mini
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
MAINTAINERS | 3 +
drivers/auxdisplay/Kconfig | 9 +
drivers/auxdisplay/Makefile | 2 +
drivers/auxdisplay/tm16xx.h | 172 ++++++++++++
drivers/auxdisplay/tm16xx_core.c | 459 +++++++++++++++++++++++++++++++
5 files changed, 645 insertions(+)
create mode 100644 drivers/auxdisplay/tm16xx.h
create mode 100644 drivers/auxdisplay/tm16xx_core.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 06b68aa9b6f8..e936dbe2ab0b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25046,7 +25046,10 @@ F: drivers/net/ethernet/ti/tlan.*
TM16XX-COMPATIBLE LED CONTROLLERS DISPLAY DRIVER
M: Jean-François Lessard <jefflessard3@gmail.com>
S: Maintained
+F: Documentation/ABI/testing/sysfs-class-leds-tm16xx
F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
+F: drivers/auxdisplay/tm16xx.h
+F: drivers/auxdisplay/tm16xx_core.c
TMIO/SDHI MMC DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index bedc6133f970..7bacf11112b5 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -526,6 +526,15 @@ config SEG_LED_GPIO
This driver can also be built as a module. If so, the module
will be called seg-led-gpio.
+config TM16XX
+ tristate
+ select LEDS_CLASS
+ select LEDS_TRIGGERS
+ select LINEDISP
+ select NEW_LEDS
+ help
+ Core TM16XX-compatible 7-segment LED controllers module
+
#
# Character LCD with non-conforming interface section
#
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index f5c13ed1cd4f..7ecf3cd4a0d3 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_LINEDISP) += line-display.o
obj-$(CONFIG_MAX6959) += max6959.o
obj-$(CONFIG_PARPORT_PANEL) += panel.o
obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o
+obj-$(CONFIG_TM16XX) += tm16xx.o
+tm16xx-y += tm16xx_core.o
diff --git a/drivers/auxdisplay/tm16xx.h b/drivers/auxdisplay/tm16xx.h
new file mode 100644
index 000000000000..973b6ac19515
--- /dev/null
+++ b/drivers/auxdisplay/tm16xx.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TM16xx and compatible LED display/keypad controller driver
+ * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips.
+ *
+ * Copyright (C) 2025 Jean-François Lessard
+ */
+
+#ifndef _TM16XX_H
+#define _TM16XX_H
+
+#include <linux/bitfield.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+
+#include "line-display.h"
+
+/* Common bit field definitions */
+
+/* Command type bits (bits 7-6) */
+#define TM16XX_CMD_MASK GENMASK(7, 6)
+#define TM16XX_CMD_MODE (0 << 6)
+#define TM16XX_CMD_DATA (1 << 6)
+#define TM16XX_CMD_CTRL (2 << 6)
+#define TM16XX_CMD_ADDR (3 << 6)
+#define TM16XX_CMD_WRITE (TM16XX_CMD_DATA | TM16XX_DATA_MODE_WRITE)
+#define TM16XX_CMD_READ (TM16XX_CMD_DATA | TM16XX_DATA_MODE_READ)
+
+/* Mode command grid settings (bits 1-0) */
+#define TM16XX_MODE_GRID_MASK GENMASK(1, 0)
+#define TM16XX_MODE_4GRIDS (0 << 0)
+#define TM16XX_MODE_5GRIDS (1 << 0)
+#define TM16XX_MODE_6GRIDS (2 << 0)
+#define TM16XX_MODE_7GRIDS (3 << 0)
+
+/* Data command settings */
+#define TM16XX_DATA_ADDR_MASK BIT(2)
+#define TM16XX_DATA_ADDR_AUTO (0 << 2)
+#define TM16XX_DATA_ADDR_FIXED (1 << 2)
+#define TM16XX_DATA_MODE_MASK GENMASK(1, 0)
+#define TM16XX_DATA_MODE_WRITE (0 << 0)
+#define TM16XX_DATA_MODE_READ (2 << 0)
+
+/* Control command settings */
+#define TM16XX_CTRL_BR_MASK GENMASK(2, 0)
+#define TM16XX_CTRL_ON (1 << 3)
+
+/* TM1618 specific constants */
+#define TM1618_BYTE1_MASK GENMASK(4, 0)
+#define TM1618_BYTE2_MASK GENMASK(7, 5)
+#define TM1618_BYTE2_SHIFT 3
+#define TM1618_KEY_READ_LEN 3
+#define TM1618_KEY_MASK (BIT(4) | BIT(1))
+
+/* TM1628 specific constants */
+#define TM1628_BYTE1_MASK GENMASK(7, 0)
+#define TM1628_BYTE2_MASK GENMASK(13, 8)
+#define TM1628_KEY_READ_LEN 5
+#define TM1628_KEY_MASK (GENMASK(4, 3) | GENMASK(1, 0))
+
+/* TM1638 specific constants */
+#define TM1638_KEY_READ_LEN 4
+#define TM1638_KEY_MASK (GENMASK(6, 4) | GENMASK(2, 0))
+
+/* FD620 specific constants */
+#define FD620_BYTE1_MASK GENMASK(6, 0)
+
+#define FD620_BYTE2_MASK BIT(7)
+#define FD620_BYTE2_SHIFT 5
+#define FD620_KEY_READ_LEN 4
+#define FD620_KEY_MASK (BIT(3) | BIT(0))
+
+/* I2C controller addresses and control settings */
+#define TM1650_CMD_CTRL 0x48
+#define TM1650_CMD_READ 0x4F
+#define TM1650_CMD_ADDR 0x68
+#define TM1650_CTRL_BR_MASK GENMASK(6, 4)
+#define TM1650_CTRL_ON (1 << 0)
+#define TM1650_CTRL_SLEEP (1 << 2)
+#define TM1650_CTRL_SEG_MASK BIT(3)
+#define TM1650_CTRL_SEG8_MODE (0 << 3)
+#define TM1650_CTRL_SEG7_MODE (1 << 3)
+#define TM1650_KEY_ROW_MASK GENMASK(1, 0)
+#define TM1650_KEY_COL_MASK GENMASK(5, 3)
+#define TM1650_KEY_DOWN_MASK BIT(6)
+#define TM1650_KEY_COMBINED GENMASK(5, 3)
+
+#define FD655_CMD_CTRL 0x48
+#define FD655_CMD_ADDR 0x66
+#define FD655_CTRL_BR_MASK GENMASK(6, 5)
+#define FD655_CTRL_ON (1 << 0)
+
+#define FD6551_CMD_CTRL 0x48
+#define FD6551_CTRL_BR_MASK GENMASK(3, 1)
+#define FD6551_CTRL_ON (1 << 0)
+
+#define HBS658_KEY_COL_MASK GENMASK(7, 5)
+
+#define TM16XX_CTRL_BRIGHTNESS(on, val, prefix) \
+ ((on) ? (FIELD_PREP(prefix##_CTRL_BR_MASK, (val)) | prefix##_CTRL_ON) : 0)
+
+/* Forward declarations */
+struct tm16xx_display;
+struct tm16xx_digit;
+struct tm16xx_led;
+
+/**
+ * DOC: struct tm16xx_controller - Controller-specific operations and limits
+ * @max_grids: Maximum number of grids supported by the controller.
+ * @max_segments: Maximum number of segments supported by the controller.
+ * @max_brightness: Maximum brightness level supported by the controller.
+ * @max_key_rows: Maximum number of key input rows supported by the controller.
+ * @max_key_cols: Maximum number of key input columns supported by the controller.
+ * @init: Pointer to controller mode/brightness configuration function.
+ * @data: Pointer to function writing display data to the controller.
+ * @keys: Pointer to function reading controller key state into bitmap.
+ *
+ * Holds function pointers and limits for controller-specific operations.
+ */
+struct tm16xx_controller {
+ const u8 max_grids;
+ const u8 max_segments;
+ const u8 max_brightness;
+ const u8 max_key_rows;
+ const u8 max_key_cols;
+ int (*const init)(struct tm16xx_display *display);
+ int (*const data)(struct tm16xx_display *display, u8 index, unsigned int grid);
+ int (*const keys)(struct tm16xx_display *display);
+};
+
+/**
+ * struct tm16xx_display - Main driver structure for the display
+ * @dev: Pointer to device struct.
+ * @controller: Controller-specific function table and limits.
+ * @linedisp: character line display structure
+ * @spi_buffer: DMA-safe buffer for SPI transactions, or NULL for I2C.
+ * @num_hwgrid: Number of controller grids in use.
+ * @num_hwseg: Number of controller segments in use.
+ * @main_led: LED class device for the entire display.
+ * @leds: Array of individual LED icon structures.
+ * @num_leds: Number of individual LED icons.
+ * @digits: Array of 7-segment digit structures.
+ * @num_digits: Number of 7-segment digits.
+ * @flush_init: Work struct for configuration update.
+ * @flush_display: Work struct for display update.
+ * @flush_status: Status/result of last flush work.
+ * @lock: Mutex protecting concurrent access to work operations.
+ * @state: Bitmap holding current raw display state.
+ */
+struct tm16xx_display {
+ struct device *dev;
+ const struct tm16xx_controller *controller;
+ struct linedisp linedisp;
+ u8 *spi_buffer;
+ u8 num_hwgrid;
+ u8 num_hwseg;
+ struct led_classdev main_led;
+ struct tm16xx_led *leds;
+ u8 num_leds;
+ struct tm16xx_digit *digits;
+ u8 num_digits;
+ struct work_struct flush_init;
+ struct work_struct flush_display;
+ int flush_status;
+ struct mutex lock; /* prevents concurrent work operations */
+ unsigned long *state;
+};
+
+int tm16xx_probe(struct tm16xx_display *display);
+void tm16xx_remove(struct tm16xx_display *display);
+
+#endif /* _TM16XX_H */
diff --git a/drivers/auxdisplay/tm16xx_core.c b/drivers/auxdisplay/tm16xx_core.c
new file mode 100644
index 000000000000..e090c578f8a0
--- /dev/null
+++ b/drivers/auxdisplay/tm16xx_core.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TM16xx and compatible LED display/keypad controller driver
+ * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips.
+ *
+ * Copyright (C) 2025 Jean-François Lessard
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/map_to_7segment.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
+#include "line-display.h"
+#include "tm16xx.h"
+
+#define TM16XX_DIGIT_SEGMENTS 7
+
+#define linedisp_to_tm16xx(display) \
+ container_of(display, struct tm16xx_display, linedisp)
+
+/**
+ * struct tm16xx_led - Individual LED icon mapping
+ * @cdev: LED class device for sysfs interface.
+ * @hwgrid: Controller grid index of the LED.
+ * @hwseg: Controller segment index of the LED.
+ */
+struct tm16xx_led {
+ struct led_classdev cdev;
+ u8 hwgrid;
+ u8 hwseg;
+};
+
+/**
+ * struct tm16xx_digit - 7-segment digit mapping and value
+ * @hwgrids: Array mapping each 7-segment position to a grid on the controller.
+ * @hwsegs: Array mapping each 7-segment position to a segment on the controller.
+ * @value: Current character value displayed on this digit.
+ */
+struct tm16xx_digit {
+ u8 hwgrids[TM16XX_DIGIT_SEGMENTS];
+ u8 hwsegs[TM16XX_DIGIT_SEGMENTS];
+};
+
+/* state bitmap helpers */
+/**
+ * tm16xx_led_nbits() - Number of bits used for the display state bitmap
+ * @display: pointer to tm16xx_display
+ *
+ * Return: total bits in the display state bitmap (grids * segments)
+ */
+static inline unsigned int tm16xx_led_nbits(const struct tm16xx_display *display)
+{
+ return display->num_hwgrid * display->num_hwseg;
+}
+
+/**
+ * tm16xx_set_seg() - Set the display state for a specific grid/segment
+ * @display: pointer to tm16xx_display
+ * @hwgrid: grid index
+ * @hwseg: segment index
+ * @on: true to turn on, false to turn off
+ */
+static inline void tm16xx_set_seg(const struct tm16xx_display *display,
+ const u8 hwgrid, const u8 hwseg, const bool on)
+{
+ assign_bit(hwgrid * display->num_hwseg + hwseg, display->state, on);
+}
+
+/**
+ * tm16xx_get_grid() - Get the current segment pattern for a grid
+ * @display: pointer to tm16xx_display
+ * @index: grid index
+ *
+ * Return: bit pattern of all segments for the given grid
+ */
+static inline unsigned int tm16xx_get_grid(const struct tm16xx_display *display,
+ const unsigned int index)
+{
+ return bitmap_read(display->state, index * display->num_hwseg,
+ display->num_hwseg);
+}
+
+/* main display */
+/**
+ * tm16xx_display_flush_init() - Workqueue to configure controller and set brightness
+ * @work: pointer to work_struct
+ */
+static void tm16xx_display_flush_init(struct work_struct *work)
+{
+ struct tm16xx_display *display = container_of(work,
+ struct tm16xx_display,
+ flush_init);
+ int ret;
+
+ if (display->controller->init) {
+ scoped_guard(mutex, &display->lock) {
+ ret = display->controller->init(display);
+ display->flush_status = ret;
+ }
+ if (ret)
+ dev_err(display->dev,
+ "Failed to configure controller: %d\n", ret);
+ }
+}
+
+/**
+ * tm16xx_display_flush_data() - Workqueue to update display data to controller
+ * @work: pointer to work_struct
+ */
+static void tm16xx_display_flush_data(struct work_struct *work)
+{
+ struct tm16xx_display *display = container_of(work,
+ struct tm16xx_display,
+ flush_display);
+ unsigned int grid, i;
+ int ret = 0;
+
+ scoped_guard(mutex, &display->lock) {
+ if (display->controller->data) {
+ for (i = 0; i < display->num_hwgrid; i++) {
+ grid = tm16xx_get_grid(display, i);
+ ret = display->controller->data(display, i, grid);
+ if (ret) {
+ dev_err(display->dev,
+ "Failed to write display data: %d\n",
+ ret);
+ break;
+ }
+ }
+ }
+
+ display->flush_status = ret;
+ }
+}
+
+/**
+ * tm16xx_brightness_set() - Set display main LED brightness
+ * @led_cdev: pointer to led_classdev
+ * @brightness: new brightness value
+ */
+static void tm16xx_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct tm16xx_display *display = dev_get_drvdata(led_cdev->dev->parent);
+
+ led_cdev->brightness = brightness;
+ schedule_work(&display->flush_init);
+}
+
+/**
+ * tm16xx_led_set() - Set state of an individual LED icon
+ * @led_cdev: pointer to led_classdev
+ * @value: new brightness (0/1)
+ */
+static void tm16xx_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct tm16xx_led *led = container_of(led_cdev, struct tm16xx_led, cdev);
+ struct tm16xx_display *display = dev_get_drvdata(led_cdev->dev->parent);
+
+ tm16xx_set_seg(display, led->hwgrid, led->hwseg, value);
+ schedule_work(&display->flush_display);
+}
+
+static int tm16xx_display_value(struct tm16xx_display *display, const char *buf, size_t count)
+{
+ struct linedisp *linedisp = &display->linedisp;
+ struct linedisp_map *map = linedisp->map;
+ struct tm16xx_digit *digit;
+ unsigned int i, j;
+ int seg_pattern;
+ bool val;
+
+ for (i = 0; i < display->num_digits && i < count; i++) {
+ digit = &display->digits[i];
+ seg_pattern = map_to_seg7(&map->map.seg7, buf[i]);
+
+ for (j = 0; j < TM16XX_DIGIT_SEGMENTS; j++) {
+ val = seg_pattern & BIT(j);
+ tm16xx_set_seg(display, digit->hwgrids[j], digit->hwsegs[j], val);
+ }
+ }
+
+ for (; i < display->num_digits; i++) {
+ digit = &display->digits[i];
+ for (j = 0; j < TM16XX_DIGIT_SEGMENTS; j++)
+ tm16xx_set_seg(display, digit->hwgrids[j], digit->hwsegs[j], 0);
+ }
+
+ schedule_work(&display->flush_display);
+ return 0;
+}
+
+static int tm16xx_linedisp_get_map_type(struct linedisp *linedisp)
+{
+ return LINEDISP_MAP_SEG7;
+}
+
+static void tm16xx_linedisp_update(struct linedisp *linedisp)
+{
+ struct tm16xx_display *display = linedisp_to_tm16xx(linedisp);
+
+ tm16xx_display_value(display, linedisp->buf, linedisp->num_chars);
+}
+
+static const struct linedisp_ops tm16xx_linedisp_ops = {
+ .get_map_type = tm16xx_linedisp_get_map_type,
+ .update = tm16xx_linedisp_update,
+};
+
+static int tm16xx_display_init(struct tm16xx_display *display)
+{
+ schedule_work(&display->flush_init);
+ flush_work(&display->flush_init);
+ if (display->flush_status)
+ return display->flush_status;
+
+ return 0;
+}
+
+static int tm16xx_parse_fwnode(struct device *dev, struct tm16xx_display *display)
+{
+ struct tm16xx_led *led;
+ struct tm16xx_digit *digit;
+ unsigned int max_hwgrid = 0, max_hwseg = 0;
+ unsigned int i, j;
+ int ret;
+ u32 segments[TM16XX_DIGIT_SEGMENTS * 2];
+ u32 reg[2];
+
+ struct fwnode_handle *digits_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "digits");
+ struct fwnode_handle *leds_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "leds");
+
+ /* parse digits */
+ if (digits_node) {
+ display->num_digits = fwnode_get_child_node_count(digits_node);
+
+ if (display->num_digits) {
+ display->digits = devm_kcalloc(dev, display->num_digits,
+ sizeof(*display->digits),
+ GFP_KERNEL);
+ if (!display->digits)
+ return -ENOMEM;
+
+ i = 0;
+ fwnode_for_each_available_child_node_scoped(digits_node, child) {
+ digit = &display->digits[i];
+
+ ret = fwnode_property_read_u32(child, "reg", reg);
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_u32_array(child,
+ "segments", segments,
+ TM16XX_DIGIT_SEGMENTS * 2);
+ if (ret < 0)
+ return ret;
+
+ for (j = 0; j < TM16XX_DIGIT_SEGMENTS; ++j) {
+ digit->hwgrids[j] = segments[2 * j];
+ digit->hwsegs[j] = segments[2 * j + 1];
+ max_hwgrid = umax(max_hwgrid, digit->hwgrids[j]);
+ max_hwseg = umax(max_hwseg, digit->hwsegs[j]);
+ }
+ i++;
+ }
+ }
+ }
+
+ /* parse leds */
+ if (leds_node) {
+ display->num_leds = fwnode_get_child_node_count(leds_node);
+
+ if (display->num_leds) {
+ display->leds = devm_kcalloc(dev, display->num_leds,
+ sizeof(*display->leds),
+ GFP_KERNEL);
+ if (!display->leds)
+ return -ENOMEM;
+
+ i = 0;
+ fwnode_for_each_available_child_node_scoped(leds_node, child) {
+ led = &display->leds[i];
+ ret = fwnode_property_read_u32_array(child, "reg", reg, 2);
+ if (ret < 0)
+ return ret;
+
+ led->hwgrid = reg[0];
+ led->hwseg = reg[1];
+ max_hwgrid = umax(max_hwgrid, led->hwgrid);
+ max_hwseg = umax(max_hwseg, led->hwseg);
+ i++;
+ }
+ }
+ }
+
+ if (max_hwgrid >= display->controller->max_grids) {
+ dev_err(dev, "grid %u exceeds controller max_grids %u\n",
+ max_hwgrid, display->controller->max_grids);
+ return -EINVAL;
+ }
+
+ if (max_hwseg >= display->controller->max_segments) {
+ dev_err(dev, "segment %u exceeds controller max_segments %u\n",
+ max_hwseg, display->controller->max_segments);
+ return -EINVAL;
+ }
+
+ display->num_hwgrid = max_hwgrid + 1;
+ display->num_hwseg = max_hwseg + 1;
+
+ return 0;
+}
+
+/**
+ * tm16xx_probe() - Probe and initialize display device, register LEDs
+ * @display: pointer to tm16xx_display
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int tm16xx_probe(struct tm16xx_display *display)
+{
+ struct device *dev = display->dev;
+ struct led_classdev *main = &display->main_led;
+ struct led_init_data led_init = {0};
+ struct fwnode_handle *leds_node;
+ struct tm16xx_led *led;
+ unsigned int nbits, i;
+ int ret;
+
+ ret = tm16xx_parse_fwnode(dev, display);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to parse device tree\n");
+
+ nbits = tm16xx_led_nbits(display);
+ display->state = devm_bitmap_zalloc(dev, nbits, GFP_KERNEL);
+ if (!display->state)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(display->dev, &display->lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize mutex\n");
+
+ INIT_WORK(&display->flush_init, tm16xx_display_flush_init);
+ INIT_WORK(&display->flush_display, tm16xx_display_flush_data);
+
+ /* Initialize main LED properties */
+ led_init.fwnode = dev_fwnode(dev); /* apply label property */
+ main->max_brightness = display->controller->max_brightness;
+ device_property_read_u32(dev, "max-brightness", &main->max_brightness);
+ main->max_brightness = umin(main->max_brightness,
+ display->controller->max_brightness);
+
+ main->brightness = main->max_brightness;
+ device_property_read_u32(dev, "default-brightness", &main->brightness);
+ main->brightness = umin(main->brightness, main->max_brightness);
+
+ main->brightness_set = tm16xx_brightness_set;
+ main->flags = LED_RETAIN_AT_SHUTDOWN | LED_CORE_SUSPENDRESUME;
+
+ /* Register individual LEDs from device tree */
+ ret = led_classdev_register_ext(dev, main, &led_init);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register main LED\n");
+
+ i = 0;
+ led_init.devicename = dev_name(main->dev);
+ led_init.devname_mandatory = true;
+ led_init.default_label = "led";
+ leds_node = device_get_named_child_node(dev, "leds");
+ fwnode_for_each_available_child_node_scoped(leds_node, child) {
+ led_init.fwnode = child;
+ led = &display->leds[i];
+ led->cdev.max_brightness = 1;
+ led->cdev.brightness_set = tm16xx_led_set;
+ led->cdev.flags = LED_RETAIN_AT_SHUTDOWN | LED_CORE_SUSPENDRESUME;
+
+ ret = led_classdev_register_ext(dev, &led->cdev, &led_init);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to register LED %s\n",
+ led->cdev.name);
+ goto unregister_leds;
+ }
+
+ i++;
+ }
+
+ ret = tm16xx_display_init(display);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to initialize display\n");
+ goto unregister_leds;
+ }
+
+ ret = linedisp_attach(&display->linedisp, display->main_led.dev,
+ display->num_digits, &tm16xx_linedisp_ops);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to initialize line-display\n");
+ goto unregister_leds;
+ }
+
+ return 0;
+
+unregister_leds:
+ while (i--)
+ led_classdev_unregister(&display->leds[i].cdev);
+
+ led_classdev_unregister(main);
+ return ret;
+}
+EXPORT_SYMBOL_NS(tm16xx_probe, "TM16XX");
+
+/**
+ * tm16xx_remove() - Remove display, unregister LEDs, blank output
+ * @display: pointer to tm16xx_display
+ */
+void tm16xx_remove(struct tm16xx_display *display)
+{
+ unsigned int nbits = tm16xx_led_nbits(display);
+ struct tm16xx_led *led;
+
+ linedisp_detach(display->main_led.dev);
+
+ /*
+ * Unregister LEDs first to immediately stop trigger activity.
+ * This prevents LED triggers from attempting to access hardware
+ * after it's been disconnected or driver unloaded.
+ */
+ for (int i = 0; i < display->num_leds; i++) {
+ led = &display->leds[i];
+ led_classdev_unregister(&led->cdev);
+ }
+ led_classdev_unregister(&display->main_led);
+
+ /* Clear display state */
+ bitmap_zero(display->state, nbits);
+ schedule_work(&display->flush_display);
+ flush_work(&display->flush_display);
+
+ /* Turn off display */
+ display->main_led.brightness = LED_OFF;
+ schedule_work(&display->flush_init);
+ flush_work(&display->flush_init);
+}
+EXPORT_SYMBOL_NS(tm16xx_remove, "TM16XX");
+
+MODULE_AUTHOR("Jean-François Lessard");
+MODULE_DESCRIPTION("TM16xx LED Display Controllers");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("LINEDISP");
--
2.34.1

View File

@@ -0,0 +1,357 @@
From d485af66b4fb4e583aaaaf35a69cdead16ee090c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Fri, 29 Aug 2025 22:28:35 -0400
Subject: [PATCH 49/54] TEST: auxdisplay: TM16xx: Add keypad support for
scanning matrix keys
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for keypad scanning on TM16xx-compatible auxiliary display
controllers. It handles keypad initialization, scanning, and input
reporting for matrix keys managed by the TM16xx devices.
Key features include:
- Input device registration configured by device properties
(poll-interval, linux,keymap, autorepeat)
- Key state tracking using managed bitmaps
- Matrix scanning and event reporting integrated with Linux input
subsystem
This code is separated from main core driver to improve maintainability
and reviewability.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
MAINTAINERS | 1 +
drivers/auxdisplay/Kconfig | 9 ++
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/tm16xx.h | 25 ++++
drivers/auxdisplay/tm16xx_core.c | 4 +
drivers/auxdisplay/tm16xx_keypad.c | 196 +++++++++++++++++++++++++++++
6 files changed, 236 insertions(+)
create mode 100644 drivers/auxdisplay/tm16xx_keypad.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e936dbe2ab0b..a5b66317a805 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25050,6 +25050,7 @@ F: Documentation/ABI/testing/sysfs-class-leds-tm16xx
F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
F: drivers/auxdisplay/tm16xx.h
F: drivers/auxdisplay/tm16xx_core.c
+F: drivers/auxdisplay/tm16xx_keypad.c
TMIO/SDHI MMC DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 7bacf11112b5..f9a2c0641c3c 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -528,13 +528,22 @@ config SEG_LED_GPIO
config TM16XX
tristate
+ depends on INPUT
+ select INPUT_MATRIXKMAP
select LEDS_CLASS
select LEDS_TRIGGERS
select LINEDISP
select NEW_LEDS
+ select TM16XX_KEYPAD if (INPUT)
help
Core TM16XX-compatible 7-segment LED controllers module
+config TM16XX_KEYPAD
+ bool
+ depends on TM16XX
+ help
+ Enable keyscan support for TM16XX driver.
+
#
# Character LCD with non-conforming interface section
#
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index 7ecf3cd4a0d3..a9b9c8ff05e8 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_PARPORT_PANEL) += panel.o
obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o
obj-$(CONFIG_TM16XX) += tm16xx.o
tm16xx-y += tm16xx_core.o
+tm16xx-$(CONFIG_TM16XX_KEYPAD) += tm16xx_keypad.o
diff --git a/drivers/auxdisplay/tm16xx.h b/drivers/auxdisplay/tm16xx.h
index 973b6ac19515..c503c6136807 100644
--- a/drivers/auxdisplay/tm16xx.h
+++ b/drivers/auxdisplay/tm16xx.h
@@ -103,6 +103,7 @@
struct tm16xx_display;
struct tm16xx_digit;
struct tm16xx_led;
+struct tm16xx_keypad;
/**
* DOC: struct tm16xx_controller - Controller-specific operations and limits
@@ -133,6 +134,7 @@ struct tm16xx_controller {
* @dev: Pointer to device struct.
* @controller: Controller-specific function table and limits.
* @linedisp: character line display structure
+ * @keypad: Opaque pointer to tm16xx_keypad struct.
* @spi_buffer: DMA-safe buffer for SPI transactions, or NULL for I2C.
* @num_hwgrid: Number of controller grids in use.
* @num_hwseg: Number of controller segments in use.
@@ -150,6 +152,7 @@ struct tm16xx_controller {
struct tm16xx_display {
struct device *dev;
const struct tm16xx_controller *controller;
+ struct tm16xx_keypad *keypad;
struct linedisp linedisp;
u8 *spi_buffer;
u8 num_hwgrid;
@@ -169,4 +172,26 @@ struct tm16xx_display {
int tm16xx_probe(struct tm16xx_display *display);
void tm16xx_remove(struct tm16xx_display *display);
+/* keypad support */
+#if IS_ENABLED(CONFIG_TM16XX_KEYPAD)
+int tm16xx_keypad_probe(struct tm16xx_display *display);
+void tm16xx_set_key(const struct tm16xx_display *display, const int row,
+ const int col, const bool pressed);
+#else
+static inline int tm16xx_keypad_probe(struct tm16xx_display *display)
+{
+ return 0;
+}
+
+static inline void tm16xx_set_key(const struct tm16xx_display *display,
+ const int row, const int col,
+ const bool pressed)
+{
+}
+#endif
+
+#define tm16xx_for_each_key(display, _r, _c) \
+ for (int _r = 0; _r < (display)->controller->max_key_rows; _r++) \
+ for (int _c = 0; _c < (display)->controller->max_key_cols; _c++)
+
#endif /* _TM16XX_H */
diff --git a/drivers/auxdisplay/tm16xx_core.c b/drivers/auxdisplay/tm16xx_core.c
index e090c578f8a0..1d474d980254 100644
--- a/drivers/auxdisplay/tm16xx_core.c
+++ b/drivers/auxdisplay/tm16xx_core.c
@@ -408,6 +408,10 @@ int tm16xx_probe(struct tm16xx_display *display)
goto unregister_leds;
}
+ ret = tm16xx_keypad_probe(display);
+ if (ret)
+ dev_warn(dev, "Failed to initialize keypad: %d\n", ret);
+
return 0;
unregister_leds:
diff --git a/drivers/auxdisplay/tm16xx_keypad.c b/drivers/auxdisplay/tm16xx_keypad.c
new file mode 100644
index 000000000000..daa6afaf749a
--- /dev/null
+++ b/drivers/auxdisplay/tm16xx_keypad.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TM16xx and compatible LED display/keypad controller driver
+ * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips.
+ *
+ * Copyright (C) 2025 Jean-François Lessard
+ */
+
+#include <linux/bitmap.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/property.h>
+
+#include "tm16xx.h"
+
+/**
+ * struct tm16xx_keypad - Keypad matrix state and input device
+ * @input: Input device for reporting key events.
+ * @state: Current bitmap of key states.
+ * @last_state: Previous bitmap of key states for change detection.
+ * @changes: Bitmap of key state changes since last poll.
+ * @row_shift: Row shift for keymap encoding.
+ */
+struct tm16xx_keypad {
+ struct input_dev *input;
+ unsigned long *state;
+ unsigned long *last_state;
+ unsigned long *changes;
+ int row_shift;
+};
+
+/**
+ * tm16xx_key_nbits() - Number of bits for the keypad state bitmap
+ * @display: pointer to tm16xx_display
+ *
+ * Return: total bits in keypad state bitmap (max_key_rows * max_key_cols)
+ */
+static inline unsigned int tm16xx_key_nbits(const struct tm16xx_display *display)
+{
+ return display->controller->max_key_rows *
+ display->controller->max_key_cols;
+}
+
+/**
+ * tm16xx_get_key_row() - Get row index from keypad bit index
+ * @display: pointer to tm16xx_display
+ * @bit: bit index in state bitmap
+ *
+ * Return: row index
+ */
+static inline int tm16xx_get_key_row(const struct tm16xx_display *display,
+ const unsigned int bit)
+{
+ return bit / display->controller->max_key_cols;
+}
+
+/**
+ * tm16xx_get_key_col() - Get column index from keypad bit index
+ * @display: pointer to tm16xx_display
+ * @bit: bit index in state bitmap
+ *
+ * Return: column index
+ */
+static inline int tm16xx_get_key_col(const struct tm16xx_display *display,
+ const unsigned int bit)
+{
+ return bit % display->controller->max_key_cols;
+}
+
+/**
+ * tm16xx_set_key() - Set the keypad state for a key
+ * @display: pointer to tm16xx_display
+ * @row: row index
+ * @col: column index
+ * @pressed: true if pressed, false otherwise
+ */
+void tm16xx_set_key(const struct tm16xx_display *display, const int row,
+ const int col, const bool pressed)
+{
+ __assign_bit(row * display->controller->max_key_cols + col,
+ display->keypad->state, pressed);
+}
+EXPORT_SYMBOL_NS(tm16xx_set_key, "TM16XX");
+
+/**
+ * tm16xx_keypad_poll() - Polls the keypad, reports events
+ * @input: pointer to input_dev
+ *
+ * Reads the matrix keypad state, compares with previous state, and
+ * reports key events to the input subsystem.
+ */
+static void tm16xx_keypad_poll(struct input_dev *input)
+{
+ struct tm16xx_display *display = input_get_drvdata(input);
+ struct tm16xx_keypad *keypad = display->keypad;
+ const unsigned short *keycodes = keypad->input->keycode;
+ unsigned int nbits = tm16xx_key_nbits(display);
+ unsigned int bit;
+ int row, col, scancode;
+ bool pressed;
+ int ret;
+
+ bitmap_zero(keypad->state, nbits);
+ bitmap_zero(keypad->changes, nbits);
+
+ scoped_guard(mutex, &display->lock) {
+ ret = display->controller->keys(display);
+ }
+
+ if (ret) {
+ dev_err(display->dev, "Reading failed: %d\n", ret);
+ return;
+ }
+
+ bitmap_xor(keypad->changes, keypad->state, keypad->last_state, nbits);
+
+ for_each_set_bit(bit, keypad->changes, nbits) {
+ row = tm16xx_get_key_row(display, bit);
+ col = tm16xx_get_key_col(display, bit);
+ pressed = _test_bit(bit, keypad->state);
+ scancode = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+
+ input_event(keypad->input, EV_MSC, MSC_SCAN, scancode);
+ input_report_key(keypad->input, keycodes[scancode], pressed);
+ }
+ input_sync(keypad->input);
+
+ bitmap_copy(keypad->last_state, keypad->state, nbits);
+}
+
+/**
+ * tm16xx_keypad_probe() - Initialize keypad/input device
+ * @display: pointer to tm16xx_display
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int tm16xx_keypad_probe(struct tm16xx_display *display)
+{
+ const unsigned int rows = display->controller->max_key_rows;
+ const unsigned int cols = display->controller->max_key_cols;
+ struct tm16xx_keypad *keypad;
+ struct input_dev *input;
+ unsigned int poll_interval, nbits;
+ int ret;
+
+ if (!display->controller->keys || !rows || !cols)
+ return 0; /* keypad not supported */
+
+ if (!device_property_present(display->dev, "poll-interval") ||
+ !device_property_present(display->dev, "linux,keymap"))
+ return 0; /* keypad disabled */
+
+ ret = device_property_read_u32(display->dev, "poll-interval", &poll_interval);
+ if (ret)
+ return dev_err_probe(display->dev, ret,
+ "Failed to read poll-interval\n");
+
+ keypad = devm_kzalloc(display->dev, sizeof(*keypad), GFP_KERNEL);
+ if (!keypad)
+ return -ENOMEM;
+ display->keypad = keypad;
+
+ nbits = tm16xx_key_nbits(display);
+ keypad->state = devm_bitmap_zalloc(display->dev, nbits, GFP_KERNEL);
+ keypad->last_state = devm_bitmap_zalloc(display->dev, nbits, GFP_KERNEL);
+ keypad->changes = devm_bitmap_zalloc(display->dev, nbits, GFP_KERNEL);
+ if (!keypad->state || !keypad->last_state || !keypad->changes)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(display->dev);
+ if (!input)
+ return -ENOMEM;
+ input->name = "tm16xx-keypad";
+ keypad->input = input;
+ input_set_drvdata(input, display);
+
+ keypad->row_shift = get_count_order(cols); /* !cols already checked */
+ ret = matrix_keypad_build_keymap(NULL, "linux,keymap", rows, cols, NULL, input);
+ if (ret)
+ return dev_err_probe(display->dev, ret,
+ "Failed to build keymap\n");
+
+ if (device_property_read_bool(display->dev, "autorepeat"))
+ __set_bit(EV_REP, input->evbit);
+
+ input_setup_polling(input, tm16xx_keypad_poll);
+ input_set_poll_interval(input, poll_interval);
+ ret = input_register_device(input);
+ if (ret)
+ return dev_err_probe(display->dev, ret,
+ "Failed to register input device\n");
+
+ return 0;
+}
--
2.34.1

View File

@@ -0,0 +1,419 @@
From 1dbdfd4092b51f693c7055c8fea169369db96f8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 23 Aug 2025 21:26:59 -0400
Subject: [PATCH 50/54] TEST: auxdisplay: TM16xx: Add support for I2C-based
controllers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for TM16xx-compatible auxiliary display controllers connected
via the I2C bus.
The implementation includes:
- I2C driver registration and initialization
- Probe/remove logic for I2C devices
- Controller-specific handling and communication sequences
- Integration with the TM16xx core driver for common functionality
This allows platforms using TM16xx or compatible controllers over I2C to be
managed by the TM16xx driver infrastructure.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
MAINTAINERS | 1 +
drivers/auxdisplay/Kconfig | 16 ++
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/tm16xx_i2c.c | 332 ++++++++++++++++++++++++++++++++
4 files changed, 350 insertions(+)
create mode 100644 drivers/auxdisplay/tm16xx_i2c.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a5b66317a805..f1354b5c9bd7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25050,6 +25050,7 @@ F: Documentation/ABI/testing/sysfs-class-leds-tm16xx
F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
F: drivers/auxdisplay/tm16xx.h
F: drivers/auxdisplay/tm16xx_core.c
+F: drivers/auxdisplay/tm16xx_i2c.c
F: drivers/auxdisplay/tm16xx_keypad.c
TMIO/SDHI MMC DRIVER
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index f9a2c0641c3c..d48c2f18950e 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -544,6 +544,22 @@ config TM16XX_KEYPAD
help
Enable keyscan support for TM16XX driver.
+config TM16XX_I2C
+ tristate "TM16XX-compatible I2C 7-segment LED controllers with keyscan"
+ depends on I2C
+ select TM16XX
+ help
+ This driver supports the following TM16XX compatible
+ I2C (2-wire) 7-segment led display chips:
+ - Titanmec: TM1650
+ - Fuda Hisi: FD650, FD655, FD6551
+ - i-Core: AiP650
+ - Winrise: HBS658
+
+ To compile this driver as a module, choose M here: the module
+ will be called tm16xx_i2c and you will also get tm16xx for the
+ core module.
+
#
# Character LCD with non-conforming interface section
#
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index a9b9c8ff05e8..ba7b310f5667 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o
obj-$(CONFIG_TM16XX) += tm16xx.o
tm16xx-y += tm16xx_core.o
tm16xx-$(CONFIG_TM16XX_KEYPAD) += tm16xx_keypad.o
+obj-$(CONFIG_TM16XX_I2C) += tm16xx_i2c.o
diff --git a/drivers/auxdisplay/tm16xx_i2c.c b/drivers/auxdisplay/tm16xx_i2c.c
new file mode 100644
index 000000000000..013becedac11
--- /dev/null
+++ b/drivers/auxdisplay/tm16xx_i2c.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TM16xx and compatible LED display/keypad controller driver
+ * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips.
+ *
+ * Copyright (C) 2025 Jean-François Lessard
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include "tm16xx.h"
+
+static int tm16xx_i2c_probe(struct i2c_client *client)
+{
+ const struct tm16xx_controller *controller;
+ struct tm16xx_display *display;
+ int ret;
+
+ controller = i2c_get_match_data(client);
+ if (!controller)
+ return -EINVAL;
+
+ display = devm_kzalloc(&client->dev, sizeof(*display), GFP_KERNEL);
+ if (!display)
+ return -ENOMEM;
+
+ display->dev = &client->dev;
+ display->controller = controller;
+
+ i2c_set_clientdata(client, display);
+
+ ret = tm16xx_probe(display);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void tm16xx_i2c_remove(struct i2c_client *client)
+{
+ struct tm16xx_display *display = i2c_get_clientdata(client);
+
+ tm16xx_remove(display);
+}
+
+/**
+ * tm16xx_i2c_write() - I2C write helper for controller
+ * @display: pointer to tm16xx_display structure
+ * @data: command and data bytes to send
+ * @len: number of bytes in @data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int tm16xx_i2c_write(struct tm16xx_display *display, u8 *data, size_t len)
+{
+ struct i2c_client *i2c = to_i2c_client(display->dev);
+
+ /* expected sequence: S Command [A] Data [A] P */
+ struct i2c_msg msg = {
+ .addr = data[0] >> 1,
+ .flags = 0,
+ .len = len - 1,
+ .buf = &data[1],
+ };
+ int ret;
+
+ ret = i2c_transfer(i2c->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+
+ return (ret == 1) ? 0 : -EIO;
+}
+
+/**
+ * tm16xx_i2c_read() - I2C read helper for controller
+ * @display: pointer to tm16xx_display structure
+ * @cmd: command/address byte to send before reading
+ * @data: buffer to receive data
+ * @len: number of bytes to read into @data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int tm16xx_i2c_read(struct tm16xx_display *display, u8 cmd, u8 *data, size_t len)
+{
+ struct i2c_client *i2c = to_i2c_client(display->dev);
+
+ /* expected sequence: S Command [A] [Data] [A] P */
+ struct i2c_msg msgs[1] = {{
+ .addr = cmd >> 1,
+ .flags = I2C_M_RD | I2C_M_NO_RD_ACK,
+ .len = len,
+ .buf = data,
+ }};
+ int ret;
+
+ ret = i2c_transfer(i2c->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+
+ return (ret == ARRAY_SIZE(msgs)) ? 0 : -EIO;
+}
+
+/* I2C controller-specific functions */
+static int tm1650_init(struct tm16xx_display *display)
+{
+ const enum led_brightness brightness = display->main_led.brightness;
+ u8 cmds[2];
+
+ cmds[0] = TM1650_CMD_CTRL;
+ cmds[1] = TM16XX_CTRL_BRIGHTNESS(brightness, brightness, TM1650) |
+ TM1650_CTRL_SEG8_MODE;
+
+ return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds));
+}
+
+static int tm1650_data(struct tm16xx_display *display, u8 index,
+ unsigned int grid)
+{
+ u8 cmds[2];
+
+ cmds[0] = TM1650_CMD_ADDR + index * 2;
+ cmds[1] = grid; /* SEG 1 to 8 */
+
+ return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds));
+}
+
+static int tm1650_keys(struct tm16xx_display *display)
+{
+ int row, col;
+ bool pressed;
+ u8 keycode;
+ int ret;
+
+ ret = tm16xx_i2c_read(display, TM1650_CMD_READ, &keycode, 1);
+ if (ret)
+ return ret;
+
+ if (keycode == 0x00 || keycode == 0xFF)
+ return -EINVAL;
+
+ row = FIELD_GET(TM1650_KEY_ROW_MASK, keycode);
+ pressed = FIELD_GET(TM1650_KEY_DOWN_MASK, keycode) != 0;
+ if ((keycode & TM1650_KEY_COMBINED) == TM1650_KEY_COMBINED) {
+ tm16xx_set_key(display, row, 0, pressed);
+ tm16xx_set_key(display, row, 1, pressed);
+ } else {
+ col = FIELD_GET(TM1650_KEY_COL_MASK, keycode);
+ tm16xx_set_key(display, row, col, pressed);
+ }
+
+ return 0;
+}
+
+static int fd655_init(struct tm16xx_display *display)
+{
+ const enum led_brightness brightness = display->main_led.brightness;
+ u8 cmds[2];
+
+ cmds[0] = FD655_CMD_CTRL;
+ cmds[1] = TM16XX_CTRL_BRIGHTNESS(brightness, brightness % 3, FD655);
+
+ return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds));
+}
+
+static int fd655_data(struct tm16xx_display *display, u8 index,
+ unsigned int grid)
+{
+ u8 cmds[2];
+
+ cmds[0] = FD655_CMD_ADDR + index * 2;
+ cmds[1] = grid; /* SEG 1 to 8 */
+
+ return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds));
+}
+
+static int fd6551_init(struct tm16xx_display *display)
+{
+ const enum led_brightness brightness = display->main_led.brightness;
+ u8 cmds[2];
+
+ cmds[0] = FD6551_CMD_CTRL;
+ cmds[1] = TM16XX_CTRL_BRIGHTNESS(brightness, ~(brightness - 1), FD6551);
+
+ return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds));
+}
+
+static void hbs658_swap_nibbles(u8 *data, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ data[i] = (data[i] << 4) | (data[i] >> 4);
+}
+
+static int hbs658_init(struct tm16xx_display *display)
+{
+ const enum led_brightness brightness = display->main_led.brightness;
+ u8 cmd;
+ int ret;
+
+ /* Set data command */
+ cmd = TM16XX_CMD_WRITE | TM16XX_DATA_ADDR_AUTO;
+ hbs658_swap_nibbles(&cmd, 1);
+ ret = tm16xx_i2c_write(display, &cmd, 1);
+ if (ret)
+ return ret;
+
+ /* Set control command with brightness */
+ cmd = TM16XX_CMD_CTRL |
+ TM16XX_CTRL_BRIGHTNESS(brightness, brightness - 1, TM16XX);
+ hbs658_swap_nibbles(&cmd, 1);
+ ret = tm16xx_i2c_write(display, &cmd, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hbs658_data(struct tm16xx_display *display, u8 index,
+ unsigned int grid)
+{
+ u8 cmds[2];
+
+ cmds[0] = TM16XX_CMD_ADDR + index * 2;
+ cmds[1] = grid;
+
+ hbs658_swap_nibbles(cmds, ARRAY_SIZE(cmds));
+ return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds));
+}
+
+static int hbs658_keys(struct tm16xx_display *display)
+{
+ u8 cmd, keycode;
+ int col;
+ int ret;
+
+ cmd = TM16XX_CMD_READ;
+ hbs658_swap_nibbles(&cmd, 1);
+ ret = tm16xx_i2c_read(display, cmd, &keycode, 1);
+ if (ret)
+ return ret;
+
+ hbs658_swap_nibbles(&keycode, 1);
+
+ if (keycode != 0xFF) {
+ col = FIELD_GET(HBS658_KEY_COL_MASK, keycode);
+ tm16xx_set_key(display, 0, col, true);
+ }
+
+ return 0;
+}
+
+/* I2C controller definitions */
+static const struct tm16xx_controller tm1650_controller = {
+ .max_grids = 4,
+ .max_segments = 8,
+ .max_brightness = 8,
+ .max_key_rows = 4,
+ .max_key_cols = 7,
+ .init = tm1650_init,
+ .data = tm1650_data,
+ .keys = tm1650_keys,
+};
+
+static const struct tm16xx_controller fd655_controller = {
+ .max_grids = 5,
+ .max_segments = 7,
+ .max_brightness = 3,
+ .max_key_rows = 5,
+ .max_key_cols = 7,
+ .init = fd655_init,
+ .data = fd655_data,
+ .keys = tm1650_keys,
+};
+
+static const struct tm16xx_controller fd6551_controller = {
+ .max_grids = 5,
+ .max_segments = 7,
+ .max_brightness = 8,
+ .max_key_rows = 0,
+ .max_key_cols = 0,
+ .init = fd6551_init,
+ .data = fd655_data,
+};
+
+static const struct tm16xx_controller hbs658_controller = {
+ .max_grids = 5,
+ .max_segments = 8,
+ .max_brightness = 8,
+ .max_key_rows = 1,
+ .max_key_cols = 8,
+ .init = hbs658_init,
+ .data = hbs658_data,
+ .keys = hbs658_keys,
+};
+
+static const struct of_device_id tm16xx_i2c_of_match[] = {
+ { .compatible = "titanmec,tm1650", .data = &tm1650_controller },
+ { .compatible = "fdhisi,fd6551", .data = &fd6551_controller },
+ { .compatible = "fdhisi,fd655", .data = &fd655_controller },
+ { .compatible = "winrise,hbs658", .data = &hbs658_controller },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tm16xx_i2c_of_match);
+
+static const struct i2c_device_id tm16xx_i2c_id[] = {
+ { "tm1650", (kernel_ulong_t)&tm1650_controller },
+ { "fd6551", (kernel_ulong_t)&fd6551_controller },
+ { "fd655", (kernel_ulong_t)&fd655_controller },
+ { "hbs658", (kernel_ulong_t)&hbs658_controller },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, tm16xx_i2c_id);
+
+static struct i2c_driver tm16xx_i2c_driver = {
+ .driver = {
+ .name = "tm16xx-i2c",
+ .of_match_table = tm16xx_i2c_of_match,
+ },
+ .probe = tm16xx_i2c_probe,
+ .remove = tm16xx_i2c_remove,
+ .shutdown = tm16xx_i2c_remove,
+ .id_table = tm16xx_i2c_id,
+};
+module_i2c_driver(tm16xx_i2c_driver);
+
+MODULE_AUTHOR("Jean-François Lessard");
+MODULE_DESCRIPTION("TM16xx-i2c LED Display Controllers");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("TM16XX");
--
2.34.1

View File

@@ -0,0 +1,484 @@
From f9f715779d52d8ae186670e89c140de64d3cc739 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
Date: Sat, 23 Aug 2025 21:55:14 -0400
Subject: [PATCH 51/54] TEST: auxdisplay: TM16xx: Add support for SPI-based
controllers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for TM16xx-compatible auxiliary display controllers connected
via the SPI bus.
The implementation includes:
- SPI driver registration and initialization
- Probe/remove logic for SPI devices
- Controller-specific handling and communication sequences
- Integration with the TM16xx core driver for common functionality
This allows platforms using TM16xx or compatible controllers over SPI to be
managed by the TM16xx driver infrastructure.
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
---
MAINTAINERS | 1 +
drivers/auxdisplay/Kconfig | 16 ++
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/tm16xx_spi.c | 397 ++++++++++++++++++++++++++++++++
4 files changed, 415 insertions(+)
create mode 100644 drivers/auxdisplay/tm16xx_spi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index f1354b5c9bd7..10fe2e0d8ce6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25052,6 +25052,7 @@ F: drivers/auxdisplay/tm16xx.h
F: drivers/auxdisplay/tm16xx_core.c
F: drivers/auxdisplay/tm16xx_i2c.c
F: drivers/auxdisplay/tm16xx_keypad.c
+F: drivers/auxdisplay/tm16xx_spi.c
TMIO/SDHI MMC DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index d48c2f18950e..61e5af8d0a3d 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -560,6 +560,22 @@ config TM16XX_I2C
will be called tm16xx_i2c and you will also get tm16xx for the
core module.
+config TM16XX_SPI
+ tristate "TM16XX-compatible SPI 7-segment LED controllers with keyscan"
+ depends on SPI
+ select TM16XX
+ help
+ This driver supports the following TM16XX compatible
+ SPI (3-wire) 7-segment led display chips:
+ - Titanmec: TM1618, TM1620, TM1628, TM1638
+ - Fuda Hisi: FD620, FD628
+ - i-Core: AiP1618, AiP1628
+ - Princeton: PT6964
+
+ To compile this driver as a module, choose M here: the module
+ will be called tm16xx_spi and you will also get tm16xx for the
+ core module.
+
#
# Character LCD with non-conforming interface section
#
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index ba7b310f5667..2485a3a6769d 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_TM16XX) += tm16xx.o
tm16xx-y += tm16xx_core.o
tm16xx-$(CONFIG_TM16XX_KEYPAD) += tm16xx_keypad.o
obj-$(CONFIG_TM16XX_I2C) += tm16xx_i2c.o
+obj-$(CONFIG_TM16XX_SPI) += tm16xx_spi.o
diff --git a/drivers/auxdisplay/tm16xx_spi.c b/drivers/auxdisplay/tm16xx_spi.c
new file mode 100644
index 000000000000..b305301f918c
--- /dev/null
+++ b/drivers/auxdisplay/tm16xx_spi.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TM16xx and compatible LED display/keypad controller driver
+ * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips.
+ *
+ * Copyright (C) 2025 Jean-François Lessard
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "tm16xx.h"
+
+#define TM16XX_SPI_BUFFER_SIZE 8
+#define TM16XX_SPI_TWAIT_US 2
+
+static int tm16xx_spi_probe(struct spi_device *spi)
+{
+ const struct tm16xx_controller *controller;
+ struct tm16xx_display *display;
+ int ret;
+
+ controller = spi_get_device_match_data(spi);
+ if (!controller)
+ return -EINVAL;
+
+ display = devm_kzalloc(&spi->dev, sizeof(*display), GFP_KERNEL);
+ if (!display)
+ return -ENOMEM;
+
+ /* Allocate DMA-safe buffer */
+ display->spi_buffer = devm_kzalloc(&spi->dev, TM16XX_SPI_BUFFER_SIZE, GFP_KERNEL);
+ if (!display->spi_buffer)
+ return -ENOMEM;
+
+ display->dev = &spi->dev;
+ display->controller = controller;
+
+ spi_set_drvdata(spi, display);
+
+ ret = tm16xx_probe(display);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void tm16xx_spi_remove(struct spi_device *spi)
+{
+ struct tm16xx_display *display = spi_get_drvdata(spi);
+
+ tm16xx_remove(display);
+}
+
+/**
+ * tm16xx_spi_read() - SPI read helper for controller
+ * @display: pointer to tm16xx_display
+ * @cmd: command to send
+ * @cmd_len: length of command
+ * @data: buffer for received data
+ * @data_len: length of data to read
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int tm16xx_spi_read(struct tm16xx_display *display, u8 *cmd,
+ size_t cmd_len, u8 *data, size_t data_len)
+{
+ struct spi_device *spi = to_spi_device(display->dev);
+ struct spi_message msg;
+ int ret;
+
+ /* If STB is high during transmission, command is invalid.
+ * Reading requires a minimum 2 microseconds wait (Twait)
+ * after the 8th CLK rising edge before reading on falling edge.
+ */
+ struct spi_transfer xfers[2] = {
+ {
+ .tx_buf = cmd,
+ .len = cmd_len,
+ .cs_change = 0, /* NO CS toggle */
+ .delay.value = TM16XX_SPI_TWAIT_US,
+ .delay.unit = SPI_DELAY_UNIT_USECS,
+ }, {
+ .rx_buf = data,
+ .len = data_len,
+ }
+ };
+
+ spi_message_init_with_transfers(&msg, xfers, ARRAY_SIZE(xfers));
+
+ ret = spi_sync(spi, &msg);
+
+ return ret;
+}
+
+/**
+ * tm16xx_spi_write() - SPI write helper for controller
+ * @display: pointer to tm16xx_display
+ * @data: data to write
+ * @len: number of bytes to write
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int tm16xx_spi_write(struct tm16xx_display *display, u8 *data, size_t len)
+{
+ struct spi_device *spi = to_spi_device(display->dev);
+
+ return spi_write(spi, data, len);
+}
+
+/* SPI controller-specific functions */
+static int tm1628_init(struct tm16xx_display *display)
+{
+ const enum led_brightness brightness = display->main_led.brightness;
+ const u8 num_hwgrid = display->num_hwgrid;
+ u8 *cmd = display->spi_buffer;
+ int ret;
+
+ /* Set mode command based on grid count */
+ cmd[0] = TM16XX_CMD_MODE;
+ if (num_hwgrid <= 4)
+ cmd[0] |= TM16XX_MODE_4GRIDS;
+ else if (num_hwgrid == 5)
+ cmd[0] |= TM16XX_MODE_5GRIDS;
+ else if (num_hwgrid == 6)
+ cmd[0] |= TM16XX_MODE_6GRIDS;
+ else
+ cmd[0] |= TM16XX_MODE_7GRIDS;
+
+ ret = tm16xx_spi_write(display, cmd, 1);
+ if (ret)
+ return ret;
+
+ /* Set data command */
+ cmd[0] = TM16XX_CMD_WRITE | TM16XX_DATA_ADDR_AUTO;
+ ret = tm16xx_spi_write(display, cmd, 1);
+ if (ret)
+ return ret;
+
+ /* Set control command with brightness */
+ cmd[0] = TM16XX_CMD_CTRL |
+ TM16XX_CTRL_BRIGHTNESS(brightness, brightness - 1, TM16XX);
+ ret = tm16xx_spi_write(display, cmd, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int tm1618_data(struct tm16xx_display *display, u8 index,
+ unsigned int grid)
+{
+ u8 *cmd = display->spi_buffer;
+
+ cmd[0] = TM16XX_CMD_ADDR + index * 2;
+ cmd[1] = FIELD_GET(TM1618_BYTE1_MASK, grid);
+ cmd[2] = FIELD_GET(TM1618_BYTE2_MASK, grid) << TM1618_BYTE2_SHIFT;
+
+ return tm16xx_spi_write(display, cmd, 3);
+}
+
+static int tm1628_data(struct tm16xx_display *display, u8 index,
+ unsigned int grid)
+{
+ u8 *cmd = display->spi_buffer;
+
+ cmd[0] = TM16XX_CMD_ADDR + index * 2;
+ cmd[1] = FIELD_GET(TM1628_BYTE1_MASK, grid);
+ cmd[2] = FIELD_GET(TM1628_BYTE2_MASK, grid);
+
+ return tm16xx_spi_write(display, cmd, 3);
+}
+
+static int tm1628_keys(struct tm16xx_display *display)
+{
+ u8 *cmd = display->spi_buffer;
+ u8 *codes = display->spi_buffer;
+ unsigned int i;
+ int bit, byte;
+ bool value;
+ int ret;
+
+ cmd[0] = TM16XX_CMD_READ;
+ ret = tm16xx_spi_read(display, cmd, 1, codes, TM1628_KEY_READ_LEN);
+ if (ret)
+ return ret;
+
+ /* prevent false readings */
+ for (i = 0; i < TM1628_KEY_READ_LEN; i++) {
+ if (codes[i] & ~TM1628_KEY_MASK)
+ return -EINVAL;
+ }
+
+ tm16xx_for_each_key(display, row, col) {
+ byte = col >> 1;
+ bit = row + ((col & 1) * 3);
+ value = !!(codes[byte] & BIT(bit));
+
+ tm16xx_set_key(display, row, col, value);
+ }
+
+ return 0;
+}
+
+static int tm1638_keys(struct tm16xx_display *display)
+{
+ u8 *cmd = display->spi_buffer;
+ u8 *codes = display->spi_buffer;
+ unsigned int i;
+ int bit, byte;
+ bool value;
+ int ret;
+
+ cmd[0] = TM16XX_CMD_READ;
+ ret = tm16xx_spi_read(display, cmd, 1, codes, TM1638_KEY_READ_LEN);
+ if (ret)
+ return ret;
+
+ /* prevent false readings */
+ for (i = 0; i < TM1638_KEY_READ_LEN; i++) {
+ if (codes[i] & ~TM1638_KEY_MASK)
+ return -EINVAL;
+ }
+
+ tm16xx_for_each_key(display, row, col) {
+ byte = col >> 1;
+ bit = (2 - row) + ((col & 1) << 2);
+ value = !!(codes[byte] & BIT(bit));
+
+ tm16xx_set_key(display, row, col, value);
+ }
+
+ return 0;
+}
+
+static int tm1618_keys(struct tm16xx_display *display)
+{
+ u8 *cmd = display->spi_buffer;
+ u8 *codes = display->spi_buffer;
+ unsigned int i;
+ int ret;
+
+ cmd[0] = TM16XX_CMD_READ;
+ ret = tm16xx_spi_read(display, cmd, 1, codes, TM1618_KEY_READ_LEN);
+ if (ret)
+ return ret;
+
+ /* prevent false readings */
+ for (i = 0; i < TM1618_KEY_READ_LEN; i++) {
+ if (codes[i] & ~TM1618_KEY_MASK)
+ return -EINVAL;
+ }
+
+ tm16xx_set_key(display, 0, 0, !!(codes[0] & BIT(1)));
+ tm16xx_set_key(display, 0, 1, !!(codes[0] & BIT(4)));
+ tm16xx_set_key(display, 0, 2, !!(codes[1] & BIT(1)));
+ tm16xx_set_key(display, 0, 3, !!(codes[1] & BIT(4)));
+ tm16xx_set_key(display, 0, 4, !!(codes[2] & BIT(1)));
+
+ return 0;
+}
+
+static int fd620_data(struct tm16xx_display *display, u8 index,
+ unsigned int grid)
+{
+ u8 *cmd = display->spi_buffer;
+
+ cmd[0] = TM16XX_CMD_ADDR + index * 2;
+ cmd[1] = FIELD_GET(FD620_BYTE1_MASK, grid);
+ cmd[2] = FIELD_GET(FD620_BYTE2_MASK, grid) << FD620_BYTE2_SHIFT;
+
+ return tm16xx_spi_write(display, cmd, 3);
+}
+
+static int fd620_keys(struct tm16xx_display *display)
+{
+ u8 *cmd = display->spi_buffer;
+ u8 *codes = display->spi_buffer;
+ unsigned int i;
+ int ret;
+
+ cmd[0] = TM16XX_CMD_READ;
+ ret = tm16xx_spi_read(display, cmd, 1, codes, FD620_KEY_READ_LEN);
+ if (ret)
+ return ret;
+
+ /* prevent false readings */
+ for (i = 0; i < FD620_KEY_READ_LEN; i++) {
+ if (codes[i] & ~FD620_KEY_MASK)
+ return -EINVAL;
+ }
+
+ tm16xx_set_key(display, 0, 0, codes[0] & BIT(0));
+ tm16xx_set_key(display, 0, 1, codes[0] & BIT(3));
+ tm16xx_set_key(display, 0, 2, codes[1] & BIT(0));
+ tm16xx_set_key(display, 0, 3, codes[1] & BIT(3));
+ tm16xx_set_key(display, 0, 4, codes[2] & BIT(0));
+ tm16xx_set_key(display, 0, 5, codes[2] & BIT(3));
+ tm16xx_set_key(display, 0, 6, codes[3] & BIT(0));
+
+ return 0;
+}
+
+/* SPI controller definitions */
+static const struct tm16xx_controller tm1618_controller = {
+ .max_grids = 7,
+ .max_segments = 8,
+ .max_brightness = 8,
+ .max_key_rows = 1,
+ .max_key_cols = 5,
+ .init = tm1628_init,
+ .data = tm1618_data,
+ .keys = tm1618_keys,
+};
+
+static const struct tm16xx_controller tm1620_controller = {
+ .max_grids = 6,
+ .max_segments = 10,
+ .max_brightness = 8,
+ .max_key_rows = 0,
+ .max_key_cols = 0,
+ .init = tm1628_init,
+ .data = tm1628_data,
+};
+
+static const struct tm16xx_controller tm1628_controller = {
+ .max_grids = 7,
+ .max_segments = 14, /* seg 11 unused */
+ .max_brightness = 8,
+ .max_key_rows = 2,
+ .max_key_cols = 10,
+ .init = tm1628_init,
+ .data = tm1628_data,
+ .keys = tm1628_keys,
+};
+
+static const struct tm16xx_controller tm1638_controller = {
+ .max_grids = 8,
+ .max_segments = 10,
+ .max_brightness = 8,
+ .max_key_rows = 3,
+ .max_key_cols = 8,
+ .init = tm1628_init,
+ .data = tm1628_data,
+ .keys = tm1638_keys,
+};
+
+static const struct tm16xx_controller fd620_controller = {
+ .max_grids = 5,
+ .max_segments = 8,
+ .max_brightness = 8,
+ .max_key_rows = 1,
+ .max_key_cols = 7,
+ .init = tm1628_init,
+ .data = fd620_data,
+ .keys = fd620_keys,
+};
+
+static const struct of_device_id tm16xx_spi_of_match[] = {
+ { .compatible = "titanmec,tm1618", .data = &tm1618_controller },
+ { .compatible = "titanmec,tm1620", .data = &tm1620_controller },
+ { .compatible = "titanmec,tm1628", .data = &tm1628_controller },
+ { .compatible = "titanmec,tm1638", .data = &tm1638_controller },
+ { .compatible = "fdhisi,fd620", .data = &fd620_controller },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tm16xx_spi_of_match);
+
+static const struct spi_device_id tm16xx_spi_id[] = {
+ { "tm1618", (kernel_ulong_t)&tm1618_controller },
+ { "tm1620", (kernel_ulong_t)&tm1620_controller },
+ { "tm1628", (kernel_ulong_t)&tm1628_controller },
+ { "tm1638", (kernel_ulong_t)&tm1638_controller },
+ { "fd620", (kernel_ulong_t)&fd620_controller },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, tm16xx_spi_id);
+
+static struct spi_driver tm16xx_spi_driver = {
+ .driver = {
+ .name = "tm16xx-spi",
+ .of_match_table = tm16xx_spi_of_match,
+ },
+ .probe = tm16xx_spi_probe,
+ .remove = tm16xx_spi_remove,
+ .shutdown = tm16xx_spi_remove,
+ .id_table = tm16xx_spi_id,
+};
+module_spi_driver(tm16xx_spi_driver);
+
+MODULE_AUTHOR("Jean-François Lessard");
+MODULE_DESCRIPTION("TM16xx-spi LED Display Controllers");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("TM16XX");
--
2.34.1

View File

@@ -0,0 +1,135 @@
From f660e55ee33d8e475ab949b8cf161f7deca08b60 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 11 Jun 2025 11:23:44 +0000
Subject: [PATCH 52/54] WIP: arm64: dts: meson-gxl-s905w-tx3-mini: support the
fd628 display
The TX3-mini has an FD628 display. Add support using the tm166xx
kernel driver and userspace tools [0].
[0] https://github.com/jefflessard/tm16xx-display
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../dts/amlogic/meson-gxl-s905w-tx3-mini.dts | 98 +++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts
index 6705c2082a78..fec1b3fa0467 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts
@@ -10,6 +10,8 @@
#include "meson-gxl-s905x.dtsi"
#include "meson-gx-p23x-q20x.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
/ {
compatible = "oranth,tx3-mini", "amlogic,s905w", "amlogic,meson-gxl";
@@ -19,6 +21,102 @@ memory@0 {
device_type = "memory";
reg = <0x0 0x0 0x0 0x40000000>; /* 1 GiB or 2 GiB */
};
+
+ display_client: spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "spi-gpio";
+ sck-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio 75 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio_ao 4 GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+
+ display@0 {
+ compatible = "fdhisi,fd628", "titanmec,tm1628";
+ reg = <0>;
+ spi-3wire;
+ spi-lsb-first;
+ spi-max-frequency = <500000>;
+
+ digits {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ digit@0 {
+ reg = <0>;
+ segments = <3 3>,
+ <3 4>,
+ <3 5>,
+ <3 0>,
+ <3 1>,
+ <3 2>,
+ <3 6>;
+ };
+ digit@1 {
+ reg = <1>;
+ segments = <2 3>,
+ <2 4>,
+ <2 5>,
+ <2 0>,
+ <2 1>,
+ <2 2>,
+ <2 6>;
+ };
+ digit@2 {
+ reg = <2>;
+ segments = <1 3>,
+ <1 4>,
+ <1 5>,
+ <1 0>,
+ <1 1>,
+ <1 2>,
+ <1 6>;
+ };
+ digit@3 {
+ reg = <3>;
+ segments = <0 3>,
+ <0 4>,
+ <0 5>,
+ <0 0>,
+ <0 1>,
+ <0 2>,
+ <0 6>;
+ };
+ };
+
+ leds {
+ #address-cells = <2>;
+ #size-cells = <0>;
+ led@4,0 {
+ reg = <4 0>;
+ function = LED_FUNCTION_ALARM;
+ };
+ led@4,1 {
+ reg = <4 1>;
+ function = LED_FUNCTION_USB;
+ };
+ led@4,3 {
+ reg = <4 3>;
+ function = "pause";
+ };
+ led@4,2 {
+ reg = <4 2>;
+ function = "play";
+ };
+ led@4,4 {
+ reg = <4 4>;
+ function = "colon";
+ };
+ led@4,5 {
+ reg = <4 5>;
+ function = LED_FUNCTION_LAN;
+ };
+ led@4,6 {
+ reg = <4 6>;
+ function = LED_FUNCTION_WLAN;
+ };
+ };
+ };
+ };
};
&ir {
--
2.34.1

View File

@@ -0,0 +1,134 @@
From 43fdd7d9ddd536d158909fece282aee5a85c4867 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Thu, 9 Feb 2023 10:11:39 +0000
Subject: [PATCH 53/54] WIP: arm64: dts: meson-gxm-tx9-pro: support the fd628
display
The TX9-Pro has an FD628 display. Add support using the tm166xx
kernel driver and userspace tools [0].
[0] https://github.com/jefflessard/tm16xx-display
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../boot/dts/amlogic/meson-gxm-tx9-pro.dts | 97 +++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts
index 9a62176cfe5a..cf16995ebf5e 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-tx9-pro.dts
@@ -9,6 +9,7 @@
#include "meson-gxm.dtsi"
#include "meson-gx-p23x-q20x.dtsi"
#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
/ {
compatible = "oranth,tx9-pro", "amlogic,s912", "amlogic,meson-gxm";
@@ -37,6 +38,102 @@ button {
gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_LOW>;
};
};
+
+ display_client: spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "spi-gpio";
+ sck-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio 75 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio 53 GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+
+ display@0 {
+ compatible = "fdhisi,fd628", "titanmec,tm1628";
+ reg = <0>;
+ spi-3wire;
+ spi-lsb-first;
+ spi-max-frequency = <500000>;
+
+ digits {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ digit@0 {
+ reg = <0>;
+ segments = <0 3>,
+ <0 1>,
+ <0 2>,
+ <0 6>,
+ <0 4>,
+ <0 5>,
+ <0 0>;
+ };
+ digit@1 {
+ reg = <1>;
+ segments = <1 3>,
+ <1 1>,
+ <1 2>,
+ <1 6>,
+ <1 4>,
+ <1 5>,
+ <1 0>;
+ };
+ digit@2 {
+ reg = <2>;
+ segments = <2 3>,
+ <2 1>,
+ <2 2>,
+ <2 6>,
+ <2 4>,
+ <2 5>,
+ <2 0>;
+ };
+ digit@3 {
+ reg = <3>;
+ segments = <3 3>,
+ <3 1>,
+ <3 2>,
+ <3 6>,
+ <3 4>,
+ <3 5>,
+ <3 0>;
+ };
+ };
+
+ leds {
+ #address-cells = <2>;
+ #size-cells = <0>;
+ led@4,0 {
+ reg = <4 0>;
+ function = LED_FUNCTION_ALARM;
+ };
+ led@4,1 {
+ reg = <4 1>;
+ function = LED_FUNCTION_USB;
+ };
+ led@4,2 {
+ reg = <4 2>;
+ function = "play";
+ };
+ led@4,3 {
+ reg = <4 3>;
+ function = "pause";
+ };
+ led@4,4 {
+ reg = <4 4>;
+ function = "colon";
+ };
+ led@4,5 {
+ reg = <4 5>;
+ function = LED_FUNCTION_LAN;
+ };
+ led@4,6 {
+ reg = <4 6>;
+ function = LED_FUNCTION_WLAN;
+ };
+ };
+ };
+ };
};
&ethmac {
--
2.34.1

View File

@@ -0,0 +1,138 @@
From 4264c7df80fd5603bf7789870dce5a94db6469c2 Mon Sep 17 00:00:00 2001
From: Christian Hewitt <christianshewitt@gmail.com>
Date: Wed, 11 Jun 2025 11:47:31 +0000
Subject: [PATCH 54/54] WIP: arm64: dts: meson-g12a-x96-max: support the fd628
display
The X96-Max has an FD628 display. Add support using the tm166xx
kernel driver and userspace tools [0].
[0] https://github.com/jefflessard/tm16xx-display
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
.../boot/dts/amlogic/meson-g12a-x96-max.dts | 101 ++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts b/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts
index 5ab460a3e637..a4712edc380f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-g12a-x96-max.dts
@@ -7,6 +7,7 @@
#include "meson-g12a.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/meson-g12a-gpio.h>
#include <dt-bindings/sound/meson-g12a-tohdmitx.h>
@@ -54,6 +55,106 @@ hdmi_connector_in: endpoint {
};
};
+ display_client: spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "spi-gpio";
+ sck-gpios = <&gpio 64 GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio 63 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio_ao 10 GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+
+ display@0 {
+ compatible = "fdhisi,fd628", "titanmec,tm1628";
+ reg = <0>;
+ spi-3wire;
+ spi-lsb-first;
+ spi-max-frequency = <500000>;
+
+ digits {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ digit@0 {
+ reg = <0>;
+ segments = <0 7>,
+ <1 7>,
+ <2 7>,
+ <3 7>,
+ <4 7>,
+ <5 7>,
+ <6 7>;
+ };
+
+ digit@1 {
+ reg = <1>;
+ segments = <0 6>,
+ <1 6>,
+ <2 6>,
+ <3 6>,
+ <4 6>,
+ <5 6>,
+ <6 6>;
+ };
+
+ digit@2 {
+ reg = <2>;
+ segments = <0 5>,
+ <1 5>,
+ <2 5>,
+ <3 5>,
+ <4 5>,
+ <5 5>,
+ <6 5>;
+ };
+
+ digit@3 {
+ reg = <3>;
+ segments = <0 4>,
+ <1 4>,
+ <2 4>,
+ <3 4>,
+ <4 4>,
+ <5 4>,
+ <6 4>;
+ };
+ };
+
+ leds {
+ #address-cells = <2>;
+ #size-cells = <0>;
+ led@0,3 {
+ reg = <0 3>;
+ function = "apps";
+ };
+ led@1,3 {
+ reg = <1 3>;
+ function = "setup";
+ };
+ led@2,3 {
+ reg = <2 3>;
+ function = LED_FUNCTION_USB;
+ };
+ led@3,3 {
+ reg = <3 3>;
+ function = LED_FUNCTION_SD;
+ };
+ led@4,3 {
+ reg = <4 3>;
+ function = "colon";
+ };
+ led@5,3 {
+ reg = <5 3>;
+ function = "hd";
+ };
+ led@6,3 {
+ reg = <6 3>;
+ function = "cvbs";
+ };
+ };
+ };
+ };
+
emmc_pwrseq: emmc-pwrseq {
compatible = "mmc-pwrseq-emmc";
reset-gpios = <&gpio BOOT_12 GPIO_ACTIVE_LOW>;
--
2.34.1

View File

@@ -1,10 +1,10 @@
#
# Automatically generated file; DO NOT EDIT.
# Linux/arm64 6.16.0-rc3 Kernel Configuration
# Linux/arm64 6.16.5 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="aarch64-libreelec-linux-gnu-gcc-15.1.0 (GCC) 15.1.0"
CONFIG_CC_VERSION_TEXT="aarch64-libreelec-linux-gnu-gcc-15.2.0 (GCC) 15.2.0"
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=150100
CONFIG_GCC_VERSION=150200
CONFIG_CLANG_VERSION=0
CONFIG_AS_IS_GNU=y
CONFIG_AS_VERSION=24400
@@ -233,7 +233,6 @@ CONFIG_ELF_CORE=y
# CONFIG_BASE_SMALL is not set
CONFIG_FUTEX=y
CONFIG_FUTEX_PI=y
CONFIG_FUTEX_PRIVATE_HASH=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
@@ -984,7 +983,6 @@ CONFIG_ARCH_HAS_PTE_DEVMAP=y
CONFIG_ARCH_HAS_ZONE_DMA_SET=y
CONFIG_ZONE_DMA=y
CONFIG_ZONE_DMA32=y
CONFIG_VMAP_PFN=y
CONFIG_VM_EVENT_COUNTERS=y
# CONFIG_PERCPU_STATS is not set
# CONFIG_GUP_TEST is not set
@@ -2743,7 +2741,7 @@ CONFIG_INPUT=y
CONFIG_INPUT_LEDS=y
CONFIG_INPUT_FF_MEMLESS=y
# CONFIG_INPUT_SPARSEKMAP is not set
# CONFIG_INPUT_MATRIXKMAP is not set
CONFIG_INPUT_MATRIXKMAP=y
#
# Userland interfaces
@@ -3040,6 +3038,7 @@ CONFIG_I2C_ALGOBIT=y
# CONFIG_I2C_AMD8111 is not set
# CONFIG_I2C_I801 is not set
# CONFIG_I2C_ISCH is not set
# CONFIG_I2C_PIIX4 is not set
# CONFIG_I2C_NFORCE2 is not set
# CONFIG_I2C_NVIDIA_GPU is not set
# CONFIG_I2C_SIS5595 is not set
@@ -3306,7 +3305,6 @@ CONFIG_POWER_RESET_GPIO_RESTART=y
# CONFIG_POWER_RESET_LTC2952 is not set
CONFIG_POWER_RESET_REGULATOR=y
CONFIG_POWER_RESET_RESTART=y
# CONFIG_POWER_RESET_TORADEX_EC is not set
# CONFIG_POWER_RESET_XGENE is not set
CONFIG_POWER_RESET_SYSCON=y
CONFIG_POWER_RESET_SYSCON_POWEROFF=y
@@ -4577,13 +4575,20 @@ CONFIG_VIDEO=y
CONFIG_AUXDISPLAY=y
# CONFIG_HD44780 is not set
# CONFIG_LCD2S is not set
CONFIG_PANEL_CHANGE_MESSAGE=y
CONFIG_PANEL_BOOT_MESSAGE=" HELLO"
# CONFIG_CHARLCD_BL_OFF is not set
# CONFIG_CHARLCD_BL_ON is not set
CONFIG_CHARLCD_BL_FLASH=y
CONFIG_LINEDISP=y
# CONFIG_IMG_ASCII_LCD is not set
# CONFIG_HT16K33 is not set
# CONFIG_MAX6959 is not set
# CONFIG_SEG_LED_GPIO is not set
CONFIG_TM16XX=y
CONFIG_TM16XX_KEYPAD=y
CONFIG_TM16XX_I2C=y
CONFIG_TM16XX_SPI=y
CONFIG_DRM=y
#

View File

@@ -67,10 +67,13 @@
ADDITIONAL_DRIVERS+=""
# additional packages to install:
ADDITIONAL_PACKAGES+=" dtc ethmactool emmctool flashrom pciutils tm16xx"
ADDITIONAL_PACKAGES+=" dtc ethmactool emmctool flashrom pciutils"
# use the kernel CEC framework for libcec (yes / no)
CEC_FRAMEWORK_SUPPORT="yes"
# debug tty path
DEBUG_TTY="/dev/ttyAML0"
# vfd pannel display support
VFD_SUPPORT="yes"