Early Splash on U-Boot for Toradex SMARC iMX95
The 'black screen' period during boot is a UX failure point. The sooner you give the user some visual feedback, the better.
“Did the device really turn on?” “Did the device freeze?” “Is my HDMI cable broken?” These are questions I’ve asked myself (and heard from customers) when powering on a device/product that takes some time to boot and provides no visual feedback during the process.
Why This is Important
Watch the following comparison: two different boot configurations on the same Toradex SMARC iMX95:
Now, tell me, which one was the faster boot, 1 or 2?
In fact, they took the same time to boot. The only difference is that in configuration 1 I used a fully black image as the U-Boot splash screen to simulate a design without early bootloader splash support.
You may have heard about why elevators have mirrors: it’s a psychological effect. In the early days of high-rise buildings, engineers discovered that instead of speeding up the motors (or making other expensive, technically difficult improvements), they could change the perception of time. When people have something to distract themselves, the wait feels shorter. The same effect applies here: when the user sees a splash screen, even if the boot time is identical, the perceived boot time is shorter.
1st Challenge
Ok, it’s important, but what if there is no bootloader splash support? For example, the Toradex SMARC iMX95 system-on-module (SoM) has a Texas Instruments SN65DSI86 MIPI DSI to eDP bridge, which does not have driver support at the U-Boot level.
So the earliest we can show something on the screen is in Linux user space, once the Linux TI-SN65DSI86 driver is ready. By then a lot has already happened: the bootloader initialized critical hardware and started the Linux kernel, then the kernel initialized and configured the TI-SN65DSI86 bridge and its dependencies (reference clock, GPU subsystem, etc.) before it could show anything on the screen. This can easily take more than 10 seconds.
The Solution
To solve this problem, we need to bring up the TI-SN65DSI86 bridge earlier in the boot process, ideally in the bootloader phase (U-Boot). In U-Boot we don’t have a GPU or other fancy video subsystems; it’s a minimal framebuffer.
For the TI-SN65DSI86 it complicates things a bit more, because we need to bring up the NXP DPU and the i.MX95 MIPI DSI interface, then configure the TI-SN65DSI86 bridge over I2C to get the eDP/DP output signal.
Getting Linux as Reference
For this bring-up, I used the Linux kernel driver as a reference. The Linux kernel already has a TI-SN65DSI86 driver, so we’re not starting from zero. The idea was to check what the Linux driver does to bring up the bridge and replicate the same steps in U-Boot.
I had a bit of help from VS Code Copilot to make the “translation” from Linux kernel driver code to U-Boot driver code. After a few rounds of iterate/build/test/debug (plus device tree adjustments), I got the TI-SN65DSI86 bridge working in U-Boot. At least the initial video link chain was correct, and the bmp command could display images on the screen.

The driver can be found in the U-Boot fork for Gaia Build System: https://github.com/gaiaBuildSystem/u-boot/commit/fd42c0ca1c39bb1ea36713047dfa1e1ea729957c
These are the required config options to have video link, MIPI DSI, bitmap, and TI-SN65DSI86 support in U-Boot:
CONFIG_VIDEO_IMX95_DPU=y
CONFIG_VIDEO_IMX95_DW_DSI=y
CONFIG_VIDEO_IMX95_PIXEL_LINK=y
CONFIG_VIDEO_IMX95_PIXEL_INTERLEAVER=y
CONFIG_VIDEO=y
CONFIG_BMP=y
CONFIG_BMP_16BPP=y
CONFIG_BMP_24BPP=y
CONFIG_BMP_32BPP=y
CONFIG_VIDEO_LOGO=y
CONFIG_VIDEO_LCD_RAYDIUM_RM692C9=y
CONFIG_VIDEO_ADV7535=y
CONFIG_SYS_WHITE_ON_BLACK=y
CONFIG_SPLASH_SCREEN=y
CONFIG_SPLASH_SCREEN_ALIGN=y
CONFIG_SPLASH_SOURCE=y
CONFIG_CMD_BMP=y
CONFIG_DISPLAY=y
CONFIG_MIPI_DPHY_HELPERS=y
# display port
CONFIG_VIDEO_BRIDGE=y
CONFIG_VIDEO_BRIDGE_TI_SN65DSI86=y
The complete defconfig used for my tests is in the cookbook-nxp repo from Gaia Build System: https://github.com/gaiaBuildSystem/cookbook-nxp/blob/main/cookbook/recipes-bsp/u-boot/smarc-imx95/smarc-imx95_defconfig.template
2nd Challenge
Nice: we have the TI-SN65DSI86 working and displaying an image at the U-Boot level 🎉. But now this conflicts with the splash screen at the Linux level. What happens is that U-Boot resets the bridge during the handover to the Linux kernel, which causes the TI-SN65DSI86 to lose its configuration. Linux then has to bring up the bridge again.
Linux knows how to do this. The issue is that the signal drop during handover can cause some displays to delay or show messages like “DisplayPort disconnected”. Even though the signal returns, the display may take time to re-sync with the source. With fast boot times, this can cause the Linux splash screen to never appear because the boot finishes and the application is already up.
Ignore Reset
To solve this, I added a check for a new environment variable preserve_display to tell U-Boot video link code not to call device_remove during handover. This way the TI-SN65DSI86 keeps its configuration and does not get reset.
int video_link_shut_down(void)
{
struct udevice *video_dev = video_link_get_video_device();
/* If preserve_display is set, keep the video device active so the
* signal remains up during kernel handoff. This allows smoother
* transition when the kernel takes over the display. */
if (env_get_yesno("preserve_display") == 1)
return 0;
if (video_dev)
device_remove(video_dev, DM_REMOVE_NORMAL);
return 0;
}
This makes sense: the bootloader already configured the bridge, so why reset it again? Solved, right?
But the Linux driver was still resetting the bridge during its initialization 🤦:
static int ti_sn65dsi86_probe(struct i2c_client *client)
{
...
pdata->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
...
}
This gets the enable- GPIO and sets it low during probe, and it can be that the line is connected to the reset pin of the TI-SN65DSI86. So even though we told U-Boot not to reset the bridge, Linux was still doing it 😛.
Another change I needed was to enable the reference clock as early as possible so the TI-SN65DSI86 has a stable clock during handover:
if (pdata->refclk) {
ret = clk_prepare_enable(pdata->refclk);
if (ret)
return dev_err_probe(dev, ret, "failed to enable refclk\n");
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_refclk_disable,
pdata->refclk);
if (ret)
return ret;
}
Conclusion
An early splash screen is not just “cosmetic”: it removes uncertainty during the longest perceived part of the boot (the black-screen phase) and makes the product feel faster-even when the actual boot time does not change.
On the Toradex SMARC iMX95, the missing piece was bootloader-level support for the MIPI DSI to eDP bridge. By bringing up the i.MX95 display chain in U-Boot (DPU - MIPI DSI - TI-SN65DSI86 - eDP/DP) it became possible to show a splash image immediately, before Linux is even started.
The second half of the work was ensuring a smooth handover to Linux. Getting “first light” in U-Boot is not enough if the handoff drops the link and forces the display to re-sync. The fix ended up being a coordinated approach: prevent U-Boot from tearing down the display pipeline (preserve_display), avoid Linux resetting the bridge during probe via the reset/enable GPIO behavior, and make sure the TI-SN65DSI86 reference clock is enabled early so the bridge stays stable while the kernel takes over.
Overall, the main takeaway is that early splash is a full-boot-chain feature: U-Boot, Linux drivers, and device tree need to agree on who owns reset/clock/power sequencing. Once they do, you get the best of both worlds-instant visual feedback from U-Boot and a seamless transition into the Linux splash and user space.
Know you are loved ❤️