Skip to contentSkip to conversation

auxdisplay: Add TM16xx 7-segment LED matrix display controllers driver

PATCH Superseded Jean-François Lessard 5 participants 30 messages Aug 25, 2025 – Sep 1, 2025

Subsystems (3)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 cover auxdisplay: Add TM16xx 7-segment LED matrix display controllers driver

This series adds mainline kernel support for TM16xx family LED matrix controllers and compatible chips, widely used in auxiliary displays on TV boxes and embedded devices.

Many consumer devices, particularly TV boxes, use auxiliary displays based on TM16xx controllers to show status information such as time, network connectivity and system state. Currently, there is no mainline kernel support for these displays, forcing users to rely on out-of-tree drivers or userspace solutions that access hardware interfaces directly.

This driver provides unified TM16xx support through the LED subsystem with both I2C and SPI communication protocols. It integrates with the LED class framework, enabling control via standard sysfs interfaces and LED triggers, while supporting keypad input when hardware connections are available.

The driver supports multiple controller families from various vendors:
- Titan Micro Electronics: TM1618, TM1620, TM1628, TM1638, TM1650
- Fuda Hisi Microelectronics: FD620, FD628, FD650, FD655, FD6551
- i-Core Electronics: AiP650, AiP1618, AiP1628
- Princeton Technology: PT6964
- Winrise Technology: HBS658

Key features:
- 7-segment display support with flexible digit/segment mapping
- Individual LED icon control through LED class devices
- Optional keypad scanning with configurable key mapping
- Device tree configuration for board-specific wiring layouts
- LED trigger integration for automatic system event indication
- I2C and SPI protocol support depending on controller interface

Device tree bindings describe board-specific display wiring since controllers are layout-agnostic. The bindings use separate 'digits' and 'leds' containers with specific addressing schemes to accommodate the hardware's grid/segment matrix organization.

Note: This driver is placed in drivers/auxdisplay rather than drivers/leds based on previous maintainer guidance. LED maintainer Pavel Machek recommended auxdisplay for TM1628-based display drivers:
https://lore.kernel.org/linux-devicetree/[email protected]/

Tested on multiple ARM TV boxes (H96 Max, Magicsee N5, Tanix TX3 Mini, Tanix TX6, X92, X96 Max) across different SoC platforms (Rockchip, Amlogic, Allwinner) in both I2C and SPI configurations.

Dependencies:
- CONFIG_NEW_LEDS, CONFIG_LEDS_CLASS (required)
- CONFIG_INPUT, CONFIG_INPUT_MATRIXKMAP (for keypad support)
- CONFIG_I2C or CONFIG_SPI (depending on hardware interface)

Optional LED trigger modules for advanced functionality:
- CONFIG_LEDS_TRIGGER_TIMER for blinking elements.
- CONFIG_LEDS_TRIGGER_NETDEV for network activity indication.
- CONFIG_USB_LEDS_TRIGGER_USBPORT for USB activity indication.

User space utilities available at:
https://github.com/jefflessard/tm16xx-display

v4:
- Split MAINTAINERS patch into each specific patch
- Document ABI of sysfs driver attributes
- Remove kernel-doc of obvious Linux core driver model APIs
- dt-bindings: Drop obvious comments that schema tells by itself
- dt-bindings: Gather canonical compatible strings in a single enum
- dt-bindings: Clarify top-level logical led DT node name/label property
- dt:bindings: Replace refs to input properties with allOf
- Split driver patch and code file for better reviewability
- Refactor into separate i2c and spi glue driver modules
- Drop driver name macro constant in favor of explicit string literals
- Revise to use bit shifts for values and GENMASK/BIT for bit positions
- Format TM16XX_CTRL_BRIGHTNESS on one line
- Drop default_value module param in favor of Kconfig compile time option
- Fix for_each_key name and expressions
- Replace manual mutex locking with scoped_guard
- Move scancode declaration to avoid mix with code
- Remove unnecessary ret initialization
- Remove ENOMEM error message
- Replace probe error messages by dev_err_probe
- Remove keypad failed probe cleanup to avoid devm anti-pattern confusion
- Switch to non-devm led registration to avoid anti-pattern confusion
- Replace u16 in favor of unsigned int for controller data

v3:
- Update vendor prefixes with documented rationale, in a single patch, per maintainer feedback
- Refine device tree bindings per maintainer feedback:
* Update compatible string ordering and fallback logic
* Improve YAML descriptions for clarity and 80-column wrapping
* Replace digit-specific properties with clearer digits container node
* Add required constraints for properties in container nodes
* Clarify addressing schemes for LED icons and digits
* Fix conditional SPI properties handling
* Document rationale for spi-3wire property
* Expand DT examples to cover typical and transposed display layouts
- Code reformat from clang-format to kernel style per maintainer feedback
- Fix conditional CONFIG_I2C/_SPI compilation issues per kernel test robot
- Add keypad scanning with configurable keymap (new feature)
- Add support for TM1638 controller extending hardware compatibility
- Add support for default and maximum brightness properties
- Fix multi-instance device handling and add optional label property
- Allocate DMA-safe SPI buffer for hardware compatibility
- Enhance error handling with comprehensive kernel-doc documentation
- Remove sysfs runtime reconfiguration, enforce device tree-only

v2:
- Fix duplicate label in dt-bindings examples
- Rename device tree property prefixes to use titanmec vendor prefix

Jean-François Lessard (6):
dt-bindings: vendor-prefixes: Add fdhisi, titanmec, princeton,
winrise, wxicore
dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx
auxdisplay: Add TM16xx 7-segment LED matrix display controllers driver
auxdisplay: TM16xx: Add keypad support for scanning matrix keys
auxdisplay: TM16xx: Add support for I2C-based controllers
auxdisplay: TM16xx: Add support for SPI-based controllers

 .../ABI/testing/sysfs-class-leds-tm16xx       |  57 ++
 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++
 .../devicetree/bindings/vendor-prefixes.yaml  |  10 +
 MAINTAINERS                                   |  11 +
 drivers/auxdisplay/Kconfig                    |  43 +-
 drivers/auxdisplay/Makefile                   |   5 +
 drivers/auxdisplay/tm16xx.h                   | 204 ++++++
 drivers/auxdisplay/tm16xx_core.c              | 622 ++++++++++++++++++
 drivers/auxdisplay/tm16xx_i2c.c               | 332 ++++++++++
 drivers/auxdisplay/tm16xx_keypad.c            | 208 ++++++
 drivers/auxdisplay/tm16xx_spi.c               | 398 +++++++++++
 11 files changed, 2366 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-leds-tm16xx
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
 create mode 100644 drivers/auxdisplay/tm16xx.h
 create mode 100644 drivers/auxdisplay/tm16xx_core.c
 create mode 100644 drivers/auxdisplay/tm16xx_i2c.c
 create mode 100644 drivers/auxdisplay/tm16xx_keypad.c
 create mode 100644 drivers/auxdisplay/tm16xx_spi.c
Signature · 1 line
2.43.0
#1 lore raw

PATCH v4 1/6 dt-bindings: vendor-prefixes: Add fdhisi, titanmec, princeton, winrise, wxicore

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.

---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 10 ++++++++++
 1 file changed, 10 insertions(+)
Documentation/devicetree/bindings/vendor-prefixes.yaml +10 -0
index 77160cd47..9fdba2911 100644
@@ -540,6 +540,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,.*":
@@ -1233,6 +1235,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,.*":
@@ -1567,6 +1571,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,.*":
@@ -1724,6 +1730,8 @@ patternProperties:
     description: Wingtech Technology Co., Ltd.
   "^winlink,.*":
     description: WinLink Co., Ltd
+  "^winrise,.*":
+    description: Shenzhen Winrise Technology Co., Ltd.
   "^winsen,.*":
     description: Winsen Corp.
   "^winstar,.*":
@@ -1740,6 +1748,8 @@ patternProperties:
     description: Wobo
   "^wolfvision,.*":
     description: WolfVision GmbH
+  "^wxicore,.*":
+    description: Wuxi i-Core Electronics Co., Ltd.
   "^x-powers,.*":
     description: X-Powers
   "^xen,.*":
Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH v4 1/6] dt-bindings: vendor-prefixes: Add fdhisi, titanmec, princeton, winrise, wxicore

Quoted 15 lines · Jean-François Lessard

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.

...

Quoted 1 line
CC Andreas Färber <[email protected]>

Not a big issue in _this_ case, but I would suggest to keep the Cc: list after the '---' line. This will have same effect except being removed from the commit messages where it would be an unneeded noise. The actual list will be available via lore archive in emails.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#3 lore raw

Re: [PATCH v4 1/6] dt-bindings: vendor-prefixes: Add fdhisi, titanmec, princeton, winrise, wxicore

Le 25 août 2025 09 h 53 min 38 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 17 lines · Jean-François Lessard
Quoted 8 lines

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.

Quoted 7 lines

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.

Quoted 9 lines

...

Quoted 1 line
CC Andreas Färber <[email protected]>

Not a big issue in _this_ case, but I would suggest to keep the Cc: list after the '---' line. This will have same effect except being removed from the commit messages where it would be an unneeded noise. The actual list will be available via lore archive in emails.

Acknowledged. Will move to the notes section on the next submission.

#4 lore raw

PATCH v4 2/6 dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

In reply to Jean-François Lessard

Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan.

---

Notes:
The 'segments' property is intentionally not vendor-prefixed as it defines a generic hardware description concept applicable to any 7-segment display controller. The property describes the fundamental grid/segment coordinate mapping that is controller-agnostic and could be reused by other LED matrix display bindings. Similar to how 'gpios' describes GPIO connections generically, 'segments' describes segment connections in a standardized way using uint32-matrix format.

 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++++++
 MAINTAINERS                                   |   5 +
 2 files changed, 482 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +477 -0
new file mode 100644
index 000000000..c94556d95
@@ -0,0 +1,477 @@
+# 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 <[email protected]>
+
+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 grid and segment indices are controller-specific.
+
+  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.
+    $ref: /schemas/leds/common.yaml#/properties/label
+
+  max-brightness:
+    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.
+    $ref: /schemas/leds/common.yaml#/properties/max-brightness
+
+  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
+
+  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
+            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
+
+allOf:
+  - $ref: /schemas/input/input.yaml#
+  - $ref: /schemas/input/matrix-keymap.yaml#
+
+dependencies:
+  poll-interval:
+    - linux,keymap
+  linux,keymap:
+    - poll-interval
+  autorepeat:
+    - linux,keymap
+    - poll-interval
+
+# 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:
+  allOf:
+    - $ref: /schemas/spi/spi-peripheral-props.yaml#
+  properties:
+    spi-3wire: true
+  required:
+    - spi-3wire
+
+required:
+  - compatible
+  - reg
+
+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";
+          };
+        };
+      };
+    };
MAINTAINERS +5 -0
index daf520a13..4e5a7db6d 100644
@@ -25402,6 +25402,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 <[email protected]>
+S:	Maintained
+F:	Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
+
 TMIO/SDHI MMC DRIVER
 M:	Wolfram Sang <[email protected]>
 L:	[email protected]
Signature · 1 line
2.43.0
maintainer
#5 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

Quoted 99 lines · Jean-François Lessard

Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan.

---

Notes:
The 'segments' property is intentionally not vendor-prefixed as it defines a generic hardware description concept applicable to any 7-segment display controller. The property describes the fundamental grid/segment coordinate mapping that is controller-agnostic and could be reused by other LED matrix display bindings. Similar to how 'gpios' describes GPIO connections generically, 'segments' describes segment connections in a standardized way using uint32-matrix format.

 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++++++
 MAINTAINERS                                   |   5 +
 2 files changed, 482 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +73 -0
new file mode 100644
index 000000000..c94556d95
@@ -0,0 +1,477 @@
+# 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 <[email protected]>
+
+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 grid and segment indices are controller-specific.
+
+  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.

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 83

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+    $ref: /schemas/leds/common.yaml#/properties/label
+
+  max-brightness:
+    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.
+    $ref: /schemas/leds/common.yaml#/properties/max-brightness

These 2 $ref's should be at the node level. The clue is you copied-n-pasted the whole description.

What you need here is some constraints. What's the max value?

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 89

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+
+  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

This needs to first go into leds/common.yaml.

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+
+  digits:
+    type: object
+    description: Container for 7-segment digit group definitions
+    additionalProperties: false
+
+    properties:
+      "#address-cells":
+        const: 1
+      "#size-cells":

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 109

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+        const: 0
+
+    patternProperties:
+      "^digit@[0-9]+$":
+        type: object
+        unevaluatedProperties: false
+
+        properties:
+          reg:
+            description: Digit position identifier

Position is right to left (0 on right)? Please clarify.

Quoted 11 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +11 -0
+            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

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 130

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+                f   b
+                f   b
+                 ggg
+                e   c
+                e   c
+                 ddd
+            items:
+              items:
+                - description: Grid index
+                - description: Segment index

Can't you do an array instead and make the array index be the grid or segment index?

Quoted 37 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +37 -0
+            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
+
+allOf:
+  - $ref: /schemas/input/input.yaml#
+  - $ref: /schemas/input/matrix-keymap.yaml#
+
+dependencies:

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 177

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+  poll-interval:
+    - linux,keymap
+  linux,keymap:
+    - poll-interval
+  autorepeat:
+    - linux,keymap
+    - poll-interval
+
+# SPI controllers require 3-wire (combined MISO/MOSI line)
+if:

Move this under the allOf.

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+  properties:
+    compatible:
+      contains:
+        enum:
+          - fdhisi,fd620

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 192

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+          - fdhisi,fd628
+          - princeton,pt6964
+          - titanmec,tm1618
+          - titanmec,tm1620
+          - titanmec,tm1628
+          - titanmec,tm1638
+          - wxicore,aip1618
+          - wxicore,aip1628
+then:
+  allOf:

Drop allOf.

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 195

Quoted 3 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +3 -0
+    - $ref: /schemas/spi/spi-peripheral-props.yaml#
+  properties:
+    spi-3wire: true

Drop 'properties'

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 199

Quoted 4 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +4 -0
+  required:
+    - spi-3wire
+
+required:

Order should be 'dependencies', 'required', 'allOf'.

Quoted 297 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +278 -0
+  - compatible
+  - reg
+
+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";
+          };
+        };
+      };
+    };
MAINTAINERS +5 -0
index daf520a13..4e5a7db6d 100644
@@ -25402,6 +25402,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 <[email protected]>
+S:	Maintained
+F:	Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
+
 TMIO/SDHI MMC DRIVER
 M:	Wolfram Sang <[email protected]>
 L:	[email protected]
Signature · 1 line
2.43.0
#6 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

Le 25 août 2025 14 h 26 min 57 s HAE, Rob Herring <[email protected]> a écrit :

Quoted 36 lines · Jean-François Lessard
Quoted 35 lines

Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan.

---

Notes:
The 'segments' property is intentionally not vendor-prefixed as it defines a generic hardware description concept applicable to any 7-segment display controller. The property describes the fundamental grid/segment coordinate mapping that is controller-agnostic and could be reused by other LED matrix display bindings. Similar to how 'gpios' describes GPIO connections generically, 'segments' describes segment connections in a standardized way using uint32-matrix format.

 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++++++
 MAINTAINERS                                   |   5 +
 2 files changed, 482 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +9 -0
new file mode 100644
index 000000000..c94556d95
@@ -0,0 +1,477 @@
+# 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:
Quoted 8 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 17

Quoted 8 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +8 -0
+  - Jean-François Lessard <[email protected]>
+
+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 grid and segment indices are controller-specific.

In reference to max-brightness, I'll replace with:

Maximum brightness and grid/segment indices are controller-specific. Controller-specific maximum are validated in the driver.

Quoted 61 lines
Quoted 61 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +61 -0
+
+  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.
+    $ref: /schemas/leds/common.yaml#/properties/label
+
+  max-brightness:
+    description:
+      Normally the maximum brightness is determined by the hardware and this
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 83

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+      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.
+    $ref: /schemas/leds/common.yaml#/properties/max-brightness

These 2 $ref's should be at the node level. The clue is you copied-n-pasted the whole description.

I'll add:

allOf:
- $ref: /schemas/leds/common.yaml#

at the node level and constrain inapplicable LED properties (color, function) using properties: false since this auxdisplay device integrates with LED subsystem for brightness control.

Quoted 2 lines

What you need here is some constraints. What's the max value?

Maximum brightness varies by controller:
- TM1618/TM1628/TM1638 support levels 0-8
- TM1650 supports levels 0-8
- TM1620 supports levels 0-3
I'll set the schema maximum to 8:

max-brightness:
maximum: 8 # Maximum across all TM16xx controllers

with the top-level description note that actual limits are controller-dependent and are enforced by the driver.

Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 89

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+
+  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

This needs to first go into leds/common.yaml.

Given its specific relevance to this auxdisplay use case rather than general LED behavior, I am not sure it's worth adding default-brightness to LEDs/common.yaml If broader LED subsystem adoption is wanted, I am willing to submit a separate patch to this series to add it.

Otherwise, existing precedent in backlight/common.yaml and leds/leds-pwm.yaml would advocate for defining it locally.

Quoted 14 lines
Quoted 14 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +14 -0
+
+  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]+$":
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 109

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+        type: object
+        unevaluatedProperties: false
+
+        properties:
+          reg:
+            description: Digit position identifier

Position is right to left (0 on right)? Please clarify.

I'll clarify: digit positions are numbered sequentially left-to-right, with reg=0 representing the leftmost digit position as displayed to the user.

Quoted 16 lines
Quoted 16 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +16 -0
+            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
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 130

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+                 ddd
+            items:
+              items:
+                - description: Grid index
+                - description: Segment index

Can't you do an array instead and make the array index be the grid or segment index?

Original design was array-based:
- titanmec,digits: array of grid indices
- titanmec,segment-mapping: array of segment indices for a,b,c,d,e,f,g
- titanmec,transposed: boolean for matrix-transposed cases

The current explicit coordinate approach was adopted based on v2 feedback and handles both standard and transposed wiring patterns effectively, where manufacturers swap grid/segment relationships:
- Standard: digit segments use same grid, different segments
- Transposed: digit segments use same segment, different grids
It also future-proofs potential irregular wiring patterns where individual digits might have different grid/segment relationships.

Unless you have strong objections, I prefer to keep this approach to avoid further churn, as it's proven to handle all the real-world board layouts encountered.

See ttps://lore.kernel.org/linux-devicetree/[email protected]/

Quoted 41 lines
Quoted 41 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +41 -0
+            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
+
+allOf:
+  - $ref: /schemas/input/input.yaml#
+  - $ref: /schemas/input/matrix-keymap.yaml#
+
+dependencies:
+  poll-interval:
+    - linux,keymap
+  linux,keymap:
+    - poll-interval
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 177

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+  autorepeat:
+    - linux,keymap
+    - poll-interval
+
+# SPI controllers require 3-wire (combined MISO/MOSI line)
+if:

Move this under the allOf.

Well received.

Quoted 16 lines
Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+  properties:
+    compatible:
+      contains:
+        enum:
+          - fdhisi,fd620

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 192

Quoted 10 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +10 -0
+          - fdhisi,fd628
+          - princeton,pt6964
+          - titanmec,tm1618
+          - titanmec,tm1620
+          - titanmec,tm1628
+          - titanmec,tm1638
+          - wxicore,aip1618
+          - wxicore,aip1628
+then:
+  allOf:
Quoted 2 lines

Drop allOf.

Well received.

Quoted 6 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 195

Quoted 3 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +3 -0
+    - $ref: /schemas/spi/spi-peripheral-props.yaml#
+  properties:
+    spi-3wire: true

Drop 'properties'

Well received.

Quoted 7 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 199

Quoted 4 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +4 -0
+  required:
+    - spi-3wire
+
+required:

Order should be 'dependencies', 'required', 'allOf'.

I'll reorder the schema sections accordingly.

Quoted 5 lines
Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+

...

Quoted 3 lines
Quoted 3 lines
Signature · 1 line
2.43.0

Thanks for your time and your feedback,

Best Regards
Jean-François Lessard

#7 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

Le 25 août 2025 21 h 33 min 58 s HAE, "Jean-François Lessard" <[email protected]> a écrit :

Quoted 8 lines

Le 25 août 2025 14 h 26 min 57 s HAE, Rob Herring <[email protected]> a écrit :

Quoted 7 lines · Jean-François Lessard
Quoted 6 lines

Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan.

---

...

Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++++++
 MAINTAINERS                                   |   5 +
 2 files changed, 482 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +8 -0
new file mode 100644
index 000000000..c94556d95
@@ -0,0 +1,477 @@
+# 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
+

...

Quoted 66 lines
Quoted 14 lines
Quoted 14 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +14 -0
+
+  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]+$":
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 109

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+        type: object
+        unevaluatedProperties: false
+
+        properties:
+          reg:
+            description: Digit position identifier

Position is right to left (0 on right)? Please clarify.

I'll clarify: digit positions are numbered sequentially left-to-right, with reg=0 representing the leftmost digit position as displayed to the user.

Quoted 16 lines
Quoted 16 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +16 -0
+            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
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 130

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+                 ddd
+            items:
+              items:
+                - description: Grid index
+                - description: Segment index

Can't you do an array instead and make the array index be the grid or segment index?

Original design was array-based:
- titanmec,digits: array of grid indices
- titanmec,segment-mapping: array of segment indices for a,b,c,d,e,f,g
- titanmec,transposed: boolean for matrix-transposed cases

The current explicit coordinate approach was adopted based on v2 feedback and handles both standard and transposed wiring patterns effectively, where manufacturers swap grid/segment relationships:
- Standard: digit segments use same grid, different segments
- Transposed: digit segments use same segment, different grids
It also future-proofs potential irregular wiring patterns where individual digits might have different grid/segment relationships.

Quoted 7 lines

Unless you have strong objections, I prefer to keep this approach to avoid further churn, as it's proven to handle all the real-world board layouts encountered.

See ttps://lore.kernel.org/linux-devicetree/[email protected]/

Diving deeper on your suggestion of using arrays, would this revised design be acceptable?

properties:
digits:
patternProperties:
"^digit@[0-9]+$":
properties:
reg:
maxItems: 1

grids:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: Grid indices for segments a,b,c,d,e,f,g in order
minItems: 7
maxItems: 7

segments:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: Segment indices for segments a,b,c,d,e,f,g in order
minItems: 7
maxItems: 7

This approach:
- Uses arrays as you suggested, indexed by segment position
- Maintains flexibility for both standard and transpose layouts
- Keeps the semantic clarity that Krzysztof requested

Example usage would be:

digit@0 {
reg = <0>;
grids = <4 4 4 4 4 4 4>; // Standard: all segments use same grid
segments = <3 4 5 0 1 2 6>; // Different segment indices
};

// vs transpose case:

digit@0 {
    reg = <0>;
    grids = <0 1 2 3 4 5 6>;     // Transpose: different grids
    segments = <3 3 3 3 3 3 3>;   // Same segment index
};

Would this better align with your preference for array-based approaches?

If so, the remaining question is if these needs to be vendor prefixed or if they are still generic enough hardware description concept applicable to any 7-segment display controller.

Quoted 7 lines
Quoted 7 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 137

Quoted 7 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +7 -0
+            minItems: 7
+            maxItems: 7
+
+        required:
+          - reg
+          - segments
+

...

Best Regards
Jean-François Lessard

maintainer
#8 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

In reply to Jean-François Lessard
Quoted 155 lines · Jean-François Lessard

Le 25 août 2025 14 h 26 min 57 s HAE, Rob Herring <[email protected]> a écrit :

Quoted 36 lines · Jean-François Lessard
Quoted 35 lines

Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan.

---

Notes:
The 'segments' property is intentionally not vendor-prefixed as it defines a generic hardware description concept applicable to any 7-segment display controller. The property describes the fundamental grid/segment coordinate mapping that is controller-agnostic and could be reused by other LED matrix display bindings. Similar to how 'gpios' describes GPIO connections generically, 'segments' describes segment connections in a standardized way using uint32-matrix format.

 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++++++
 MAINTAINERS                                   |   5 +
 2 files changed, 482 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +9 -0
new file mode 100644
index 000000000..c94556d95
@@ -0,0 +1,477 @@
+# 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:
Quoted 8 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 17

Quoted 8 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +8 -0
+  - Jean-François Lessard <[email protected]>
+
+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 grid and segment indices are controller-specific.

In reference to max-brightness, I'll replace with:

Maximum brightness and grid/segment indices are controller-specific. Controller-specific maximum are validated in the driver.

Quoted 61 lines
Quoted 61 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +61 -0
+
+  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.
+    $ref: /schemas/leds/common.yaml#/properties/label
+
+  max-brightness:
+    description:
+      Normally the maximum brightness is determined by the hardware and this
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 83

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+      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.
+    $ref: /schemas/leds/common.yaml#/properties/max-brightness

These 2 $ref's should be at the node level. The clue is you copied-n-pasted the whole description.

I'll add:

allOf:
- $ref: /schemas/leds/common.yaml#

at the node level and constrain inapplicable LED properties (color, function) using properties: false since this auxdisplay device integrates with LED subsystem for brightness control.

Quoted 2 lines

What you need here is some constraints. What's the max value?

Maximum brightness varies by controller:
- TM1618/TM1628/TM1638 support levels 0-8
- TM1650 supports levels 0-8
- TM1620 supports levels 0-3
I'll set the schema maximum to 8:

max-brightness:
maximum: 8 # Maximum across all TM16xx controllers

with the top-level description note that actual limits are controller-dependent and are enforced by the driver.

Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 89

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+
+  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

This needs to first go into leds/common.yaml.

Quoted 7 lines

Given its specific relevance to this auxdisplay use case rather than general LED behavior, I am not sure it's worth adding default-brightness to LEDs/common.yaml If broader LED subsystem adoption is wanted, I am willing to submit a separate patch to this series to add it.

Otherwise, existing precedent in backlight/common.yaml and leds/leds-pwm.yaml would advocate for defining it locally.

The type for a property should really only be set in 1 place. Otherwise, it is easy to define different types for the same property (which we have and have to deal with some).

Given we now have 2 cases for LEDs, it should at least be in leds/common.yaml. Should there be 1 definition for both backlight and LEDs, yes. But I can live with 2 definitions in common bindings for now.

Quoted 66 lines
Quoted 14 lines
Quoted 14 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +14 -0
+
+  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]+$":
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 109

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+        type: object
+        unevaluatedProperties: false
+
+        properties:
+          reg:
+            description: Digit position identifier

Position is right to left (0 on right)? Please clarify.

I'll clarify: digit positions are numbered sequentially left-to-right, with reg=0 representing the leftmost digit position as displayed to the user.

Quoted 16 lines
Quoted 16 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +16 -0
+            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
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 130

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+                 ddd
+            items:
+              items:
+                - description: Grid index
+                - description: Segment index

Can't you do an array instead and make the array index be the grid or segment index?

Original design was array-based:
- titanmec,digits: array of grid indices
- titanmec,segment-mapping: array of segment indices for a,b,c,d,e,f,g
- titanmec,transposed: boolean for matrix-transposed cases

The current explicit coordinate approach was adopted based on v2 feedback and handles both standard and transposed wiring patterns effectively, where manufacturers swap grid/segment relationships:
- Standard: digit segments use same grid, different segments
- Transposed: digit segments use same segment, different grids
It also future-proofs potential irregular wiring patterns where individual digits might have different grid/segment relationships.

Quoted 3 lines

Unless you have strong objections, I prefer to keep this approach to avoid further churn, as it's proven to handle all the real-world board layouts encountered.

2 arrays would not be an improvement. So leave it as you have it.

Rob

#9 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

Le 29 août 2025 11 h 26 min 13 s HAE, Rob Herring <[email protected]> a écrit :

Quoted 164 lines · Jean-François Lessard
Quoted 155 lines

Le 25 août 2025 14 h 26 min 57 s HAE, Rob Herring <[email protected]> a écrit :

Quoted 36 lines · Jean-François Lessard
Quoted 35 lines

Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan.

---

Notes:
The 'segments' property is intentionally not vendor-prefixed as it defines a generic hardware description concept applicable to any 7-segment display controller. The property describes the fundamental grid/segment coordinate mapping that is controller-agnostic and could be reused by other LED matrix display bindings. Similar to how 'gpios' describes GPIO connections generically, 'segments' describes segment connections in a standardized way using uint32-matrix format.

 .../bindings/auxdisplay/titanmec,tm16xx.yaml  | 477 ++++++++++++++++++
 MAINTAINERS                                   |   5 +
 2 files changed, 482 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +9 -0
new file mode 100644
index 000000000..c94556d95
@@ -0,0 +1,477 @@
+# 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:
Quoted 8 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 17

Quoted 8 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +8 -0
+  - Jean-François Lessard <[email protected]>
+
+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 grid and segment indices are controller-specific.

In reference to max-brightness, I'll replace with:

Maximum brightness and grid/segment indices are controller-specific. Controller-specific maximum are validated in the driver.

Quoted 61 lines
Quoted 61 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +61 -0
+
+  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.
+    $ref: /schemas/leds/common.yaml#/properties/label
+
+  max-brightness:
+    description:
+      Normally the maximum brightness is determined by the hardware and this
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 83

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+      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.
+    $ref: /schemas/leds/common.yaml#/properties/max-brightness

These 2 $ref's should be at the node level. The clue is you copied-n-pasted the whole description.

I'll add:

allOf:
- $ref: /schemas/leds/common.yaml#

at the node level and constrain inapplicable LED properties (color, function) using properties: false since this auxdisplay device integrates with LED subsystem for brightness control.

Quoted 2 lines

What you need here is some constraints. What's the max value?

Maximum brightness varies by controller:
- TM1618/TM1628/TM1638 support levels 0-8
- TM1650 supports levels 0-8
- TM1620 supports levels 0-3
I'll set the schema maximum to 8:

max-brightness:
maximum: 8 # Maximum across all TM16xx controllers

with the top-level description note that actual limits are controller-dependent and are enforced by the driver.

Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 89

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+
+  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

This needs to first go into leds/common.yaml.

Quoted 7 lines

Given its specific relevance to this auxdisplay use case rather than general LED behavior, I am not sure it's worth adding default-brightness to LEDs/common.yaml If broader LED subsystem adoption is wanted, I am willing to submit a separate patch to this series to add it.

Otherwise, existing precedent in backlight/common.yaml and leds/leds-pwm.yaml would advocate for defining it locally.

Quoted 8 lines

The type for a property should really only be set in 1 place. Otherwise, it is easy to define different types for the same property (which we have and have to deal with some).

Given we now have 2 cases for LEDs, it should at least be in leds/common.yaml. Should there be 1 definition for both backlight and LEDs, yes. But I can live with 2 definitions in common bindings for now.

Understood. I'll add default-brightness definition to leds/common.yaml.

Quoted 66 lines
Quoted 66 lines
Quoted 14 lines
Quoted 14 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +14 -0
+
+  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]+$":
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 109

Quoted 6 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +6 -0
+        type: object
+        unevaluatedProperties: false
+
+        properties:
+          reg:
+            description: Digit position identifier

Position is right to left (0 on right)? Please clarify.

I'll clarify: digit positions are numbered sequentially left-to-right, with reg=0 representing the leftmost digit position as displayed to the user.

Quoted 16 lines
Quoted 16 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +16 -0
+            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
Quoted 9 lines

Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml line 130

Quoted 5 lines
Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file +5 -0
+                 ddd
+            items:
+              items:
+                - description: Grid index
+                - description: Segment index

Can't you do an array instead and make the array index be the grid or segment index?

Original design was array-based:
- titanmec,digits: array of grid indices
- titanmec,segment-mapping: array of segment indices for a,b,c,d,e,f,g
- titanmec,transposed: boolean for matrix-transposed cases

The current explicit coordinate approach was adopted based on v2 feedback and handles both standard and transposed wiring patterns effectively, where manufacturers swap grid/segment relationships:
- Standard: digit segments use same grid, different segments
- Transposed: digit segments use same segment, different grids
It also future-proofs potential irregular wiring patterns where individual digits might have different grid/segment relationships.

Quoted 6 lines
Quoted 3 lines

Unless you have strong objections, I prefer to keep this approach to avoid further churn, as it's proven to handle all the real-world board layouts encountered.

2 arrays would not be an improvement. So leave it as you have it.

Acknowledged. I'll keep segments as uint32-matrix.

Thank you,
Jean-François Lessard

#10 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

In reply to Jean-François Lessard
Quoted 1 line · On Sun, 24 Aug 2025 23:32:28 -0400 Jean-François Lessard…

+ - Digits use 1-cell addressing with explicit segment mapping

This new digits layout introduced in v3 implies that a different segment mapping can be used per digit by a single display/controller. Is that really a thing?

Regards
Per Larsson

#11 lore raw

Re: [PATCH v4 2/6] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx

Le 25 août 2025 15 h 08 min 36 s HAE, Per Larsson <[email protected]> a écrit :

Quoted 9 lines
Quoted 1 line · On Sun, 24 Aug 2025 23:32:28 -0400 Jean-François Lessard…

+ - Digits use 1-cell addressing with explicit segment mapping

This new digits layout introduced in v3 implies that a different segment mapping can be used per digit by a single display/controller. Is that really a thing?

Board manufacturers typically use consistent patterns across digits, but some use matrix-transposed layouts where grid/segment relationships are swapped system-wide. The explicit coordinate mapping handles both standard and transposed configurations.

Bottom line: I am not aware of different per digit 7-segment mappings on a single display board.

Best Regards
Jean-François Lessard

#12 lore raw

PATCH v4 3/6 auxdisplay: Add TM16xx 7-segment LED matrix display controllers driver

In reply to Jean-François Lessard

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).

---

Notes:
checkpatch reports false positives that are intentionally ignored:
DEVICE_ATTR_FUNCTIONS: Functions are correctly prefixed with driver name (tm16xx_*) following standard kernel practice for device attribute functions to avoid namespace conflicts.
BIT_MACRO: bit shifts are used for field values while GENMASK/BIT are used for bit positions per semantic convention

include <linux/of.h> is required for the default name of the main led device, excluding the unit address, as implemented in drivers/leds/led-core.c which relies on of_node->name

LED registration uses non-devm variant on-purpose to allow explicit unregistration on device removal, ensuring LED triggers are immediately stopped. This prevents stale LED trigger activity from continuing after the hardware is gone, avoiding the need for complex state tracking in brightness callbacks.

 .../ABI/testing/sysfs-class-leds-tm16xx       |  57 ++
 MAINTAINERS                                   |   3 +
 drivers/auxdisplay/Kconfig                    |  20 +-
 drivers/auxdisplay/Makefile                   |   2 +
 drivers/auxdisplay/tm16xx.h                   | 177 +++++
 drivers/auxdisplay/tm16xx_core.c              | 618 ++++++++++++++++++
 6 files changed, 876 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-leds-tm16xx
 create mode 100644 drivers/auxdisplay/tm16xx.h
 create mode 100644 drivers/auxdisplay/tm16xx_core.c
Documentation/ABI/testing/sysfs-class-leds-tm16xx new file +57 -0
new file mode 100644
index 000000000..c01cca251
@@ -0,0 +1,57 @@
+What:		/sys/class/leds/<led>/value
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Controls the text displayed on the TM16xx 7-segment display.
+
+		Reading returns the current display content as ASCII characters,
+		one character per digit position, followed by a newline.
+
+		Writing sets new display content. Input characters are mapped
+		to 7-segment patterns using the configured character map. The
+		string length should not exceed the number of available digits
+		(see num_digits). Shorter strings will clear remaining digits.
+
+		Example:
+		  echo "1234" > value    # Display "1234"
+		  cat value              # Returns "1234\n"
+
+What:		/sys/class/leds/<led>/num_digits
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Read-only attribute showing the number of 7-segment digit
+		positions available on this TM16xx display controller.
+
+		The value is determined by the device tree configuration
+		and represents the maximum length for the 'value' attribute.
+
+		Example:
+		  cat num_digits         # Returns "4\n" for 4-digit display
+
+What:		/sys/class/leds/<led>/map_seg7
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Read/write binary blob representing the ASCII-to-7-segment
+		display conversion table used by the TM16xx driver, as defined
+		by struct seg7_conversion_map in <linux/map_to_7segment.h>.
+
+		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 auxdisplay and 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
+Users:		Display configuration utilities and embedded system scripts/tools.
MAINTAINERS +3 -0
index 4e5a7db6d..0ed971881 100644
@@ -25405,7 +25405,10 @@ F:	drivers/net/ethernet/ti/tlan.*
 TM16XX-COMPATIBLE LED CONTROLLERS DISPLAY DRIVER
 M:	Jean-François Lessard <[email protected]>
 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 <[email protected]>
drivers/auxdisplay/Kconfig +19 -1
index bedc6133f..7b58c6cc8 100644
@@ -316,7 +316,7 @@ endif # PARPORT_PANEL
 
 config PANEL_CHANGE_MESSAGE
 	bool "Change LCD initialization message ?"
-	depends on CHARLCD || LINEDISP
+	depends on CHARLCD || LINEDISP || TM16XX
 	help
 	  This allows you to replace the boot message indicating the kernel version
 	  and the driver version with a custom message. This is useful on appliances
@@ -526,6 +526,24 @@ 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 "TM16XX-compatible 7-segment LED controllers"
+	depends on SPI || I2C
+	select NEW_LEDS
+	select LEDS_CLASS
+	select LEDS_TRIGGERS
+	help
+	  This driver supports the following TM16XX compatible
+	  I2C and SPI 7-segment led display chips:
+	  - Titanmec: TM1618, TM1620, TM1628, TM1638, TM1650
+	  - Fuda Hisi: FD620, FD628, FD650, FD655, FD6551
+	  - i-Core: AiP650, AiP1618, AiP1628
+	  - Princeton: PT6964
+	  - Winrise: HBS658
+
+	  This driver can also be built as a module. If so, the module
+	  will be called tm16xx.
+
 #
 # Character LCD with non-conforming interface section
 #
drivers/auxdisplay/Makefile +2 -0
index f5c13ed1c..7ecf3cd4a 100644
@@ -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
drivers/auxdisplay/tm16xx.h new file +177 -0

177 lines not on this page · raw message

drivers/auxdisplay/tm16xx_core.c new file +618 -0

618 lines not on this page · raw message

Signature · 1 line
2.43.0
#13 lore raw
Quoted 8 lines · Jean-François Lessard

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).

...

Quoted 11 lines

Notes:
checkpatch reports false positives that are intentionally ignored:
DEVICE_ATTR_FUNCTIONS: Functions are correctly prefixed with driver name (tm16xx_*) following standard kernel practice for device attribute functions to avoid namespace conflicts.
BIT_MACRO: bit shifts are used for field values while GENMASK/BIT are used for bit positions per semantic convention

include <linux/of.h> is required for the default name of the main led device, excluding the unit address, as implemented in drivers/leds/led-core.c which relies on of_node->name

Sorry, but I do not see how of.h is related to all this... Please, drop it.

Quoted 5 lines

LED registration uses non-devm variant on-purpose to allow explicit unregistration on device removal, ensuring LED triggers are immediately stopped. This prevents stale LED trigger activity from continuing after the hardware is gone, avoiding the need for complex state tracking in brightness callbacks.

...

Documentation/ABI/testing/sysfs-class-leds-tm16xx line 3

Quoted 4 lines
Documentation/ABI/testing/sysfs-class-leds-tm16xx new file +3 -0
+What:		/sys/class/leds/<led>/value
 
+Date:		August 2025
+KernelVersion:	6.17

The Date should be approximate date of the kernel release (alternatively, -rc1 of that). The version is estimated version where ABI can be found first.

Both of these need to be changed.

Quoted 43 lines
Documentation/ABI/testing/sysfs-class-leds-tm16xx new file +43 -0
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Controls the text displayed on the TM16xx 7-segment display.
+
+		Reading returns the current display content as ASCII characters,
+		one character per digit position, followed by a newline.
+
+		Writing sets new display content. Input characters are mapped
+		to 7-segment patterns using the configured character map. The
+		string length should not exceed the number of available digits
+		(see num_digits). Shorter strings will clear remaining digits.
+
+		Example:
+		  echo "1234" > value    # Display "1234"
+		  cat value              # Returns "1234\n"
+
+What:		/sys/class/leds/<led>/num_digits
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Read-only attribute showing the number of 7-segment digit
+		positions available on this TM16xx display controller.
+
+		The value is determined by the device tree configuration
+		and represents the maximum length for the 'value' attribute.
+
+		Example:
+		  cat num_digits         # Returns "4\n" for 4-digit display
+
+What:		/sys/class/leds/<led>/map_seg7
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Read/write binary blob representing the ASCII-to-7-segment
+		display conversion table used by the TM16xx driver, as defined
+		by struct seg7_conversion_map in <linux/map_to_7segment.h>.
+
+		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.
+

Documentation/ABI/testing/sysfs-class-leds-tm16xx line 56

Quoted 10 lines
Documentation/ABI/testing/sysfs-class-leds-tm16xx new file +10 -0
+		This interface and its implementation match existing conventions
+		used in auxdisplay and 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

So, the driver is under auxdisplay, but at the same time it completely relies on LED subsystem... What's going on here?

Btw, have you seen
https://lore.kernel.org/linux-leds/[email protected]/
? And if so, what're your takeaways? (Yes, I know that's about different HW)

Quoted 1 line

+Users: Display configuration utilities and embedded system scripts/tools.

...

Quoted 1 line

+ * Copyright (C) 2024 Jean-François Lessard

My calendar shows something different.

drivers/auxdisplay/tm16xx.h line 13

Quoted 3 lines
drivers/auxdisplay/tm16xx.h new file +2 -0
+#include <linux/bitfield.h>
 
+#include <linux/bitmap.h>

Is this used?

Quoted 2 lines
drivers/auxdisplay/tm16xx.h new file +2 -0
+#include <linux/leds.h>
+#include <linux/workqueue.h>

...

Quoted 1 line

+#define TM16XX_DIGIT_SEGMENTS 7

Why do we even need this?

...

drivers/auxdisplay/tm16xx.h line 101

Quoted 2 lines
drivers/auxdisplay/tm16xx.h new file +2 -0
+#define TM16XX_CTRL_BRIGHTNESS(on, val, prfx) \
+	((on) ? (FIELD_PREP(prfx##_CTRL_BR_MASK, (val)) | prfx##_CTRL_ON) : 0)

prefix can be spelled fully, going slightly over 80 is not a crime.

...

drivers/auxdisplay/tm16xx.h line 158

Quoted 8 lines
drivers/auxdisplay/tm16xx.h new file +7 -0
+struct tm16xx_display {
+	struct device *dev;
+	const struct tm16xx_controller *controller;
 
+	union {
+		struct i2c_client *i2c;
+		struct spi_device *spi;
+	} client;

Why? Just drop it. struct device *dev is enough and I can't see the need in this at all.

Quoted 14 lines
drivers/auxdisplay/tm16xx.h new file +14 -0
+	u8 *spi_buffer;
+	u8 num_grids;
+	u8 num_segments;
+	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;
+};

...

Quoted 1 line

+ * Copyright (C) 2024 Jean-François Lessard

Year?

...

drivers/auxdisplay/tm16xx_core.c line 12

Quoted 4 lines
drivers/auxdisplay/tm16xx_core.c new file +4 -0
+#include <linux/map_to_7segment.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/property.h>

Please, follow IWYU principle.

...

drivers/auxdisplay/tm16xx_core.c line 267

Quoted 7 lines
drivers/auxdisplay/tm16xx_core.c new file +7 -0
+static ssize_t tm16xx_num_digits_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct tm16xx_display *display = dev_get_drvdata(led_cdev->dev->parent);
+
+	return sprintf(buf, "%u\n", display->num_digits);

Should be sysfs_emit().

Quoted 1 line

+}

...

drivers/auxdisplay/tm16xx_core.c line 283

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+static ssize_t tm16xx_map_seg7_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	memcpy(buf, &map_seg7, sizeof(map_seg7));
+	return sizeof(map_seg7);
+}

Can we use LINEDISP library?

...

drivers/auxdisplay/tm16xx_core.c line 312

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+static struct attribute *tm16xx_main_led_attrs[] = {
+	&dev_attr_value.attr,
+	&dev_attr_num_digits.attr,
+	&dev_attr_map_seg7.attr,
 
+	NULL,

No comma in the terminator entry.

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+};
+ATTRIBUTE_GROUPS(tm16xx_main_led);

...

drivers/auxdisplay/tm16xx_core.c line 326

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +4 -0
+static int tm16xx_display_init(struct tm16xx_display *display)
+{
+	unsigned int nbits = tm16xx_led_nbits(display);
 
+	dev_dbg(display->dev, "Initializing display\n");

Please, drop all these dev_dbg() over the code as they are close to useless, use tracers and other mechanisms to debug if required.

Also drop unneeded kernel-doc for the (esp. static) functions that have well established meaning (e.g., no need a kernel-doc for device attributes as they should be documented in the ABI).

Quoted 22 lines
drivers/auxdisplay/tm16xx_core.c new file +22 -0
+	schedule_work(&display->flush_init);
+	flush_work(&display->flush_init);
+	if (display->flush_status < 0)
+		return display->flush_status;
+
+	if (tm16xx_init_value) {
+		tm16xx_value_store(display->main_led.dev, NULL,
+				   tm16xx_init_value,
+				   strlen(tm16xx_init_value));
+	} else {
+		bitmap_fill(display->state, nbits);
+		schedule_work(&display->flush_display);
+		flush_work(&display->flush_display);
+		bitmap_zero(display->state, nbits);
+		if (display->flush_status < 0)
+			return display->flush_status;
+	}
+
+	dev_info(display->dev, "Display turned on\n");
+
+	return 0;
+}

...

Quoted 1 line

+static int tm16xx_parse_dt(struct device *dev, struct tm16xx_display *display)

Why DT only? No support for other platforms? Why? I think this is just matter of naming the function properly.

drivers/auxdisplay/tm16xx_core.c line 362

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+{
+	struct fwnode_handle *leds_node, *digits_node, *child;
+	struct tm16xx_led *led;
+	struct tm16xx_digit *digit;
 
+	int max_grid = 0, max_segment = 0;

Why signed?

drivers/auxdisplay/tm16xx_core.c line 363

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+	int ret, i, j;

Why are i and j signed?

drivers/auxdisplay/tm16xx_core.c line 372

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+	u32 segments[TM16XX_DIGIT_SEGMENTS * 2];
+	u32 reg[2];
+
+	/* parse digits */
+	digits_node = device_get_named_child_node(dev, "digits");
+	if (digits_node) {
 
+		display->num_digits = 0;
+		fwnode_for_each_child_node(digits_node, child)
+			display->num_digits++;

Don't we have a _count API for this?

drivers/auxdisplay/tm16xx_core.c line 381

Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+		dev_dbg(dev, "Number of digits: %u\n", display->num_digits);
+
+		if (display->num_digits) {
+			display->digits = devm_kcalloc(dev, display->num_digits,
+						       sizeof(*display->digits),
+						       GFP_KERNEL);
+			if (!display->digits) {
 
+				fwnode_handle_put(digits_node);

Use RAII instead, we have defined __free() method for this.

drivers/auxdisplay/tm16xx_core.c line 386

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+				return -ENOMEM;
+			}
+
+			i = 0;
+			fwnode_for_each_child_node(digits_node, child) {

Ditto. Use _scoped variant.

drivers/auxdisplay/tm16xx_core.c line 390

Quoted 4 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+				digit = &display->digits[i];
 
+				ret = fwnode_property_read_u32(child, "reg",
+							       reg);

One line.

Review

Quoted 1 line
(quoted diff) +1 -0
+				if (ret < 0) {

Can it be positive? Here and everywhere else, if there is no positive return, use 'if (ret)'.

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +10 -0
+					fwnode_handle_put(child);
+					fwnode_handle_put(digits_node);
+					return ret;
+				}
+
+				ret = fwnode_property_read_u32_array(child,
+								     "segments",
+								     segments,
+								     TM16XX_DIGIT_SEGMENTS * 2);
+				if (ret < 0) {

drivers/auxdisplay/tm16xx_core.c line 410

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+					fwnode_handle_put(child);
+					fwnode_handle_put(digits_node);
+					return ret;
+				}
+
+				for (j = 0; j < TM16XX_DIGIT_SEGMENTS; ++j) {
+					digit->segments[j].grid = segments[2 * j];
+					digit->segments[j].segment = segments[2 * j + 1];
 
+					max_grid = umax(max_grid,

Firstly, the variables made signed and then specifically force them to be unsigned in the macro. Weird. Can we make them to be a proper type and use max()?

drivers/auxdisplay/tm16xx_core.c line 411

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+							digit->segments[j].grid);

One line

drivers/auxdisplay/tm16xx_core.c line 413

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+					max_segment = umax(max_segment,
+							   digit->segments[j].segment);

As per above comments.

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+				}
+				digit->value = 0;
+				i++;
+			}
+

drivers/auxdisplay/tm16xx_core.c line 428

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +10 -0
+			fwnode_handle_put(digits_node);
+		}
+	}
+
+	/* parse leds */
+	leds_node = device_get_named_child_node(dev, "leds");
+	if (leds_node) {
+		display->num_leds = 0;
+		fwnode_for_each_child_node(leds_node, child)
+			display->num_leds++;

As per above.

Quoted 8 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+		dev_dbg(dev, "Number of LEDs: %u\n", display->num_leds);
+
+		if (display->num_leds) {
+			display->leds = devm_kcalloc(dev, display->num_leds,
+						     sizeof(*display->leds),
+						     GFP_KERNEL);
+			if (!display->leds) {
+				fwnode_handle_put(leds_node);

drivers/auxdisplay/tm16xx_core.c line 446

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+				return -ENOMEM;
+			}
+
+			i = 0;
+			fwnode_for_each_child_node(leds_node, child) {
+				led = &display->leds[i];
 
+				ret = fwnode_property_read_u32_array(child,
+								     "reg", reg,
+								     2);

Make it one line.

Quoted 24 lines
drivers/auxdisplay/tm16xx_core.c new file +24 -0
+				if (ret < 0) {
+					fwnode_handle_put(child);
+					fwnode_handle_put(leds_node);
+					return ret;
+				}
+
+				led->grid = reg[0];
+				led->segment = reg[1];
+				max_grid = umax(max_grid, led->grid);
+				max_segment = umax(max_segment, led->segment);
+				i++;
+			}
+		}
+
+		fwnode_handle_put(leds_node);
+	}
+
+	if (max_grid >= display->controller->max_grids) {
+		dev_err(dev, "grid %u exceeds controller max_grids %u\n",
+			max_grid, display->controller->max_grids);
+		return -EINVAL;
+	}
+
+	if (max_segment >= display->controller->max_segments) {

drivers/auxdisplay/tm16xx_core.c line 480

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+		dev_err(dev, "segment %u exceeds controller max_segments %u\n",
+			max_segment, display->controller->max_segments);
+		return -EINVAL;
+	}
+
+	display->num_grids = max_grid + 1;
+	display->num_segments = max_segment + 1;
 
+	dev_dbg(dev, "Number of grids: %u\n", display->num_grids);
+	dev_dbg(dev, "Number of segments: %u\n", display->num_segments);

I didn't get this. You mean that they are not strictly 7-segment ones?

Quoted 3 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+
+	return 0;
+}

...

drivers/auxdisplay/tm16xx_core.c line 497

Quoted 7 lines
drivers/auxdisplay/tm16xx_core.c new file +7 -0
+int tm16xx_probe(struct tm16xx_display *display)
+{
+	struct device *dev = display->dev;
+	struct led_classdev *main = &display->main_led;
+	struct fwnode_handle *leds_node, *child;
+	unsigned int nbits;
+	int ret, i;

Why is i signed?

drivers/auxdisplay/tm16xx_core.c line 509

Quoted 11 lines
drivers/auxdisplay/tm16xx_core.c new file +10 -0
+	dev_dbg(dev, "Probing device\n");
+	ret = tm16xx_parse_dt(dev, display);
+	if (ret < 0)
+		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;
 
+	mutex_init(&display->lock);

devm_mutex_init()

drivers/auxdisplay/tm16xx_core.c line 518

Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+	INIT_WORK(&display->flush_init, tm16xx_display_flush_init);
+	INIT_WORK(&display->flush_display, tm16xx_display_flush_data);
 
+	/* Initialize main LED properties */
+	if (dev->of_node)
+		main->name = dev->of_node->name;
+	if (!main->name)
+		main->name = "display";
+	device_property_read_string(dev, "label", &main->name);

My gosh. This is done in the LED core if we even need this...

Quoted 58 lines
drivers/auxdisplay/tm16xx_core.c new file +57 -0
+	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->groups = tm16xx_main_led_groups;
+	main->flags = LED_RETAIN_AT_SHUTDOWN | LED_CORE_SUSPENDRESUME;
+
+	ret = led_classdev_register(dev, main);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to register main LED\n");
 
+	i = 0;
+	leds_node = device_get_named_child_node(dev, "leds");
+	fwnode_for_each_child_node(leds_node, child) {
+		struct tm16xx_led *led = &display->leds[i];
+		struct led_init_data led_init = {
+			.fwnode = child,
+			.devicename = dev_name(main->dev),
+			.devname_mandatory = true,
+			.default_label = "led",
+		};
+		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 < 0) {
+			fwnode_handle_put(child);
+			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 < 0) {
+		dev_err_probe(dev, ret, "Failed to initialize display\n");
+		goto unregister_leds;
+	}
+
+	return 0;
+
+unregister_leds:
+	while (i--)
+		led_classdev_unregister(&display->leds[i].cdev);
+
+	led_classdev_unregister(main);
+	return ret;
+}

...

drivers/auxdisplay/tm16xx_core.c line 589

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+void tm16xx_remove(struct tm16xx_display *display)
+{
+	unsigned int nbits = tm16xx_led_nbits(display);
+	struct tm16xx_led *led;
 
+	dev_dbg(display->dev, "Removing display\n");

Unneeded noise.

drivers/auxdisplay/tm16xx_core.c line 596

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+	/*
+	 * 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++) {

Why iterator is signed?

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+		led = &display->leds[i];
+		led_classdev_unregister(&led->cdev);
+	}
+	led_classdev_unregister(&display->main_led);
+
+	/* Clear display state */

drivers/auxdisplay/tm16xx_core.c line 612

Quoted 10 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+	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);
 
+	dev_info(display->dev, "Display turned off\n");

Unneeded noise.

Quoted 1 line

+}

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#14 lore raw

Le 25 août 2025 11 h 14 min 21 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 20 lines · Jean-François Lessard
Quoted 8 lines

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).

...

Quoted 7 lines

Notes:
checkpatch reports false positives that are intentionally ignored:
DEVICE_ATTR_FUNCTIONS: Functions are correctly prefixed with driver name (tm16xx_*) following standard kernel practice for device attribute functions to avoid namespace conflicts.
BIT_MACRO: bit shifts are used for field values while GENMASK/BIT are used for bit positions per semantic convention

Quoted 6 lines
Quoted 3 lines

include <linux/of.h> is required for the default name of the main led device, excluding the unit address, as implemented in drivers/leds/led-core.c which relies on of_node->name

Sorry, but I do not see how of.h is related to all this... Please, drop it.

This relates to the LED subsystem integration question below. I'll address both together and remove the include if not needed.

Quoted 10 lines
Quoted 5 lines

LED registration uses non-devm variant on-purpose to allow explicit unregistration on device removal, ensuring LED triggers are immediately stopped. This prevents stale LED trigger activity from continuing after the hardware is gone, avoiding the need for complex state tracking in brightness callbacks.

...

Quoted 1 line

+What: /sys/class/leds/<led>/value

Quoted 9 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+Date:		August 2025
+KernelVersion:	6.17

The Date should be approximate date of the kernel release (alternatively, -rc1 of that). The version is estimated version where ABI can be found first.

Both of these need to be changed.

Given that 6.17-rc3 was just released, should I target 6.18 for the kernel version and use a March 2025 date for the estimated release timeframe?

Quoted 48 lines
Quoted 48 lines
Documentation/ABI/testing/sysfs-class-leds-tm16xx new file +48 -0
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Controls the text displayed on the TM16xx 7-segment display.
+
+		Reading returns the current display content as ASCII characters,
+		one character per digit position, followed by a newline.
+
+		Writing sets new display content. Input characters are mapped
+		to 7-segment patterns using the configured character map. The
+		string length should not exceed the number of available digits
+		(see num_digits). Shorter strings will clear remaining digits.
+
+		Example:
+		  echo "1234" > value    # Display "1234"
+		  cat value              # Returns "1234\n"
+
+What:		/sys/class/leds/<led>/num_digits
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Read-only attribute showing the number of 7-segment digit
+		positions available on this TM16xx display controller.
+
+		The value is determined by the device tree configuration
+		and represents the maximum length for the 'value' attribute.
+
+		Example:
+		  cat num_digits         # Returns "4\n" for 4-digit display
+
+What:		/sys/class/leds/<led>/map_seg7
+Date:		August 2025
+KernelVersion:	6.17
+Contact:	Jean-François Lessard <[email protected]>
+Description:
+		Read/write binary blob representing the ASCII-to-7-segment
+		display conversion table used by the TM16xx driver, as defined
+		by struct seg7_conversion_map in <linux/map_to_7segment.h>.
+
+		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 auxdisplay and 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
Quoted 9 lines

Documentation/ABI/testing/sysfs-class-leds-tm16xx line 56

Quoted 5 lines
Documentation/ABI/testing/sysfs-class-leds-tm16xx new file +5 -0
+		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

So, the driver is under auxdisplay, but at the same time it completely relies on LED subsystem... What's going on here?

The design integrates with the LED subsystem for two main reasons:

1. Brightness control:
The entire display brightness is controlled at the display level (individual LED icons can only be on/off via their brightness attributes). The LED subsystem provides established mechanisms for this.

2. Coherent sysfs interface:
This provides consistent /sys/class/leds/display for display-level controls and /sys/class/leds/display::{function} for individual icons.

I'm seeking your guidance on the best design for the auxdisplay subsystem.

Quoted 4 lines

Btw, have you seen
https://lore.kernel.org/linux-leds/[email protected]/
? And if so, what're your takeaways? (Yes, I know that's about different HW)

I've read the thread but I'm not clear on the specific point you're making. Could you clarify what aspect I should focus on?
(Though, my personal opinion is that using auxdisplay for keyboard LEDs doesn't really make sense. I think it would be better to properly implement it the required mechanism into input subsystem, with maybe some integration with the leds subsystem. Just a quick opinion, I do not master all aspects of this question at all.)

Quoted 9 lines
Quoted 1 line

+Users: Display configuration utilities and embedded system scripts/tools.

...

Quoted 1 line

+ * Copyright (C) 2024 Jean-François Lessard

My calendar shows something different.

The original code was developed in 2024, though it's being submitted in 2025.

Quoted 6 lines

drivers/auxdisplay/tm16xx.h line 13

Quoted 3 lines
drivers/auxdisplay/tm16xx.h new file +2 -0
+#include <linux/bitfield.h>
 
+#include <linux/bitmap.h>

Is this used?

Yes, display->state is a bitmap. I'll move this include to tm16xx_core.c since it's not used in the header itself.

Quoted 9 lines
Quoted 2 lines
drivers/auxdisplay/tm16xx.h new file +2 -0
+#include <linux/leds.h>
+#include <linux/workqueue.h>

...

Quoted 1 line

+#define TM16XX_DIGIT_SEGMENTS 7

Why do we even need this?

You're right. I'll move it to tm16xx_core.c.

Quoted 7 lines

...

drivers/auxdisplay/tm16xx.h line 101

Quoted 2 lines
drivers/auxdisplay/tm16xx.h new file +2 -0
+#define TM16XX_CTRL_BRIGHTNESS(on, val, prfx) \
+	((on) ? (FIELD_PREP(prfx##_CTRL_BR_MASK, (val)) | prfx##_CTRL_ON) : 0)

prefix can be spelled fully, going slightly over 80 is not a crime.

Acknowledged, I'll use the full prefix name.

Quoted 6 lines

...

Quoted 3 lines
drivers/auxdisplay/tm16xx.h new file +3 -0
+struct tm16xx_display {
+	struct device *dev;
+	const struct tm16xx_controller *controller;
Quoted 8 lines

drivers/auxdisplay/tm16xx.h line 158

Quoted 4 lines
drivers/auxdisplay/tm16xx.h new file +4 -0
+	union {
+		struct i2c_client *i2c;
+		struct spi_device *spi;
+	} client;

Why? Just drop it. struct device *dev is enough and I can't see the need in this at all.

I'll remove this union and use container_of(dev, struct i2c_client, dev) or container_of(dev, struct spi_device, dev) where the specific client type is needed.

Quoted 17 lines
Quoted 14 lines
drivers/auxdisplay/tm16xx.h new file +14 -0
+	u8 *spi_buffer;
+	u8 num_grids;
+	u8 num_segments;
+	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;
+};

...

Quoted 4 lines
Quoted 1 line

+ * Copyright (C) 2024 Jean-François Lessard

Year?

Same as above.

Quoted 9 lines

...

drivers/auxdisplay/tm16xx_core.c line 12

Quoted 4 lines
drivers/auxdisplay/tm16xx_core.c new file +4 -0
+#include <linux/map_to_7segment.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/property.h>

Please, follow IWYU principle.

I'll explicitly include all required headers in each source file instead of relying on transitive includes from the header.

Quoted 12 lines

...

drivers/auxdisplay/tm16xx_core.c line 267

Quoted 7 lines
drivers/auxdisplay/tm16xx_core.c new file +7 -0
+static ssize_t tm16xx_num_digits_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct tm16xx_display *display = dev_get_drvdata(led_cdev->dev->parent);
+
+	return sprintf(buf, "%u\n", display->num_digits);

Should be sysfs_emit().

Well received.

Quoted 4 lines
Quoted 1 line

+}

...

Quoted 9 lines

drivers/auxdisplay/tm16xx_core.c line 283

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+static ssize_t tm16xx_map_seg7_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	memcpy(buf, &map_seg7, sizeof(map_seg7));
+	return sizeof(map_seg7);
+}

Can we use LINEDISP library?

I considered this but have two concerns:

1. It creates attributes under a virtual "linedisp.{n}" device, which conflicts with the coherent LED sysfs design

2. Messages scroll indefinitely. There should be control for single-pass scrolling

I'm willing to contribute improvements to line-display if needed, but this depends on resolving the main LED design question above.

Quoted 11 lines

...

drivers/auxdisplay/tm16xx_core.c line 312

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+static struct attribute *tm16xx_main_led_attrs[] = {
+	&dev_attr_value.attr,
+	&dev_attr_num_digits.attr,
+	&dev_attr_map_seg7.attr,
 
+	NULL,

No comma in the terminator entry.

Well received.

Quoted 5 lines
Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+};
+ATTRIBUTE_GROUPS(tm16xx_main_led);

...

Quoted 9 lines

drivers/auxdisplay/tm16xx_core.c line 326

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +4 -0
+static int tm16xx_display_init(struct tm16xx_display *display)
+{
+	unsigned int nbits = tm16xx_led_nbits(display);
 
+	dev_dbg(display->dev, "Initializing display\n");

Please, drop all these dev_dbg() over the code as they are close to useless, use tracers and other mechanisms to debug if required.

Understood, I'll remove the debug noise.

Quoted 4 lines

Also drop unneeded kernel-doc for the (esp. static) functions that have well established meaning (e.g., no need a kernel-doc for device attributes as they should be documented in the ABI).

Understood, I'll remove these kernel-doc.

Quoted 25 lines
Quoted 22 lines
drivers/auxdisplay/tm16xx_core.c new file +22 -0
+	schedule_work(&display->flush_init);
+	flush_work(&display->flush_init);
+	if (display->flush_status < 0)
+		return display->flush_status;
+
+	if (tm16xx_init_value) {
+		tm16xx_value_store(display->main_led.dev, NULL,
+				   tm16xx_init_value,
+				   strlen(tm16xx_init_value));
+	} else {
+		bitmap_fill(display->state, nbits);
+		schedule_work(&display->flush_display);
+		flush_work(&display->flush_display);
+		bitmap_zero(display->state, nbits);
+		if (display->flush_status < 0)
+			return display->flush_status;
+	}
+
+	dev_info(display->dev, "Display turned on\n");
+
+	return 0;
+}

...

Quoted 5 lines
Quoted 1 line

+static int tm16xx_parse_dt(struct device *dev, struct tm16xx_display *display)

Why DT only? No support for other platforms? Why? I think this is just matter of naming the function properly.

You're right, I'll rename it to tm16xx_parse_fwnode since it uses fwnode APIs.

Quoted 9 lines

drivers/auxdisplay/tm16xx_core.c line 362

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+{
+	struct fwnode_handle *leds_node, *digits_node, *child;
+	struct tm16xx_led *led;
+	struct tm16xx_digit *digit;
 
+	int max_grid = 0, max_segment = 0;

Why signed?

My oversight - I'll change these to appropriate unsigned types.

Quoted 4 lines

drivers/auxdisplay/tm16xx_core.c line 363

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+	int ret, i, j;

Why are i and j signed?

Standard kernel practice uses int for simple loop counters. But I will change to unsigned types for consistency.

Quoted 7 lines
Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+	u32 segments[TM16XX_DIGIT_SEGMENTS * 2];
+	u32 reg[2];
+
+	/* parse digits */
+	digits_node = device_get_named_child_node(dev, "digits");
+	if (digits_node) {
Quoted 6 lines

drivers/auxdisplay/tm16xx_core.c line 372

Quoted 3 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+		display->num_digits = 0;
+		fwnode_for_each_child_node(digits_node, child)
+			display->num_digits++;

Don't we have a _count API for this?

I'll use device_get_child_node_count() instead of manual counting loops.

Quoted 12 lines

drivers/auxdisplay/tm16xx_core.c line 381

Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+		dev_dbg(dev, "Number of digits: %u\n", display->num_digits);
+
+		if (display->num_digits) {
+			display->digits = devm_kcalloc(dev, display->num_digits,
+						       sizeof(*display->digits),
+						       GFP_KERNEL);
+			if (!display->digits) {
 
+				fwnode_handle_put(digits_node);

Use RAII instead, we have defined __free() method for this.

Quoted 8 lines

drivers/auxdisplay/tm16xx_core.c line 386

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+				return -ENOMEM;
+			}
+
+			i = 0;
+			fwnode_for_each_child_node(digits_node, child) {

Ditto. Use _scoped variant.

Well received.

Quoted 7 lines

drivers/auxdisplay/tm16xx_core.c line 390

Quoted 4 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+				digit = &display->digits[i];
 
+				ret = fwnode_property_read_u32(child, "reg",
+							       reg);

One line.

Well received.

Quoted 5 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+				if (ret < 0) {

Can it be positive? Here and everywhere else, if there is no positive return, use 'if (ret)'.

I'll change error checks to if (ret) where functions only return 0 on success or negative on error.

Quoted 19 lines
Quoted 18 lines
drivers/auxdisplay/tm16xx_core.c new file +18 -0
+					fwnode_handle_put(child);
+					fwnode_handle_put(digits_node);
+					return ret;
+				}
+
+				ret = fwnode_property_read_u32_array(child,
+								     "segments",
+								     segments,
+								     TM16XX_DIGIT_SEGMENTS * 2);
+				if (ret < 0) {
+					fwnode_handle_put(child);
+					fwnode_handle_put(digits_node);
+					return ret;
+				}
+
+				for (j = 0; j < TM16XX_DIGIT_SEGMENTS; ++j) {
+					digit->segments[j].grid = segments[2 * j];
+					digit->segments[j].segment = segments[2 * j + 1];
Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 410

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+					max_grid = umax(max_grid,

Firstly, the variables made signed and then specifically force them to be unsigned in the macro. Weird. Can we make them to be a proper type and use max()?

Will change to unsigned types per above.

Quoted 4 lines

drivers/auxdisplay/tm16xx_core.c line 411

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+							digit->segments[j].grid);

One line

Well received.

Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 413

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+					max_segment = umax(max_segment,
+							   digit->segments[j].segment);

As per above comments.

Will change to unsigned types per above.

Quoted 9 lines
Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+				}
+				digit->value = 0;
+				i++;
+			}
+
+			fwnode_handle_put(digits_node);
+		}
+	}
+
Quoted 9 lines

drivers/auxdisplay/tm16xx_core.c line 428

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+	/* parse leds */
+	leds_node = device_get_named_child_node(dev, "leds");
+	if (leds_node) {
+		display->num_leds = 0;
+		fwnode_for_each_child_node(leds_node, child)
+			display->num_leds++;

As per above.

I'll use device_get_child_node_count() instead of manual counting loops.

Quoted 15 lines
Quoted 14 lines
drivers/auxdisplay/tm16xx_core.c new file +14 -0
+		dev_dbg(dev, "Number of LEDs: %u\n", display->num_leds);
+
+		if (display->num_leds) {
+			display->leds = devm_kcalloc(dev, display->num_leds,
+						     sizeof(*display->leds),
+						     GFP_KERNEL);
+			if (!display->leds) {
+				fwnode_handle_put(leds_node);
+				return -ENOMEM;
+			}
+
+			i = 0;
+			fwnode_for_each_child_node(leds_node, child) {
+				led = &display->leds[i];
Quoted 6 lines

drivers/auxdisplay/tm16xx_core.c line 446

Quoted 3 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+				ret = fwnode_property_read_u32_array(child,
+								     "reg", reg,
+								     2);

Make it one line.

Well received.

Quoted 32 lines
Quoted 31 lines
drivers/auxdisplay/tm16xx_core.c new file +31 -0
+				if (ret < 0) {
+					fwnode_handle_put(child);
+					fwnode_handle_put(leds_node);
+					return ret;
+				}
+
+				led->grid = reg[0];
+				led->segment = reg[1];
+				max_grid = umax(max_grid, led->grid);
+				max_segment = umax(max_segment, led->segment);
+				i++;
+			}
+		}
+
+		fwnode_handle_put(leds_node);
+	}
+
+	if (max_grid >= display->controller->max_grids) {
+		dev_err(dev, "grid %u exceeds controller max_grids %u\n",
+			max_grid, display->controller->max_grids);
+		return -EINVAL;
+	}
+
+	if (max_segment >= display->controller->max_segments) {
+		dev_err(dev, "segment %u exceeds controller max_segments %u\n",
+			max_segment, display->controller->max_segments);
+		return -EINVAL;
+	}
+
+	display->num_grids = max_grid + 1;
+	display->num_segments = max_segment + 1;
Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 480

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+	dev_dbg(dev, "Number of grids: %u\n", display->num_grids);
+	dev_dbg(dev, "Number of segments: %u\n", display->num_segments);

I didn't get this. You mean that they are not strictly 7-segment ones?

The terminology is confusing - "segment" is used both for 7-segment digits (which are indeed 7-segment) and for controller matrix coordinates (grid,segment) from datasheets. Controllers support varying numbers of segments For individual LED icons, not necessarily related to 7-segment displays. I'll add a comment to clarify this distinction.

Quoted 6 lines
Quoted 3 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+
+	return 0;
+}

...

Quoted 10 lines

drivers/auxdisplay/tm16xx_core.c line 497

Quoted 7 lines
drivers/auxdisplay/tm16xx_core.c new file +7 -0
+int tm16xx_probe(struct tm16xx_display *display)
+{
+	struct device *dev = display->dev;
+	struct led_classdev *main = &display->main_led;
+	struct fwnode_handle *leds_node, *child;
+	unsigned int nbits;
+	int ret, i;

Why is i signed?

Will change to unsigned types per above.

Quoted 10 lines
Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +9 -0
+	dev_dbg(dev, "Probing device\n");
+	ret = tm16xx_parse_dt(dev, display);
+	if (ret < 0)
+		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;
Quoted 4 lines

drivers/auxdisplay/tm16xx_core.c line 509

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+	mutex_init(&display->lock);

devm_mutex_init()

Well received.

Quoted 12 lines

drivers/auxdisplay/tm16xx_core.c line 518

Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+	INIT_WORK(&display->flush_init, tm16xx_display_flush_init);
+	INIT_WORK(&display->flush_display, tm16xx_display_flush_data);
 
+	/* Initialize main LED properties */
+	if (dev->of_node)
+		main->name = dev->of_node->name;
+	if (!main->name)
+		main->name = "display";
+	device_property_read_string(dev, "label", &main->name);

My gosh. This is done in the LED core if we even need this...

This relates to the LED subsystem integration question. If my design approach is acceptable, I'll review the LED core implementation to avoid duplicating this logic if possible.

Quoted 61 lines
Quoted 58 lines
drivers/auxdisplay/tm16xx_core.c new file +57 -0
+	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->groups = tm16xx_main_led_groups;
+	main->flags = LED_RETAIN_AT_SHUTDOWN | LED_CORE_SUSPENDRESUME;
+
+	ret = led_classdev_register(dev, main);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to register main LED\n");
 
+	i = 0;
+	leds_node = device_get_named_child_node(dev, "leds");
+	fwnode_for_each_child_node(leds_node, child) {
+		struct tm16xx_led *led = &display->leds[i];
+		struct led_init_data led_init = {
+			.fwnode = child,
+			.devicename = dev_name(main->dev),
+			.devname_mandatory = true,
+			.default_label = "led",
+		};
+		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 < 0) {
+			fwnode_handle_put(child);
+			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 < 0) {
+		dev_err_probe(dev, ret, "Failed to initialize display\n");
+		goto unregister_leds;
+	}
+
+	return 0;
+
+unregister_leds:
+	while (i--)
+		led_classdev_unregister(&display->leds[i].cdev);
+
+	led_classdev_unregister(main);
+	return ret;
+}

...

Quoted 9 lines

drivers/auxdisplay/tm16xx_core.c line 589

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+void tm16xx_remove(struct tm16xx_display *display)
+{
+	unsigned int nbits = tm16xx_led_nbits(display);
+	struct tm16xx_led *led;
 
+	dev_dbg(display->dev, "Removing display\n");

Unneeded noise.

Well received.

Quoted 9 lines

drivers/auxdisplay/tm16xx_core.c line 596

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+	/*
+	 * 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++) {

Why iterator is signed?

Will change to unsigned types per above.

Quoted 15 lines
Quoted 14 lines
drivers/auxdisplay/tm16xx_core.c new file +14 -0
+		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);
Quoted 4 lines

drivers/auxdisplay/tm16xx_core.c line 612

Quoted 1 line
drivers/auxdisplay/tm16xx_core.c new file +1 -0
+	dev_info(display->dev, "Display turned off\n");

Unneeded noise.

Well received.

Quoted 2 lines
Quoted 1 line

+}

Thank you for the detailed review. The main question remains about the LED subsystem integration approach. I'd appreciate your guidance on the best design direction.

#15 lore raw
Quoted 2 lines · Jean-François Lessard

Le 25 août 2025 11 h 14 min 21 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 1 line · Jean-François Lessard

...

Quoted 10 lines
Quoted 7 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+Date:		August 2025
+KernelVersion:	6.17

The Date should be approximate date of the kernel release (alternatively, -rc1 of that). The version is estimated version where ABI can be found first.

Both of these need to be changed.

Given that 6.17-rc3 was just released, should I target 6.18 for the kernel version and use a March 2025 date for the estimated release timeframe?

6.18
The date is not in the past, obviously. You can consult with this site:
https://hansen.beer/~dave/phb/

...

Quoted 14 lines
Quoted 2 lines

So, the driver is under auxdisplay, but at the same time it completely relies on LED subsystem... What's going on here?

The design integrates with the LED subsystem for two main reasons:

1. Brightness control:
The entire display brightness is controlled at the display level (individual LED icons can only be on/off via their brightness attributes). The LED subsystem provides established mechanisms for this.

2. Coherent sysfs interface:
This provides consistent /sys/class/leds/display for display-level controls and /sys/class/leds/display::{function} for individual icons.

Quoted 8 lines

I'm seeking your guidance on the best design for the auxdisplay subsystem.

Quoted 3 lines

Btw, have you seen
https://lore.kernel.org/linux-leds/[email protected]/
? And if so, what're your takeaways? (Yes, I know that's about different HW)

I've read the thread but I'm not clear on the specific point you're making. Could you clarify what aspect I should focus on?

If you have a LED matrix, perhaps we can consider different approaches as well. (It's all about the current HW, is it a 7-segment or arbitrary display, if the
former, that discussion is unrelated)

Quoted 5 lines

(Though, my personal opinion is that using auxdisplay for keyboard LEDs doesn't really make sense. I think it would be better to properly implement it the required mechanism into input subsystem, with maybe some integration with the leds subsystem. Just a quick opinion, I do not master all aspects of this question at all.)

...

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

+ * Copyright (C) 2024 Jean-François Lessard

My calendar shows something different.

The original code was developed in 2024, though it's being submitted in 2025.

But haven't you changed it in 2025?

...

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

+#include <linux/bitmap.h>

Is this used?

Yes, display->state is a bitmap. I'll move this include to tm16xx_core.c since it's not used in the header itself.

Yes, that's what I meant "used by this header file".

...

Quoted 11 lines
Quoted 7 lines

drivers/auxdisplay/tm16xx.h line 158

Quoted 4 lines
drivers/auxdisplay/tm16xx.h new file +4 -0
+	union {
+		struct i2c_client *i2c;
+		struct spi_device *spi;
+	} client;

Why? Just drop it. struct device *dev is enough and I can't see the need in this at all.

I'll remove this union and use container_of(dev, struct i2c_client, dev) or container_of(dev, struct spi_device, dev) where the specific client type is needed.

This is in correlation with the regmap proposal.

...

Quoted 9 lines
Quoted 8 lines

drivers/auxdisplay/tm16xx_core.c line 283

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+static ssize_t tm16xx_map_seg7_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	memcpy(buf, &map_seg7, sizeof(map_seg7));
+	return sizeof(map_seg7);
+}

Can we use LINEDISP library?

Quoted 4 lines

I considered this but have two concerns:

1. It creates attributes under a virtual "linedisp.{n}" device, which conflicts with the coherent LED sysfs design

It creates the specific attributes for the 7-segment HW, So look at it from this angle. We have well established library and we expect 7-seg drivers will use it to make sure that user space may be written in uniform way.

Quoted 1 line

2. Messages scroll indefinitely. There should be control for single-pass scrolling

If we miss that, add it to linedisp. I wouldn't mind, actually I will be in favour of the development of that library.

Quoted 2 lines

I'm willing to contribute improvements to line-display if needed, but this depends on resolving the main LED design question above.

...

Quoted 7 lines
Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 372

Quoted 3 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+		display->num_digits = 0;
+		fwnode_for_each_child_node(digits_node, child)
+			display->num_digits++;

Don't we have a _count API for this?

I'll use device_get_child_node_count() instead of manual counting loops.

fwnode_get_child_node_count() I assume you meant.

...

Quoted 10 lines
Quoted 4 lines

drivers/auxdisplay/tm16xx_core.c line 480

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+	dev_dbg(dev, "Number of grids: %u\n", display->num_grids);
+	dev_dbg(dev, "Number of segments: %u\n", display->num_segments);

I didn't get this. You mean that they are not strictly 7-segment ones?

The terminology is confusing - "segment" is used both for 7-segment digits (which are indeed 7-segment) and for controller matrix coordinates (grid,segment) from datasheets. Controllers support varying numbers of segments For individual LED icons, not necessarily related to 7-segment displays. I'll add a comment to clarify this distinction.

Hmm... Maybe try to rename these 'segments' to something else, like 'hwseg' (find a better name).

...

Quoted 9 lines
Quoted 8 lines

drivers/auxdisplay/tm16xx_core.c line 518

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+	/* Initialize main LED properties */
+	if (dev->of_node)
+		main->name = dev->of_node->name;
+	if (!main->name)
+		main->name = "display";
+	device_property_read_string(dev, "label", &main->name);

My gosh. This is done in the LED core if we even need this...

Quoted 3 lines

This relates to the LED subsystem integration question. If my design approach is acceptable, I'll review the LED core implementation to avoid duplicating this logic if possible.

I think if you integrate LED for special LED icons and linedisp for 7-segment into a single driver, it's fine. I just can't speak about LED icons case. The 7-seg LGTM (assuming linedisp and other existing APIs/ABIs to use, if required).

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#16 lore raw

Le 26 août 2025 11 h 22 min 56 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 12 lines · Jean-François Lessard
Quoted 2 lines

Le 25 août 2025 11 h 14 min 21 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 1 line · Jean-François Lessard

...

Quoted 5 lines
Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+Date:		August 2025
+KernelVersion:	6.17

The Date should be approximate date of the kernel release (alternatively, -rc1 of that). The version is estimated version where ABI can be found first.

Quoted 9 lines
Quoted 4 lines
Quoted 1 line

Both of these need to be changed.

Given that 6.17-rc3 was just released, should I target 6.18 for the kernel version and use a March 2025 date for the estimated release timeframe?

6.18
The date is not in the past, obviously. You can consult with this site:
https://hansen.beer/~dave/phb/

Thanks for the correction and link. I'll update to 6.18 with appropriate future date.

Quoted 22 lines

...

Quoted 20 lines
Quoted 2 lines

So, the driver is under auxdisplay, but at the same time it completely relies on LED subsystem... What's going on here?

The design integrates with the LED subsystem for two main reasons:

1. Brightness control:
The entire display brightness is controlled at the display level (individual LED icons can only be on/off via their brightness attributes). The LED subsystem provides established mechanisms for this.

2. Coherent sysfs interface:
This provides consistent /sys/class/leds/display for display-level controls and /sys/class/leds/display::{function} for individual icons.

I'm seeking your guidance on the best design for the auxdisplay subsystem.

Quoted 3 lines

Btw, have you seen
https://lore.kernel.org/linux-leds/[email protected]/
? And if so, what're your takeaways? (Yes, I know that's about different HW)

Quoted 7 lines
Quoted 2 lines

I've read the thread but I'm not clear on the specific point you're making. Could you clarify what aspect I should focus on?

If you have a LED matrix, perhaps we can consider different approaches as well. (It's all about the current HW, is it a 7-segment or arbitrary display, if the former, that discussion is unrelated)

TM16xx devices are primarily 7-segment displays with additional LED icon and key scanning capabilities.

Quoted 8 lines
Quoted 5 lines

(Though, my personal opinion is that using auxdisplay for keyboard LEDs doesn't really make sense. I think it would be better to properly implement it the required mechanism into input subsystem, with maybe some integration with the leds subsystem. Just a quick opinion, I do not master all aspects of this question at all.)

...

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line

+ * Copyright (C) 2024 Jean-François Lessard

My calendar shows something different.

The original code was developed in 2024, though it's being submitted in 2025.

But haven't you changed it in 2025?

You're right, I'll update the copyright to 2025.

Quoted 11 lines

...

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

+#include <linux/bitmap.h>

Is this used?

Yes, display->state is a bitmap. I'll move this include to tm16xx_core.c since it's not used in the header itself.

Yes, that's what I meant "used by this header file".

I'll move bitmap.h to the source file.

Quoted 10 lines

...

Quoted 8 lines
Quoted 7 lines

drivers/auxdisplay/tm16xx.h line 158

Quoted 4 lines
drivers/auxdisplay/tm16xx.h new file +4 -0
+	union {
+		struct i2c_client *i2c;
+		struct spi_device *spi;
+	} client;

Why? Just drop it. struct device *dev is enough and I can't see the need in this at all.

Quoted 6 lines
Quoted 3 lines

I'll remove this union and use container_of(dev, struct i2c_client, dev) or container_of(dev, struct spi_device, dev) where the specific client type is needed.

This is in correlation with the regmap proposal.

I'll address the regmap evaluation in the cover letter of the next submission as you requested.

The union removal will be addressed too.

Quoted 13 lines

...

Quoted 11 lines
Quoted 8 lines

drivers/auxdisplay/tm16xx_core.c line 283

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+static ssize_t tm16xx_map_seg7_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	memcpy(buf, &map_seg7, sizeof(map_seg7));
+	return sizeof(map_seg7);
+}

Can we use LINEDISP library?

I considered this but have two concerns:

Quoted 8 lines
Quoted 2 lines

1. It creates attributes under a virtual "linedisp.{n}" device, which conflicts with the coherent LED sysfs design

It creates the specific attributes for the 7-segment HW, So look at it from this angle. We have well established library and we expect 7-seg drivers will use it to make sure that user space may be written in uniform way.

To provide a coherent user interface, I'd like to propose extending linedisp to optionally attach attributes to an existing device rather than always creating virtual devices. This would create a unified /sys/class/leds/{label}/ interface for content, brightness, and icons while maintaining linedisp's established APIs.

If device attachment isn't feasible, could linedisp at minimum accept a device name parameter to create /sys/virtual/linedisp-{label} instead of generic linedisp.{n}?

I'm happy to implement these linedisp enhancements as part of the TM16xx integration if you find the approach acceptable.

Quoted 5 lines
Quoted 1 line

2. Messages scroll indefinitely. There should be control for single-pass scrolling

If we miss that, add it to linedisp. I wouldn't mind, actually I will be in favour of the development of that library.

I'll submit patches to extend linedisp with missing functionality (like single-pass scrolling control) as part of this integration.

Quoted 11 lines
Quoted 2 lines

I'm willing to contribute improvements to line-display if needed, but this depends on resolving the main LED design question above.

...

Quoted 6 lines
Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 372

Quoted 3 lines
drivers/auxdisplay/tm16xx_core.c new file +3 -0
+		display->num_digits = 0;
+		fwnode_for_each_child_node(digits_node, child)
+			display->num_digits++;

Don't we have a _count API for this?

Quoted 4 lines
Quoted 1 line

I'll use device_get_child_node_count() instead of manual counting loops.

fwnode_get_child_node_count() I assume you meant.

Correct, my oversight. I'll use the fwnode variant.

Quoted 7 lines

...

Quoted 5 lines
Quoted 4 lines

drivers/auxdisplay/tm16xx_core.c line 480

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+	dev_dbg(dev, "Number of grids: %u\n", display->num_grids);
+	dev_dbg(dev, "Number of segments: %u\n", display->num_segments);

I didn't get this. You mean that they are not strictly 7-segment ones?

Quoted 9 lines
Quoted 5 lines

The terminology is confusing - "segment" is used both for 7-segment digits (which are indeed 7-segment) and for controller matrix coordinates (grid,segment) from datasheets. Controllers support varying numbers of segments For individual LED icons, not necessarily related to 7-segment displays. I'll add a comment to clarify this distinction.

Hmm... Maybe try to rename these 'segments' to something else, like 'hwseg' (find a better name).

Good point. I'll use a different name to distinguish from 7-segment terminology.

Quoted 11 lines

...

Quoted 9 lines
Quoted 8 lines

drivers/auxdisplay/tm16xx_core.c line 518

Quoted 6 lines
drivers/auxdisplay/tm16xx_core.c new file +6 -0
+	/* Initialize main LED properties */
+	if (dev->of_node)
+		main->name = dev->of_node->name;
+	if (!main->name)
+		main->name = "display";
+	device_property_read_string(dev, "label", &main->name);

My gosh. This is done in the LED core if we even need this...

Quoted 8 lines
Quoted 3 lines

This relates to the LED subsystem integration question. If my design approach is acceptable, I'll review the LED core implementation to avoid duplicating this logic if possible.

I think if you integrate LED for special LED icons and linedisp for 7-segment into a single driver, it's fine. I just can't speak about LED icons case. The 7-seg LGTM (assuming linedisp and other existing APIs/ABIs to use, if required).

I think linedisp is the way to go if we can provide a consistent user space API.

#17 lore raw
In reply to Jean-François Lessard

Le 25 août 2025 13 h 48 min 45 s HAE, "Jean-François Lessard" <[email protected]> a écrit :

Quoted 2 lines

Le 25 août 2025 11 h 14 min 21 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 1 line · Jean-François Lessard

...

Quoted 29 lines
Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 372

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+		fwnode_for_each_child_node(digits_node, child)
+			display->num_digits++;

Don't we have a _count API for this?

I'll use device_get_child_node_count() instead of manual counting loops.

Quoted 12 lines

drivers/auxdisplay/tm16xx_core.c line 381

Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+		dev_dbg(dev, "Number of digits: %u\n", display->num_digits);
+
+		if (display->num_digits) {
+			display->digits = devm_kcalloc(dev, display->num_digits,
+						       sizeof(*display->digits),
+						       GFP_KERNEL);
+			if (!display->digits) {
 
+				fwnode_handle_put(digits_node);

Use RAII instead, we have defined __free() method for this.

Quoted 8 lines

drivers/auxdisplay/tm16xx_core.c line 386

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+				return -ENOMEM;
+			}
+
+			i = 0;
+			fwnode_for_each_child_node(digits_node, child) {

Ditto. Use _scoped variant.

Quoted 2 lines

Well received.

After further investigation, _scoped variant exists for device_for_each_child_node_scoped() but not for fwnode_for_each_child_node().

I suggest to include an additional patch in next submission to add to include/linux/property.h:

#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_named_child_node_scoped(fwnode, child, name)	\
	fwnode_for_each_child_node_scoped(fwnode, child)		\
		for_each_if(fwnode_name_eq(child, name))
#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))

...

#18 lore raw
Quoted 3 lines · Jean-François Lessard

Le 25 août 2025 13 h 48 min 45 s HAE, "Jean-François Lessard" <[email protected]> a écrit :

Quoted 2 lines

Le 25 août 2025 11 h 14 min 21 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 1 line · Jean-François Lessard

...

Quoted 41 lines
Quoted 28 lines
Quoted 5 lines

drivers/auxdisplay/tm16xx_core.c line 372

Quoted 2 lines
drivers/auxdisplay/tm16xx_core.c new file +2 -0
+		fwnode_for_each_child_node(digits_node, child)
+			display->num_digits++;

Don't we have a _count API for this?

I'll use device_get_child_node_count() instead of manual counting loops.

Quoted 12 lines

drivers/auxdisplay/tm16xx_core.c line 381

Quoted 9 lines
drivers/auxdisplay/tm16xx_core.c new file +8 -0
+		dev_dbg(dev, "Number of digits: %u\n", display->num_digits);
+
+		if (display->num_digits) {
+			display->digits = devm_kcalloc(dev, display->num_digits,
+						       sizeof(*display->digits),
+						       GFP_KERNEL);
+			if (!display->digits) {
 
+				fwnode_handle_put(digits_node);

Use RAII instead, we have defined __free() method for this.

Quoted 7 lines

drivers/auxdisplay/tm16xx_core.c line 386

Quoted 5 lines
drivers/auxdisplay/tm16xx_core.c new file +5 -0
+				return -ENOMEM;
+			}
+
+			i = 0;
+			fwnode_for_each_child_node(digits_node, child) {

Ditto. Use _scoped variant.

Quoted 1 line

Well received.

After further investigation, _scoped variant exists for device_for_each_child_node_scoped() but not for fwnode_for_each_child_node().

I suggest to include an additional patch in next submission to add to include/linux/property.h:

#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))
Quoted 8 lines
#define fwnode_for_each_named_child_node_scoped(fwnode, child, name)	\
	fwnode_for_each_child_node_scoped(fwnode, child)		\
		for_each_if(fwnode_name_eq(child, name))
#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))

LGTM!

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#19 lore raw

PATCH v4 4/6 auxdisplay: TM16xx: Add keypad support for scanning matrix keys

In reply to Jean-François Lessard

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.

---

Notes:
checkpatch reports false positives that are intentionally ignored:
COMPLEX_MACRO/MACRO_ARG_REUSE for tm16xx_for_each_key(): This is a standard iterator pattern following kernel conventions (similar to for_each_* macros throughout the kernel). The nested for loops are the correct implementation for matrix iteration.

 MAINTAINERS                        |   1 +
 drivers/auxdisplay/Kconfig         |  11 +-
 drivers/auxdisplay/Makefile        |   1 +
 drivers/auxdisplay/tm16xx.h        |  27 ++++
 drivers/auxdisplay/tm16xx_core.c   |   4 +
 drivers/auxdisplay/tm16xx_keypad.c | 208 +++++++++++++++++++++++++++++
 6 files changed, 251 insertions(+), 1 deletion(-)
 create mode 100644 drivers/auxdisplay/tm16xx_keypad.c
MAINTAINERS +1 -0
index 0ed971881..8edcdd52c 100644
@@ -25409,6 +25409,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 <[email protected]>
drivers/auxdisplay/Kconfig +10 -1
index 7b58c6cc8..b5dcd024d 100644
@@ -527,11 +527,14 @@ config SEG_LED_GPIO
 	  will be called seg-led-gpio.
 
 config TM16XX
-	tristate "TM16XX-compatible 7-segment LED controllers"
+	tristate "TM16XX-compatible 7-segment LED controllers with keyscan"
 	depends on SPI || I2C
+	depends on INPUT
 	select NEW_LEDS
 	select LEDS_CLASS
 	select LEDS_TRIGGERS
+	select INPUT_MATRIXKMAP
+	select TM16XX_KEYPAD if (INPUT)
 	help
 	  This driver supports the following TM16XX compatible
 	  I2C and SPI 7-segment led display chips:
@@ -544,6 +547,12 @@ config TM16XX
 	  This driver can also be built as a module. If so, the module
 	  will be called tm16xx.
 
+config TM16XX_KEYPAD
+	bool
+	depends on TM16XX
+	help
+	  Enable keyscan support for TM16XX driver.
+
 #
 # Character LCD with non-conforming interface section
 #
drivers/auxdisplay/Makefile +1 -0
index 7ecf3cd4a..a9b9c8ff0 100644
@@ -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
drivers/auxdisplay/tm16xx.h +27 -0
index a7ce483d3..65e2511cd 100644
@@ -104,6 +104,7 @@
 struct tm16xx_display;
 struct tm16xx_digit;
 struct tm16xx_led;
+struct tm16xx_keypad;
 
 /**
  * DOC: struct tm16xx_controller - Controller-specific operations and limits
@@ -136,6 +137,7 @@ struct tm16xx_controller {
  * @controller: Controller-specific function table and limits.
  * @client: Union of I2C and SPI client pointers.
  * @spi_buffer: DMA-safe buffer for SPI transactions, or NULL for I2C.
+ * @keypad: Opaque pointer to tm16xx_keypad struct.
  * @num_grids: Number of controller grids in use.
  * @num_segments: Number of controller segments in use.
  * @main_led: LED class device for the entire display.
@@ -157,6 +159,7 @@ struct tm16xx_display {
 		struct spi_device *spi;
 	} client;
 	u8 *spi_buffer;
+	struct tm16xx_keypad *keypad;
 	u8 num_grids;
 	u8 num_segments;
 	struct led_classdev main_led;
@@ -174,4 +177,28 @@ 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 u8 row,
+		    const u8 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 u8 row, const u8 col,
+				  const bool pressed)
+{
+}
+#endif
+
+#define tm16xx_for_each_key(display, _r, _c) \
+	for (unsigned int _r = 0; \
+	     _r < (display)->controller->max_key_rows; _r++) \
+		for (unsigned int _c = 0; \
+		     _c < (display)->controller->max_key_cols; _c++)
+
 #endif /* _TM16XX_H */
drivers/auxdisplay/tm16xx_core.c +4 -0
index 415be7747..e21c41a09 100644
@@ -566,6 +566,10 @@ int tm16xx_probe(struct tm16xx_display *display)
 		goto unregister_leds;
 	}
 
+	ret = tm16xx_keypad_probe(display);
+	if (ret < 0)
+		dev_warn(dev, "Failed to initialize keypad: %d\n", ret);
+
 	return 0;
 
 unregister_leds:
drivers/auxdisplay/tm16xx_keypad.c new file +208 -0

208 lines not on this page · raw message

Signature · 1 line
2.43.0
#20 lore raw

PATCH v4 5/6 auxdisplay: TM16xx: Add support for I2C-based controllers

In reply to Jean-François Lessard

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.

---
 MAINTAINERS                     |   1 +
 drivers/auxdisplay/Kconfig      |   7 +
 drivers/auxdisplay/Makefile     |   1 +
 drivers/auxdisplay/tm16xx_i2c.c | 332 ++++++++++++++++++++++++++++++++
 4 files changed, 341 insertions(+)
 create mode 100644 drivers/auxdisplay/tm16xx_i2c.c
MAINTAINERS +1 -0
index 8edcdd52c..51cc910e2 100644
@@ -25409,6 +25409,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
drivers/auxdisplay/Kconfig +7 -0
index b5dcd024d..6238e753d 100644
@@ -535,6 +535,7 @@ config TM16XX
 	select LEDS_TRIGGERS
 	select INPUT_MATRIXKMAP
 	select TM16XX_KEYPAD if (INPUT)
+	select TM16XX_I2C if (I2C)
 	help
 	  This driver supports the following TM16XX compatible
 	  I2C and SPI 7-segment led display chips:
@@ -553,6 +554,12 @@ config TM16XX_KEYPAD
 	help
 	  Enable keyscan support for TM16XX driver.
 
+config TM16XX_I2C
+	tristate
+	depends on TM16XX
+	help
+	  Enable I2C support for TM16XX driver.
+
 #
 # Character LCD with non-conforming interface section
 #
drivers/auxdisplay/Makefile +1 -0
index a9b9c8ff0..ba7b310f5 100644
@@ -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
drivers/auxdisplay/tm16xx_i2c.c new file +332 -0

332 lines not on this page · raw message

Signature · 1 line
2.43.0
#21 lore raw

Re: [PATCH v4 5/6] auxdisplay: TM16xx: Add support for I2C-based controllers

Quoted 11 lines · Jean-François Lessard

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.

...

drivers/auxdisplay/tm16xx_i2c.c line 10

Quoted 2 lines
drivers/auxdisplay/tm16xx_i2c.c new file +2 -0
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>

IWYU everywhere, too little header inclusions, you use much more.

drivers/auxdisplay/tm16xx_i2c.c line 58

Quoted 4 lines
drivers/auxdisplay/tm16xx_i2c.c new file +3 -0
+static int tm16xx_i2c_write(struct tm16xx_display *display, u8 *data, size_t len)
+{
 
+	dev_dbg(display->dev, "i2c_write %*ph", (char)len, data);

Noise.

Quoted 4 lines
drivers/auxdisplay/tm16xx_i2c.c new file +4 -0
+	/* expected sequence: S Command [A] Data [A] P */
+	struct i2c_msg msg = {
+		.addr = data[0] >> 1,
+		.flags = 0,

drivers/auxdisplay/tm16xx_i2c.c line 73

Quoted 10 lines
drivers/auxdisplay/tm16xx_i2c.c new file +10 -0
+		.len = len - 1,
+		.buf = &data[1],
+	};
+	int ret;
+
+	ret = i2c_transfer(display->client.i2c->adapter, &msg, 1);
+	if (ret < 0)
+		return ret;
+
+	return (ret == 1) ? 0 : -EIO;

Can we use regmap for all parts of the driver? Why not?

Quoted 1 line

+}

...

drivers/auxdisplay/tm16xx_i2c.c line 283

Quoted 10 lines
drivers/auxdisplay/tm16xx_i2c.c new file +9 -0
+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,
 
+	.keys = NULL,

Redundant initialiser.

Quoted 1 line

+};

...

Quoted 1 line

+#if IS_ENABLED(CONFIG_OF)

No, please remove all these ugly ifdefferies.

Quoted 9 lines
drivers/auxdisplay/tm16xx_i2c.c new file +9 -0
+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);
+#endif

...

drivers/auxdisplay/tm16xx_i2c.c line 320

Quoted 1 line
drivers/auxdisplay/tm16xx_i2c.c new file +1 -0
+		.of_match_table = of_match_ptr(tm16xx_i2c_of_match),

Definitely no to of_match_ptr(). Must be not used in a new code.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#22 lore raw

Re: [PATCH v4 5/6] auxdisplay: TM16xx: Add support for I2C-based controllers

Le 25 août 2025 11 h 18 min 27 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 13 lines · Jean-François Lessard
Quoted 11 lines

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.

Quoted 7 lines

...

drivers/auxdisplay/tm16xx_i2c.c line 10

Quoted 2 lines
drivers/auxdisplay/tm16xx_i2c.c new file +2 -0
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>

IWYU everywhere, too little header inclusions, you use much more.

I'll explicitly include all required headers in each source file instead of relying on transitive includes from the header.

Quoted 7 lines

drivers/auxdisplay/tm16xx_i2c.c line 58

Quoted 4 lines
drivers/auxdisplay/tm16xx_i2c.c new file +3 -0
+static int tm16xx_i2c_write(struct tm16xx_display *display, u8 *data, size_t len)
+{
 
+	dev_dbg(display->dev, "i2c_write %*ph", (char)len, data);

Noise.

Understood, I'll remove the debug noise.

Quoted 8 lines
Quoted 8 lines
drivers/auxdisplay/tm16xx_i2c.c new file +8 -0
+	/* 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;
Quoted 9 lines

drivers/auxdisplay/tm16xx_i2c.c line 73

Quoted 6 lines
drivers/auxdisplay/tm16xx_i2c.c new file +6 -0
+
+	ret = i2c_transfer(display->client.i2c->adapter, &msg, 1);
+	if (ret < 0)
+		return ret;
+
+	return (ret == 1) ? 0 : -EIO;

Can we use regmap for all parts of the driver? Why not?

These controllers implement custom 2-wire/3-wire protocols that share sufficient commonalities with I2C/SPI to leverage those subsystems, but are not fully compliant with standard register-based access patterns.

Specific regmap incompatibilities:

I2C protocol:
- Dynamic addressing: slave address embedded in command byte (data[0] >> 1)
- Custom message flags: requires I2C_M_NO_RD_ACK for reads

SPI protocol:
- Inter-transfer timing: mandatory TM16XX_SPI_TWAIT_US delay between command/data
- CS control: requires cs_change = 0 to maintain assertion across phases

Regmap's I2C/SPI bus implementations use fixed addressing and standard transfer patterns without support for these protocol-specific requirements. A custom regmap bus would internally call these same helper functions without providing practical benefit.

The explicit transfer approach better reflects the actual hardware protocol requirements.

Quoted 13 lines
Quoted 1 line

+}

...

Quoted 8 lines
drivers/auxdisplay/tm16xx_i2c.c new file +8 -0
+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,
Quoted 4 lines

drivers/auxdisplay/tm16xx_i2c.c line 283

Quoted 1 line
drivers/auxdisplay/tm16xx_i2c.c new file +1 -0
+	.keys = NULL,

Redundant initialiser.

Acknowledged. Will remove.

Quoted 8 lines
Quoted 1 line

+};

...

Quoted 1 line

+#if IS_ENABLED(CONFIG_OF)

No, please remove all these ugly ifdefferies.

Acknowledged. Will remove.

Quoted 12 lines
Quoted 9 lines
drivers/auxdisplay/tm16xx_i2c.c new file +9 -0
+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);
+#endif

...

Quoted 4 lines

drivers/auxdisplay/tm16xx_i2c.c line 320

Quoted 1 line
drivers/auxdisplay/tm16xx_i2c.c new file +1 -0
+		.of_match_table = of_match_ptr(tm16xx_i2c_of_match),

Definitely no to of_match_ptr(). Must be not used in a new code.

Well received. Will ban usage of of_match_ptr.

#23 lore raw

Re: [PATCH v4 5/6] auxdisplay: TM16xx: Add support for I2C-based controllers

Quoted 2 lines · Jean-François Lessard

Le 25 août 2025 11 h 18 min 27 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 1 line · Jean-François Lessard

...

Quoted 10 lines
Quoted 1 line

Can we use regmap for all parts of the driver? Why not?

These controllers implement custom 2-wire/3-wire protocols that share sufficient commonalities with I2C/SPI to leverage those subsystems, but are not fully compliant with standard register-based access patterns.

Specific regmap incompatibilities:

I2C protocol:
- Dynamic addressing: slave address embedded in command byte (data[0] >> 1)

Isn't this called paging? Or actually we have also non-standard (non-power-of-2) regmap implementations, perhaps one of them (7 + 9) if exists is what you need?

Quoted 1 line

- Custom message flags: requires I2C_M_NO_RD_ACK for reads

Hmm... If we have more than one device like this, we might implement the support in regmap. Or, perhaps, the custom regmap IO accessors can solve this.

Quoted 3 lines

SPI protocol:
- Inter-transfer timing: mandatory TM16XX_SPI_TWAIT_US delay between command/data

One may implement custom regmap IO accessors.

Quoted 6 lines

- CS control: requires cs_change = 0 to maintain assertion across phases

Regmap's I2C/SPI bus implementations use fixed addressing and standard transfer patterns without support for these protocol-specific requirements. A custom regmap bus would internally call these same helper functions without providing practical benefit.

regmap provides a few benefits on top of the raw implementations. First of all, it takes care about synchronisation (and as a side effect enables configurations of the multi-functional HW, if ever needed in this case). It also gives a debugfs implementation, and paging support (if it's what we need). And many more...

Quoted 2 lines

The explicit transfer approach better reflects the actual hardware protocol requirements.

That said, please, try to look into it closer.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#24 lore raw

Re: [PATCH v4 5/6] auxdisplay: TM16xx: Add support for I2C-based controllers

Le 26 août 2025 11 h 30 min 41 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 45 lines · Jean-François Lessard
Quoted 2 lines

Le 25 août 2025 11 h 18 min 27 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 1 line · Jean-François Lessard

...

Quoted 10 lines
Quoted 1 line

Can we use regmap for all parts of the driver? Why not?

These controllers implement custom 2-wire/3-wire protocols that share sufficient commonalities with I2C/SPI to leverage those subsystems, but are not fully compliant with standard register-based access patterns.

Specific regmap incompatibilities:

I2C protocol:
- Dynamic addressing: slave address embedded in command byte (data[0] >> 1)

Isn't this called paging? Or actually we have also non-standard (non-power-of-2) regmap implementations, perhaps one of them (7 + 9) if exists is what you need?

Quoted 1 line

- Custom message flags: requires I2C_M_NO_RD_ACK for reads

Hmm... If we have more than one device like this, we might implement the support in regmap. Or, perhaps, the custom regmap IO accessors can solve this.

Quoted 3 lines

SPI protocol:
- Inter-transfer timing: mandatory TM16XX_SPI_TWAIT_US delay between command/data

One may implement custom regmap IO accessors.

Quoted 6 lines

- CS control: requires cs_change = 0 to maintain assertion across phases

Regmap's I2C/SPI bus implementations use fixed addressing and standard transfer patterns without support for these protocol-specific requirements. A custom regmap bus would internally call these same helper functions without providing practical benefit.

regmap provides a few benefits on top of the raw implementations. First of all, it takes care about synchronisation (and as a side effect enables configurations of the multi-functional HW, if ever needed in this case). It also gives a debugfs implementation, and paging support (if it's what we need). And many more...

Quoted 5 lines
Quoted 2 lines

The explicit transfer approach better reflects the actual hardware protocol requirements.

That said, please, try to look into it closer.

I investigated your regmap suggestions thoroughly:

Custom IO accessors:
While technically possible, TM16xx protocols create significant implementation challenges:

- TM1650: Commands 0x48 (control) and 0x4F (key read) appear as I2C addresses but represent completely different operations with different data structures. Custom accessors would need complex command routing logic.

- TM1628: Requires coordinated command sequences (mode -> write command -> control command -> data transfers). A single regmap_write() call can't express this multi-step initialization.

Paging/non-standard addressing:
TM1650's 0x68-0x6E digit commands could theoretically map to regmap pages, but the 0x48/0x4F control/read commands break the model since they're fundamentally different operations, not register variants.

You're correct that regmap provides valuable synchronization, debugfs, and abstraction benefits. However, implementing custom accessors for TM16xx would essentially recreate the existing controller functions while forcing them into register semantics they don't naturally fit.

Custom regmap implementation is possible but would add significant complexity to achieve register abstraction over inherently command-based protocols, while the current approach directly expresses the hardware's actual command structure.

#25 lore raw

Re: [PATCH v4 5/6] auxdisplay: TM16xx: Add support for I2C-based controllers

Quoted 4 lines · Jean-François Lessard
Quoted 3 lines · Andy Shevchenko
Quoted 2 lines · Jean-François Lessard
Quoted 1 line · Andy Shevchenko

On Sun, Aug 24, 2025 at 11:32:31PM -0400, Jean-François Lessard wrote:

...

Quoted 47 lines
Quoted 39 lines
Quoted 10 lines
Quoted 1 line

Can we use regmap for all parts of the driver? Why not?

These controllers implement custom 2-wire/3-wire protocols that share sufficient commonalities with I2C/SPI to leverage those subsystems, but are not fully compliant with standard register-based access patterns.

Specific regmap incompatibilities:

I2C protocol:
- Dynamic addressing: slave address embedded in command byte (data[0] >> 1)

Isn't this called paging? Or actually we have also non-standard (non-power-of-2) regmap implementations, perhaps one of them (7 + 9) if exists is what you need?

Quoted 1 line

- Custom message flags: requires I2C_M_NO_RD_ACK for reads

Hmm... If we have more than one device like this, we might implement the support in regmap. Or, perhaps, the custom regmap IO accessors can solve this.

Quoted 3 lines

SPI protocol:
- Inter-transfer timing: mandatory TM16XX_SPI_TWAIT_US delay between command/data

One may implement custom regmap IO accessors.

Quoted 6 lines

- CS control: requires cs_change = 0 to maintain assertion across phases

Regmap's I2C/SPI bus implementations use fixed addressing and standard transfer patterns without support for these protocol-specific requirements. A custom regmap bus would internally call these same helper functions without providing practical benefit.

regmap provides a few benefits on top of the raw implementations. First of all, it takes care about synchronisation (and as a side effect enables configurations of the multi-functional HW, if ever needed in this case). It also gives a debugfs implementation, and paging support (if it's what we need). And many more...

Quoted 5 lines
Quoted 2 lines

The explicit transfer approach better reflects the actual hardware protocol requirements.

That said, please, try to look into it closer.

I investigated your regmap suggestions thoroughly:

Quoted 7 lines

Custom IO accessors:
While technically possible, TM16xx protocols create significant implementation challenges:

- TM1650: Commands 0x48 (control) and 0x4F (key read) appear as I2C addresses but represent completely different operations with different data structures. Custom accessors would need complex command routing logic.

So, to me it sounds like mutli-functional I²C device with two clients, hence would be two drivers under the same umbrella.

Quoted 3 lines

- TM1628: Requires coordinated command sequences (mode -> write command -> control command -> data transfers). A single regmap_write() call can't express this multi-step initialization.

I believe we have something like that done in a few cases in the kernel, but I don't remember for sure.

Quoted 4 lines

Paging/non-standard addressing:
TM1650's 0x68-0x6E digit commands could theoretically map to regmap pages, but the 0x48/0x4F control/read commands break the model since they're fundamentally different operations, not register variants.

Paging can be partial.

Quoted 8 lines

You're correct that regmap provides valuable synchronization, debugfs, and abstraction benefits. However, implementing custom accessors for TM16xx would essentially recreate the existing controller functions while forcing them into register semantics they don't naturally fit.

Custom regmap implementation is possible but would add significant complexity to achieve register abstraction over inherently command-based protocols, while the current approach directly expresses the hardware's actual command structure.

Okay, if you still think regmap doesn't fit this case, please provide a summary in the cover letter to describe all your discoveries and thoughts.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#26 lore raw

Re: [PATCH v4 5/6] auxdisplay: TM16xx: Add support for I2C-based controllers

Le 26 août 2025 14 h 26 min 37 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 86 lines
Quoted 4 lines · Jean-François Lessard
Quoted 3 lines · Andy Shevchenko
Quoted 2 lines · Jean-François Lessard
Quoted 1 line · Andy Shevchenko

On Sun, Aug 24, 2025 at 11:32:31PM -0400, Jean-François Lessard wrote:

...

Quoted 47 lines
Quoted 39 lines
Quoted 10 lines
Quoted 1 line

Can we use regmap for all parts of the driver? Why not?

These controllers implement custom 2-wire/3-wire protocols that share sufficient commonalities with I2C/SPI to leverage those subsystems, but are not fully compliant with standard register-based access patterns.

Specific regmap incompatibilities:

I2C protocol:
- Dynamic addressing: slave address embedded in command byte (data[0] >> 1)

Isn't this called paging? Or actually we have also non-standard (non-power-of-2) regmap implementations, perhaps one of them (7 + 9) if exists is what you need?

Quoted 1 line

- Custom message flags: requires I2C_M_NO_RD_ACK for reads

Hmm... If we have more than one device like this, we might implement the support in regmap. Or, perhaps, the custom regmap IO accessors can solve this.

Quoted 3 lines

SPI protocol:
- Inter-transfer timing: mandatory TM16XX_SPI_TWAIT_US delay between command/data

One may implement custom regmap IO accessors.

Quoted 6 lines

- CS control: requires cs_change = 0 to maintain assertion across phases

Regmap's I2C/SPI bus implementations use fixed addressing and standard transfer patterns without support for these protocol-specific requirements. A custom regmap bus would internally call these same helper functions without providing practical benefit.

regmap provides a few benefits on top of the raw implementations. First of all, it takes care about synchronisation (and as a side effect enables configurations of the multi-functional HW, if ever needed in this case). It also gives a debugfs implementation, and paging support (if it's what we need). And many more...

Quoted 5 lines
Quoted 2 lines

The explicit transfer approach better reflects the actual hardware protocol requirements.

That said, please, try to look into it closer.

I investigated your regmap suggestions thoroughly:

Quoted 7 lines

Custom IO accessors:
While technically possible, TM16xx protocols create significant implementation challenges:

- TM1650: Commands 0x48 (control) and 0x4F (key read) appear as I2C addresses but represent completely different operations with different data structures. Custom accessors would need complex command routing logic.

So, to me it sounds like mutli-functional I²C device with two clients, hence would be two drivers under the same umbrella.

Quoted 3 lines

- TM1628: Requires coordinated command sequences (mode -> write command -> control command -> data transfers). A single regmap_write() call can't express this multi-step initialization.

I believe we have something like that done in a few cases in the kernel, but I don't remember for sure.

Quoted 4 lines

Paging/non-standard addressing:
TM1650's 0x68-0x6E digit commands could theoretically map to regmap pages, but the 0x48/0x4F control/read commands break the model since they're fundamentally different operations, not register variants.

Paging can be partial.

Quoted 5 lines

You're correct that regmap provides valuable synchronization, debugfs, and abstraction benefits. However, implementing custom accessors for TM16xx would essentially recreate the existing controller functions while forcing them into register semantics they don't naturally fit.

Quoted 8 lines
Quoted 3 lines

Custom regmap implementation is possible but would add significant complexity to achieve register abstraction over inherently command-based protocols, while the current approach directly expresses the hardware's actual command structure.

Okay, if you still think regmap doesn't fit this case, please provide a summary in the cover letter to describe all your discoveries and thoughts.

Understood. I'll provide a summary of the regmap evaluation and design decisions in the cover letter of the next submission.

#27 lore raw

PATCH v4 6/6 auxdisplay: TM16xx: Add support for SPI-based controllers

In reply to Jean-François Lessard

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.

---

Notes:
include <linux/of.h> is required for of_match_ptr

 MAINTAINERS                     |   1 +
 drivers/auxdisplay/Kconfig      |   7 +
 drivers/auxdisplay/Makefile     |   1 +
 drivers/auxdisplay/tm16xx_spi.c | 398 ++++++++++++++++++++++++++++++++
 4 files changed, 407 insertions(+)
 create mode 100644 drivers/auxdisplay/tm16xx_spi.c
MAINTAINERS +1 -0
index 51cc910e2..1ee45be14 100644
@@ -25411,6 +25411,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 <[email protected]>
drivers/auxdisplay/Kconfig +7 -0
index 6238e753d..868625596 100644
@@ -536,6 +536,7 @@ config TM16XX
 	select INPUT_MATRIXKMAP
 	select TM16XX_KEYPAD if (INPUT)
 	select TM16XX_I2C if (I2C)
+	select TM16XX_SPI if (SPI)
 	help
 	  This driver supports the following TM16XX compatible
 	  I2C and SPI 7-segment led display chips:
@@ -560,6 +561,12 @@ config TM16XX_I2C
 	help
 	  Enable I2C support for TM16XX driver.
 
+config TM16XX_SPI
+	tristate
+	depends on TM16XX
+	help
+	  Enable SPI support for TM16XX driver.
+
 #
 # Character LCD with non-conforming interface section
 #
drivers/auxdisplay/Makefile +1 -0
index ba7b310f5..2485a3a67 100644
@@ -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
drivers/auxdisplay/tm16xx_spi.c new file +398 -0

398 lines not on this page · raw message

Signature · 1 line
2.43.0
#28 lore raw

Re: [PATCH v4 6/6] auxdisplay: TM16xx: Add support for SPI-based controllers

Quoted 9 lines · Jean-François Lessard

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

Quoted 8 lines

This allows platforms using TM16xx or compatible controllers over SPI to be managed by the TM16xx driver infrastructure.

---

Notes:
include <linux/of.h> is required for of_match_ptr

Yeah, please drop the latter and remove the former.

All other comments are the same as per I²C counterpart of the driver.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#29 lore raw

Re: [PATCH v4 6/6] auxdisplay: TM16xx: Add support for SPI-based controllers

Le 25 août 2025 11 h 19 min 21 s HAE, Andy Shevchenko <[email protected]> a écrit :

Quoted 13 lines · Jean-François Lessard
Quoted 12 lines

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.

Quoted 8 lines
Quoted 5 lines

---

Notes:
include <linux/of.h> is required for of_match_ptr

Yeah, please drop the latter and remove the former.

Well received.

Quoted 2 lines

All other comments are the same as per I²C counterpart of the driver.

Acknowledged.