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,