Skip to content

PiTFT Plus does not work on 50% of boot cycles, probably due to race condition (/dev/fb0 vs. /dev/fb1) #365

@neuron-whisperer

Description

@neuron-whisperer

Script Command

git clone https://github.com/adafruit/Raspberry-Pi-Installer-Scripts.git

cd Raspberry-Pi-Installer-Scripts

sudo /home/pi/.pyenv/shims/python adafruit-pitft.py --display=28c --rotation=90 --install-type=console --reboot no

Operating System

Raspberry Pi OS Trixie Lite (64-bit)

Hardware

Raspberry Pi Zero 2 W and Adafruit PiTFT Plus (2.8" capacitive touch)

Behavior

On 50% of boot cycles, the PiTFT Plus shows the normal output from initialization and works fine. During these boot cycles, /dev/fb0 exists and /dev/fb1 does not exist.

On the other 50% of boot cycles, the PiTFT Plus is inaccessible and remains in its state at power-on (blank screen, output from previous boot cycle, etc.) During these boot cycles, /dev/fb1 exists and /dev/fb0 does not exist.

This behavior is reproducible: repeatedly booting the Raspberry Pi Zero 2 W resulted in one of these two states.

Description

Boot cycles that result in a functional PiTFT contain the following lines in journalctl and dmesg:

simple-framebuffer 1eaf0000.framebuffer: framebuffer at 0x1eaf0000, 0x10a800 bytes
simple-framebuffer 1eaf0000.framebuffer: format=a8r8g8b8, mode=656x416x32, linelength=2624
Console: switching to colour frame buffer device 82x26
simple-framebuffer 1eaf0000.framebuffer: fb0: simplefb registered!

...

[drm] Initialized ili9341 1.0.0 for spi0.0 on minor 1
Console: switching to colour frame buffer device 40x30
ili9341 spi0.0: [drm] fb0: ili9341drmfb frame buffer device

Boot cycles that result in a non-functional PiTFT contain the following lines in journalctl and dmesg:

simple-framebuffer 1eaf0000.framebuffer: framebuffer at 0x1eaf0000, 0x10a800 bytes
simple-framebuffer 1eaf0000.framebuffer: format=a8r8g8b8, mode=656x416x32, linelength=2624
Console: switching to colour frame buffer device 82x26
simple-framebuffer 1eaf0000.framebuffer: fb0: simplefb registered!

...

[drm] Initialized ili9341 1.0.0 for spi0.0 on minor 0
ili9341 spi0.0: [drm] fb1: ili9341drmfb frame buffer device

Attached are the journalctl and dmesg dumps for example nonworking and working boot cycles.

working.txt

nonworking.txt

Additional information

Upon further investigation, I traced the output in the latter chunk of journalctl/dmesg output above to /usr/local/bin/con2fbmap-helper.sh, which contains this:

# Wait up to 30 seconds for /dev/fb0 or /dev/fb1 to appear
for i in {1..300}; do
    for fbdev in 0 1; do
        if [ -e /dev/fb$fbdev ]; then
            echo "Found /dev/fb$fbdev, checking if it's ili9341..."

            # Check if it's actually the ili9341 device
            if dmesg | grep -q "ili9341.*fb$fbdev"; then
                echo "ili9341 framebuffer ready, mapping console..."
                con2fbmap 1 $fbdev
                echo "Console mapped to framebuffer $fbdev"
                exit 0
            fi
        fi
    done
    sleep 0.1
done

The problem looks clear to me: in both cases, initialization begins with a mapping to fb0, but then con2fbmap-helper.sh waits for the display device (ili9341) to appear as either fb0 or fb1. If it happens to appear as fb0, then configuration proceeds normally and the display works. But if it happens to appear as fb1, then the kernel attempts to configure the display on fb1, which does not match up with the earlier device reservations at fb0.

(edit) Looking at the journalctl/dmesg output again, I found this additional difference:

Working:

kernel: rc rc0: vc4-hdmi as /devices/platform/soc/3f902000.hdmi/rc/rc0
kernel: input: vc4-hdmi as /devices/platform/soc/3f902000.hdmi/rc/rc0/input0
kernel: input: vc4-hdmi HDMI Jack as /devices/platform/soc/3f902000.hdmi/sound/card0/input1
kernel: [drm] Initialized vc4 0.0.0 for soc:gpu on minor 0

Nonworking:

kernel: rc rc0: vc4-hdmi as /devices/platform/soc/3f902000.hdmi/rc/rc0
kernel: input: vc4-hdmi as /devices/platform/soc/3f902000.hdmi/rc/rc0/input1
kernel: input: vc4-hdmi HDMI Jack as /devices/platform/soc/3f902000.hdmi/sound/card0/input2
kernel: [drm] Initialized vc4 0.0.0 for soc:gpu on minor 1

Notably, in the working configuration, the "Initialized vc4 0.0.0 for soc:gpu" line occurs before the "Initialized ili9341" line (i.e., HDMI is initialized first and is initialized as "minor 0" on /dev/fb1 which forces the PiTFT to be initialized as "minor 1" on /dev/fb0). In the nonworking configuration, it's the opposite.

Bottom line: There is apparently a race condition occurring during the boot cycle between vc4-hdmi and ili9341.

As a workaround - editing /boot/firmware/config.txt to delete or comment out the line dtoverlay=vc4-kms-v3d results in a consistently working PiTFT Plus for six consecutive boot cycles. Re-adding that line caused the race condition and the inconsistent behavior to recur.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions