Display configuration from U-Boot
Published on October 21, 2016
Starting with Linux kernel v3.14.x and above, the way to setup the display on our platforms has changed. This blog post will detail how to set the proper display configuration from U-Boot.
This applies to all operating systems that use the Linux kernel (Ubuntu, Debian, Yocto, Buildroot, Android).
What was the problem?
At first, back in the 3.0.x days it was all handled from the kernel bootargs. It required the kernel to know of a display and to recognize the boot parameter and then apply the values. At that time, we were "detecting" the display setup depending on the touch screen controllers present on the I2C bus.
Then, when using the device trees starting with the 3.10.x kernels, we kept this approach of detecting the touch controllers but had to change it a little since the display timings were held in the device tree. So we decided to have a node for each display and then the bootscript would be in charge of deleting the nodes/displays not used.
As the number of supported displays increased, the above approach was not suitable anymore because:
- The detection on touch controller wasn't flexible enough
- Several displays are using the FT5x06 for instance
- The number of display nodes inside the device tree was getting out of control
- Each new display required a modification in U-Boot and in the device tree (Kernel)
For all those reasons we decided to change the display configuration for kernel 3.14.x and above.
So what has changed?
Since U-Boot can easily read/write/update nodes inside the device tree blob before booting the kernel, the idea is to have a generic display node in the device tree which U-Boot will populate with the proper values.
You can see in our source tree that all our device trees contain:
- fb_hdmi alias to setup HDMI configuration
- fb_lcd alias to setup LCD displays
- fb_lvds and t_lvds to setup LVDS1 display
- fb_lvds2 and t_lvds2 to setup LVDS2 display (when available)
So U-Boot is now is charged to setup those nodes which will configure your display(s) easily.
Note that the touch controller detection is still present as a legacy mode, it means that if you don't provide any information, U-Boot will try to guess which display is connected, but there's now an easy way to override that setting.
How does it work?
First of all it requires a U-Boot >= v2015.07, at the time of this writing our latest version is v2016.03 but you can always grab the latest U-Boot from the link below:
So if your board contains an older version of U-Boot, please upgrade. You can follow the v2016.03 instructions.
Once you have a recent U-Boot, you can have a look at the supported display for your board by issuing:
=> fbpanel
clock-frequency hactive vactive hback-porch hfront-porch vback-porch vfront-porch hsync-len vsync-len
hdmi: 1280x720M@60:m24x1,50:74161969,1280,720,220,110,20,5,40,5
74161969 1280 720 220 110 20 5 40 5
hdmi: 1920x1080M@60:m24x1,50:148500148,1920,1080,148,88,36,4,44,5
148500148 1920 1080 148 88 36 4 44 5
...
Since the list is actually pretty long and not always easy to read, you can also filter by type of display (hdmi, lcd or lvds)
=> fbpanel lcd
clock-frequency hactive vactive hback-porch hfront-porch vback-porch vfront-porch hsync-len vsync-len
lcd: fusion7:m18x2,10:33264586,800,480,96,24,31,11,136,3
33264586 800 480 96 24 31 11 136 3
lcd: CLAA-WVGA:m18x2,48:27000027,800,480,40,60,10,10,20,10
27000027 800 480 40 60 10 10 20 10
...
=> fbpanel lvds
clock-frequency hactive vactive hback-porch hfront-porch vback-porch vfront-porch hsync-len vsync-len
lvds: hannstar7:18x2,38:71108582,1280,800,80,48,15,2,32,6
71108582 1280 800 80 48 15 2 32 6
...
The above command just lists the available displays, when you want to set one, you need will to set the following variables:
fb_hdmi
controls HDMI display selectionfb_lcd
controls LCD display selectionfb_lvds
controls LVDS display selectionfb_lvds2
controls LVDS2 display selection
Note that those variables are parsed by U-Boot at bootup, so when set one of those variables: remember to save the environment and reboot the board.
Also, when a display isn't used, you need to set it to off
. Here is an example on how to setup the HDMI to display at 1080P and LVDS display to be the Hannstar 10".
=> setenv fb_lvds hannstar
=> setenv fb_hdmi 1920x1080M@60
=> setenv fb_lcd off
=> saveenv
Saving Environment to SPI Flash...
SF: Detected SST25VF016B with page size 256 Bytes, erase size 4 KiB, total 2 MiB
Erasing SPI flash...Writing to SPI flash...done
=> reset
Once rebooted, you can have a look at the cmd_hdmi
, cmd_lcd
and cmd_lvds
that U-Boot will have set
=> print cmd_hdmi
cmd_hdmi=fdt set fb_hdmi status okay;fdt set fb_hdmi mode_str 1920x1080M@60;
=> print cmd_lvds
cmd_lvds=fdt set fb_lvds status okay;fdt set fb_lvds interface_pix_fmt RGB666;fdt set ldb/lvds-channel@0 fsl,data-width ;fdt set ldb/lvds-channel@0 fsl,data-mapping spwg;fdt set t_lvds clock-frequency ;fdt set t_lvds hactive ;fdt set t_lvds vactive ;fdt set t_lvds hback-porch ;fdt set t_lvds hfront-porch ;fdt set t_lvds vback-porch ;fdt set t_lvds vfront-porch ;fdt set t_lvds hsync-len ;fdt set t_lvds vsync-len ;
=> print cmd_lcd
cmd_lcd=fdt set fb_lcd status disabled
Do not try to set those cmd_*
variables yourself, they will be overwritten by U-Boot at bootup anyway.
That's it, you should now be able to list, select and setup the displays the way you want.
Can I use yet another display easily?
It depends on the type of display:
- LVDS: yes, since all the timings are inside the device tree node you can change them.
Here is an example for our latest 7" 1280x800 display, although only the latest U-Boot binary lists it, you can have it running by entering:
=> setenv fb_lvds tm070jdhg30:24:68152388,1280,800,5,63,2,39,1,1
=> saveenv
Note that it goes like this:
setenv fb_xxx mode_str:connection-type:clk-frequency,hactive,vactive,hback-porch,hfront-porch,vback-porch,vfront-porch,hsync-len,vsync-len
The connection-type
is very important since it allows to specify:
- The data mapping: default is SPWG, need to add "j" to switch to JEIDA
- The split mode: for dual LVDS channels operations (for 1080P display for instance) need to add "s"
- The data width: can be 18 or 24
For instance, here is a fb_lvds setup for a dual channel JEIDA LVDS display with 24-bit witdth:
=> setenv fb_lvds 1080P60:js24:148500148,1920,1080,148,88,36,4,44,5
- LCD: yes for U-Boot display, no for the kernel
You can set the fb_lcd
like it is done for LVDS above, however the timings will only be used to setup U-Boot, only the mode_str
will be passed on to the kernel. This means that the kernel needs to know about the LCD beforehand.
Here is an example for the ASIT500MA6F5D
display:
=> setenv fb_lcd ASIT500MA6F5D:m24:32341861,800,480,88,40,32,13,48,3
=> saveenv
The above line will make the display work in U-Boot, however the kernel will require this change to work.
- HDMI: yes (well more or less)
Same as the LCD setting, only the mode_str
is passed on to the kernel. The difference is that it can work out of the box on the kernel side if you ask for a standard resolution and standard refresh rate.
For instance, setting fb_hdmi
to 1920x1080M@30
will work automatically since the kernel is smart enough to recognize a known resolution (1080P) with a standard refresh rate (30fps).
As usual, let us know if you have question in comment section below.