[GUIDE] Waveshare 11.9"/7.9" DSI LCD Display Configuration for Volumio 4

Hey @Mustang65, @aluisperezh,

A user-side workaround involving delayed driver loading is not viable on Volumio. The display must initialize during early boot to support Volumio’s custom Plymouth splash - this is core branding that displays from initramfs through to user session. Delaying ws_touchscreen would result in a dark screen during the entire boot sequence, defeating the purpose of having a display. This leaves no practical user-side solution. The fix must come from Waveshare in the driver itself.

I examined the actual driver source code. The file is panel-waveshare-dsi.c in the Raspberry Pi kernel tree.

The probe function (line 390) does this immediately after obtaining the I2C handle:

ts->i2c = i2c;

ws_panel_i2c_write(ts, 0xc0, 0x01);
ws_panel_i2c_write(ts, 0xc2, 0x01);
ws_panel_i2c_write(ts, 0xac, 0x01);

Three I2C writes fire instantly with no stabilization delay. The write function itself (line 309):

static void ws_panel_i2c_write(struct ws_panel *ts, u8 reg, u8 val)
{
    int ret;

    ret = i2c_smbus_write_byte_data(ts->i2c, reg, val);
    if (ret)
        dev_err(&ts->i2c->dev, "I2C write failed: %d\n", ret);
}

Single attempt. Error logged. No retry. No recovery. Panel stays uninitialized.


How production-quality DSI drivers handle this

For comparison, here is initialization code from a Volumio proprietary DSI panel driver (OEM series, ILITEK-based controller). This demonstrates defensive initialization patterns expected in embedded display drivers.

Retry parameters defined upfront:

#define RETRY_CMD    3      // Retry attempts before failure
#define RETRY_DELAY  120    // Retry wait time in ms

Initialization sequence begins with stabilization delay:

static const struct panel_init_cmd mt1280800a_init_cmd[] = {
    _INIT_DELAY_CMD(5),    // Allow controller to stabilize
    ...

Every critical operation uses retry logic:

retry = 0;
do {
    ret = mipi_dsi_dcs_nop(ctx->dsi);
    if (ret) msleep(RETRY_DELAY);
    ++retry;
}
while (ret && retry < RETRY_CMD);
if (ret < 0) {
    dev_err(panel->dev, "DSI: Failed to return to LP11 state: %d\n", ret);
}

Panel prepare sequence with proper reset timing:

static int mtdsi_prepare(struct drm_panel *panel)
{
    // ... power enable ...
    usleep_range(1000, 2000);

    // Warmup with retry
    retry = 0;
    do {
        ret = mipi_dsi_dcs_nop(ctx->dsi);
        if (ret) msleep(RETRY_DELAY);
        ++retry;
    }
    while (ret && retry < RETRY_CMD);

    usleep_range(1000, 2000);

    // Reset sequence with proper timing
    gpiod_set_value_cansleep(ctx->reset, 0);
    msleep(50);
    gpiod_set_value_cansleep(ctx->reset, 1);
    usleep_range(6000, 10000);

    ret = mtdsi_init_dcs_cmd(ctx);
    // ... error handling with poweroff path ...
}

Error state tracking for recovery:

static atomic_t errorFlag = ATOMIC_INIT(0);

// Set on failure
atomic_set(&errorFlag, 1);

// Clear on success
atomic_set(&errorFlag, 0);

Direct comparison

Aspect Waveshare OEM (Volumio)
Initial delay before I2C None 5ms minimum
Retry attempts 1 (fire and forget) 3 with delay
Retry delay N/A 120ms between attempts
Reset timing None 50ms low, 6-10ms settle
Stabilization delays None Multiple usleep_range/msleep
Error recovery Log and continue broken Track state, proper error path
Error state tracking None Atomic flag for recovery

The Waveshare driver lacks every defensive pattern that production embedded code requires. This is not an edge case - cold boot timing variability is fundamental to embedded systems. Every display driver I have written or reviewed handles this. Waveshare’s code does not.


The fix

Minimum viable fix - one line at line 393:

ts->i2c = i2c;

msleep(100);  /* Allow panel controller to stabilize after power-up */

ws_panel_i2c_write(ts, 0xc0, 0x01);

Proper fix - retry logic in the write function:

static int ws_panel_i2c_write(struct ws_panel *ts, u8 reg, u8 val)
{
    int ret, retries = 3;

    while (retries--) {
        ret = i2c_smbus_write_byte_data(ts->i2c, reg, val);
        if (!ret)
            return 0;
        msleep(50);
    }
    dev_err(&ts->i2c->dev, "I2C write failed after retries: %d\n", ret);
    return ret;
}

The required header #include <linux/delay.h> is already present at line 8. Either fix takes under 5 minutes to implement.


Summary

Waveshare told you to implement a delay. The delay needs to go in their driver at line 393, not in customer boot configurations. They have the source code. They maintain the driver. The fix is trivial. Asking customers to work around defective initialization code - when the fix is literally adding msleep(100); - is unacceptable.

The driver is maintained in the Raspberry Pi kernel tree. If Waveshare continues to refuse action, an issue can be raised at GitHub · Where software is built referencing this analysis.

Kind Regards,