Skip to content

drivers/lcd: fix apa102/max7219 object name collision with drivers/leds - #19785

Open
ricardgb wants to merge 2 commits into
apache:masterfrom
ricardgb:drivers-apa102-build-fixes
Open

drivers/lcd: fix apa102/max7219 object name collision with drivers/leds#19785
ricardgb wants to merge 2 commits into
apache:masterfrom
ricardgb:drivers-apa102-build-fixes

Conversation

@ricardgb

Copy link
Copy Markdown
Contributor

Summary

Two build-level defects around the APA102 RGB-LED driver, found while bringing an APA102 strip up on an ESP32-S3 board.

1. drivers/lcd/apa102.c and drivers/leds/apa102.c collide on one object name

drivers/ is built by a single flat Makefile. Each per-directory Make.defs appends to one shared CSRCS list and one shared VPATH, and every object lands in drivers/ named after the basename of its source. Two sources with the same basename in different subdirectories therefore map to the same object file, and make resolves the prerequisite through VPATH, which is searched in the order drivers/Makefile includes the Make.defs files. lcd is included before leds, so drivers/lcd always wins.

Two part names exist in both directories:

LCD front-end option LED driver option
drivers/lcd/apa102.c CONFIG_LCD_APA102 drivers/leds/apa102.c CONFIG_LEDS_APA102
drivers/lcd/max7219.c CONFIG_LCD_MAX7219 drivers/leds/max7219.c CONFIG_LEDS_MAX7219

drivers/lcd/Make.defs adds lcd to the VPATH for the whole directory whenever CONFIG_LCD=y, so selecting only the LED driver still compiles the LCD source into apa102.o / max7219.o and the selected LED driver is never compiled at all. Because the LCD front-ends take their constants from include/nuttx/lcd/apa102.h / include/nuttx/lcd/max7219.h, which are guarded by CONFIG_LCD_APA102 / CONFIG_LCD_MAX7219, the substituted source does not even compile. With CONFIG_LCD=y + CONFIG_LEDS_APA102=y and CONFIG_LCD_APA102 unset:

lcd/apa102.c:701:20: error: 'APA102_BLACK' undeclared (first use in this function); did you mean 'APA102_BPP'?
  701 |   memset(priv->fb, APA102_BLACK, 4 * APA102_FBSIZE);

and the same for CONFIG_LEDS_MAX7219 without CONFIG_LCD_MAX7219 ('MAX7219_BLACK' undeclared, plus MAX7219_POWER_OFF, MAX7219_SHUTDOWN, …). Net effect: neither LED driver can be built in any configuration that also enables CONFIG_LCD, and the error message points at a file the user never selected.

Fix: give the LCD front-ends distinct basenames (apa102_lcd.c, max7219_lcd.c, via git mv so history follows). The LCD side is the adapted use of these parts — an LED matrix driven as a display — and drivers/lcd already names such variants for their role (e.g. ht16k33_14seg.c), so the suffix goes there and the LED drivers keep the plain part names. drivers/lcd/Make.defs and drivers/lcd/CMakeLists.txt are both updated, plus the two .github/CODEOWNERS lines.

The CMake build was never affected: target_sources() resolves relative paths against the current source directory and CMake namespaces object paths per directory, so both apa102.c files can coexist there. Only the Make build is broken. The CMake source lists are updated purely to match the new filenames.

Other duplicate basenames in drivers/ — the only remaining one is skeleton.c, which exists four times (ioexpander/, lcd/, mtd/, net/). Three of those are unbuilt driver templates; only drivers/net/skeleton.c is ever added to CSRCS (CONFIG_NET_SKELETON), and because lcd precedes net in the include order it would be shadowed by drivers/lcd/skeleton.c in any CONFIG_LCD=y build. That is the same latent defect, but it only affects a template driver, so it is left out of this PR to keep the change focused — happy to rename those too if maintainers prefer.

2. The APA102 LCD driver ignores its own Kconfig settings

drivers/lcd/Kconfig offers CONFIG_LCD_APA102_XRES, CONFIG_LCD_APA102_YRES and CONFIG_LCD_APA102_FREQUENCY under if LCD_APA102, but the driver tests CONFIG_APA102_XRES, CONFIG_APA102_YRES and CONFIG_APA102_FREQUENCY — names no Kconfig file in the tree defines. The #ifndef fallbacks therefore always win and the geometry is hard-wired to 16x16 regardless of configuration. The frequency setting is doubly dead: even the fallback is unused, because apa102_configspi() passes APA102_SPI_MAXFREQUENCY from include/nuttx/leds/apa102.h, which is 100 kHz (its /* Default 4MHz */ comment notwithstanding), so the chain is always clocked at 100 kHz.

Fix: use the names Kconfig actually defines, and drive the bus at the configured frequency. The #ifndef fallbacks are kept for an out-of-Kconfig build and aligned with the Kconfig defaults; 16x16 preserves the previous geometry for anyone who never set the options.

Impact

  • Configurations with CONFIG_LCD=y together with CONFIG_LEDS_APA102=y or CONFIG_LEDS_MAX7219=y build for the first time, and build the driver that was actually selected.
  • CONFIG_LCD_APA102 / CONFIG_LCD_MAX7219 configurations are unchanged apart from the source filename — same code, same symbols (apa102_initialize, max7219_initialize).
  • No public header, Kconfig symbol, or API changes; nothing outside drivers/lcd references these .c files by name (board glue links against the headers).
  • The CONFIG_LCD_APA102_* settings start taking effect. For a configuration that never set them the behaviour is identical except for the SPI clock, which moves from the hard-coded 100 kHz to the Kconfig default of 1 MHz.

Testing

Build-verified on stm32f4discovery:nsh (arm-none-eabi-gcc, no new warnings). drivers/Make.dep is quoted because it records which source each object was actually built from:

# Case A: CONFIG_LCD=y, CONFIG_LEDS_APA102=y, CONFIG_LEDS_MAX7219=y, LCD_APA102/LCD_MAX7219 unset
# before: build fails -> lcd/apa102.c:701: 'APA102_BLACK' undeclared
# after:
$ make -j4                      # succeeds
$ grep '^apa102.o:\|^max7219.o:' drivers/Make.dep
apa102.o: leds/apa102.c \
max7219.o: leds/max7219.c \
$ nm drivers/apa102.o  | grep ' T '   ->  T apa102_register
$ nm drivers/max7219.o | grep ' T '   ->  T max7219_leds_register

# Case B: CONFIG_LCD_APA102=y (LEDS_APA102 unset)
$ make -j4                      # succeeds
$ nm drivers/apa102_lcd.o | grep ' T '   ->  T apa102_initialize

# Case C: all four selected at once - impossible before, links now
$ make -j4                      # succeeds
apa102.o: leds/apa102.c        apa102_lcd.o  -> T apa102_initialize
max7219.o: leds/max7219.c      max7219_lcd.o -> T max7219_initialize

# Kconfig plumbing: CONFIG_LCD_APA102_XRES=8 YRES=4 FREQUENCY=4000000
# g_apa102dev shrinks to an 8x4 shadow framebuffer and the SPI_SETFREQUENCY
# argument is 0x003d0900 (4 MHz); before the change both settings were ignored.

tools/checkpatch.sh -g on the two commits: all checks pass.

The drivers/leds/apa102.c LED-strip path itself was exercised on hardware today (APA102 on an ESP32-S3 SPI bus, apps/examples/apa102 writing a colour cycle to /dev/leddrv0) — that is what surfaced the collision. The change in this PR is build-verified only; the APA102/MAX7219 LCD front-ends are pure renames plus the Kconfig-name fix and were not exercised on a panel.

Disclosure

This analysis, patch, and build validation were performed by an AI agent (Claude Code, operated and directed by the submitter), and the result was reviewed by the submitter before posting.

drivers/ is built by a single flat Makefile: every per-directory Make.defs
appends its sources to one CSRCS list and its directory to one VPATH, and the
objects all land in drivers/ named after the source basename.  Two sources
with the same basename in different subdirectories therefore map to the same
object, and make resolves the prerequisite through VPATH, which is searched
in the order drivers/Makefile includes the Make.defs files.  lcd is included
before leds, so drivers/lcd always wins.

Both apa102 and max7219 exist twice, once as an LCD front-end and once as an
LED driver:

  drivers/lcd/apa102.c   CONFIG_LCD_APA102    drivers/leds/apa102.c   CONFIG_LEDS_APA102
  drivers/lcd/max7219.c  CONFIG_LCD_MAX7219   drivers/leds/max7219.c  CONFIG_LEDS_MAX7219

drivers/lcd/Make.defs puts lcd on the VPATH for the whole directory whenever
CONFIG_LCD=y, so selecting only the LED driver still builds apa102.o from
drivers/lcd/apa102.c and the selected LED driver is never compiled at all.
Because the LCD front-ends take their constants from
include/nuttx/lcd/apa102.h and include/nuttx/lcd/max7219.h, which are behind
CONFIG_LCD_APA102 / CONFIG_LCD_MAX7219, the substituted source does not even
compile.  With CONFIG_LCD=y + CONFIG_LEDS_APA102=y and CONFIG_LCD_APA102
unset:

  lcd/apa102.c:701:20: error: 'APA102_BLACK' undeclared (first use in this
  function); did you mean 'APA102_BPP'?

and correspondingly for CONFIG_LEDS_MAX7219 without CONFIG_LCD_MAX7219:

  lcd/max7219.c:773:20: error: 'MAX7219_BLACK' undeclared (first use in this
  function); did you mean 'MAX7219_BPP'?

So neither LED driver can be built together with CONFIG_LCD, and there is no
diagnostic pointing at the real cause.

Give the LCD front-ends distinct basenames.  The LCD side is the adapted use
of these parts (an LED matrix driven as a display), and drivers/lcd already
names such variants for their role, e.g. ht16k33_14seg.c, so the suffix goes
there and the LED drivers keep the plain part names.  The CMake build derives
object paths from the source directory and was never affected; its source
lists are updated to match.

Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude Opus 5 (Claude Code)
drivers/lcd/Kconfig offers CONFIG_LCD_APA102_XRES, CONFIG_LCD_APA102_YRES and
CONFIG_LCD_APA102_FREQUENCY under "if LCD_APA102", but apa102_lcd.c tests for
CONFIG_APA102_XRES, CONFIG_APA102_YRES and CONFIG_APA102_FREQUENCY, which no
Kconfig file defines.  The #ifndef fallbacks therefore always win and the
matrix geometry is hard-wired to 16x16 no matter what is configured.  The
frequency setting is doubly dead: even the fallback is unused, because
apa102_configspi() calls SPI_SETFREQUENCY() with APA102_SPI_MAXFREQUENCY from
include/nuttx/leds/apa102.h, which is 100 kHz (its "Default 4MHz" comment
notwithstanding), so the chain is always clocked at 100 kHz.

Use the names the Kconfig actually defines and drive the bus at the
configured frequency.  The fallback definitions are kept for an
out-of-Kconfig build and given the Kconfig defaults; 16x16 keeps the previous
geometry for anyone who never set the options.

Verified on stm32f4discovery:nsh with CONFIG_LCD_APA102_XRES=8,
CONFIG_LCD_APA102_YRES=4 and CONFIG_LCD_APA102_FREQUENCY=4000000: the shadow
framebuffer in g_apa102dev shrinks to 8x4 LEDs and the SPI frequency
argument is 0x003d0900, where before the settings had no effect at all.

Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude Opus 5 (Claude Code)
@github-actions github-actions Bot added Area: CI Size: S The size of the change in this PR is small labels Aug 11, 2026
@github-actions

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@acassis acassis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ricardgb please test it on stm32f103-minimum and stm32f4discovery, otherwise you could be introducing an issue that we only will notice later when some user try to use it.

@ricardgb

Copy link
Copy Markdown
Contributor Author

@ricardgb please test it on stm32f103-minimum and stm32f4discovery, otherwise you could be introducing an issue that we only will notice later when some user try to use it.

I have a stm32f4discovery laying around. I'll give it a try

@ricardgb

Copy link
Copy Markdown
Contributor Author

@ricardgb please test it on stm32f103-minimum and stm32f4discovery, otherwise you could be introducing an issue that we only will notice later when some user try to use it.

The board I have is different, so I can't test it. However the compilation doesn't throw any errors:

stm32f4discovery:nsh (default): builds clean — no regression.

stm32f4discovery with CONFIG_LCD + CONFIG_LEDS_APA102 + CONFIG_LEDS_MAX7219 (the exact combo that fails on master with the APA102_BLACK/MAX7219_BLACK collision): builds clean — the fix resolves it. With LCD=y but LCD_APA102 unset, only drivers/leds/apa102.o compiles (the lcd side is now apa102_lcd.c, gated on its own option), so no shadowing.
stm32f103-minimum:nsh (default): builds clean — the small-board build isn't broken by the rename.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: CI Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants