The following steps allow to make use of device tree overlay files, a definition of device tree overlay provided by kernel.org is the next: "A Devicetree’s overlay purpose is to modify the kernel’s live tree, and have the modification affecting the state of the kernel in a way that is reflecting the changes. Since the kernel mainly deals with devices, any new device node that result in an active device should have it created while if the device node is either disabled or removed all together, the affected device should be deregistered." Knowing that, in this post will be used as an example the baseboard "i.MX 93 EVK" and will be added with device tree overlay an LVDS panel, adding an automatic detection from u-boot, and will be used a host with linux version Ubuntu 20.04.2. Note: It only works for linux kernel version 6.6.3-nanbield onward.
Linux device-tree overlay from linux-imx
This section explains all about device tree overlay compilation and building, to create a .dtso file, the equivalent of .dts for overlays, adding some difference between them, using as base the linux-imx repository. It can be downloaded from the following repository:
git clone https://github.com/nxp-imx/linux-imx.git -b <branch version>
Branch version used by this post "lf-6.6.3-1.0.0".
Device tree source overlay (.dtso)
It can be similar to a device tree source (.dts) but it had little difference between them, there are some difference in the next list:
There's another type of files to be included, if is used pinmux it's necessary adding it with "#include "imx93-pinfunc.h"" and libraries from dt-bindings, it depends on the type of device tree to implement "#include <dt-bindings/<library>>"
At initialization it needs to add: "/dts-v1/;" "/plugin/;"
Addition of "fragment" nodes, it allow override parts of a device tree, it can be a specific node or create a new node. following structure it's the structure of a fragment:
{
/* ignored properties by the overlay */
fragment@0 { /* first child node */
target=<phandle>; /* phandle target of the overlay */
or
target-path="/path"; /* target path of the overlay */
__overlay__ {
property-a; /* add property-a to the target */
node-a { /* add to an existing, or create a node-a */
...
};
};
}
fragment@1 { /* second child node */
...
};
/* more fragments follow */
}
kernel.org
Overlays can't delete a property or a node when it's applied, so can't be used "/delete-node/" nor "/delete-prop/", but it can be added to the node "status = "disabled";" to disable it.
Using as an example the file imx93-11x11-evk-boe-wxga-lvds-panel.dts located in the previous repository file direction <linux-imx path>/arch/arm64/boot/dts/freescale/ using it as a base tree:
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright 2022 NXP
*/
#include "imx93-11x11-evk.dts"
/ {
lvds_backlight: lvds_backlight {
compatible = "pwm-backlight";
pwms = <&adp5585pwm 0 100000 0>;
enable-gpios = <&adp5585gpio 8 GPIO_ACTIVE_HIGH>;
power-supply = <®_vdd_12v>;
status = "okay";
brightness-levels = < 0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
100>;
default-brightness-level = <80>;
};
...
};
...
&adv7535 {
status = "disabled";
};
...
imx93-11x11-evk-boe-wxga-lvds-panel.dts
Using the previous points and making use of fragments, if we want adapt the node lvds_backlight as fragment, it will be added in the section of overlay, and adding it to a target-path "/":
#include <dt-bindings/interrupt-controller/irq.h>
#include "imx93-pinfunc.h"
#include <dt-bindings/gpio/gpio.h>
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
lvds_backlight: lvds_backlight {
compatible = "pwm-backlight";
pwms = <&adp5585pwm 0 100000 0>;
enable-gpios = <&adp5585gpio 8 GPIO_ACTIVE_HIGH>;
power-supply = <®_vdd_12v>;
status = "okay";
brightness-levels = < 0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
100>;
default-brightness-level = <80>;
};
};
};
...
};
imx93-11x11-evk-test-lvds-panel.dtso
In the case of adding a property to an existing node, it will look in the following way using as example the node adv7535.
...
/ {
...
fragment@2 {
target = <&adv7535>;
__overlay__ {
status = "disabled";
};
};
...
};
imx93-11x11-evk-boe-wxga-lvds-panel.dts
At the end of this post, will be attach the complete file used for LVDS panel named as imx93-11x11-evk-test-lvds-panel.dtso
Build device tree blob for overlay (dtbo)
To compile the previous .dtso it's necessary to include it to linux-imx repository, linux device tree overlay was included in BSP from version 6.6.3-nanbield onward in Makefile, so it's only necessary adding it as files to be compiled as .dtso, at the end of the post will be a patch file named as linux-imx-makefile.patch to add LVDS-panel to Makefile from branch lf-6.6.3-1.0.0
Add previously file imx93-11x11-evk-test-lvds-panel.dtso to path <linux-imx path>/arch/arm64/boot/dts/freescale/
Add imx93-11x11-evk-test-lvds-panel.dtso as file to be compiled in Makefile, it is located in the next path <linux-imx path>/arch/arm64/boot/dts/freescale/Makefile, it can be added with the next sentence format: <overlay without extension>-dtbs := <file to be overlayed>.dtb <overlay>.dtbo
Example of how to add LVDS panel to makefile
imx93-11x11-evk-test-lvds-panel-dtbs := imx93-11x11-evk.dtb imx93-11x11-evk-test-lvds-panel.dtbo
Makefile
From main path, make the configuration to be compiled with the following bash command: $ cd <linux-imx path>/
$ make -j$(nproc --all) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- imx_v8_defconfig
Compile overlay to use $ make -j $(nproc --all) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- freescale/<overlay>.dtbo
as example for LVDS panel
$ make -j $(nproc --all) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- freescale/imx93-11x11-evk-test-lvds-panel.dtbo
It will compile the device tree blob overlay to use.
Copy .dtbo generated in memory used by i.MX 93, it can be sending it from scp. scp ./<overlay>.dtbo root@<ip>:/run/media/<memory section used>
u-boot
This section explain the procedure to load a device tree overlay, it will be from u-boot explaining commands used and using the LVDS panel as an example.
Before applying overlay
Before applying, it's necessary had a device tree loaded so looking around in the process of booting in a i.MX 93 from u-boot, this process is defined by the enviroment variable "bsp_bootcmd" that calls the variable mmcboot, and looking what does these variables, it can be look in the following sentence:
bsp_bootcmd=echo Running BSP bootcmd ...; mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if test ${sec_boot} = yes; then if run loadcntr; then run mmcboot; else run netboot; fi; else if run loadimage; then run mmcboot; else run netboot; fi; fi; fi; fi;
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${sec_boot} = yes; then if run auth_os; then run boot_os; else echo ERR: failed to authenticate; fi; else if test ${boot_fit} = yes || test ${boot_fit} = try; then bootm ${loadaddr}; else if run loadfdt; then run boot_os; else echo WARN: Cannot load the DT; fi; fi;fi;
but reducing it in a normal situation, ignoring if else case and echoes, it can be simplify to:
mmc dev ${mmcdev}; run loadimage; run mmcargs; run loadfdt; run boot_os;
the device tree is load is in the section "run loadfdt" with fatload in his definition:
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile}
So, it's necessary to applying device tree overlay after "run loadfdt".
How to apply an overlay
To load correctly an overlay it's necessary to following some steps:
Load flattened device tree (fdt). (executed by loadfdt)
Configure fdt address.
In some cases it's necessary to expand fdt memory size
Load overlay
Apply overlay
The full sentence to apply it, it's the following u-boot command:
u-boot=> setexpr fdtovaddr ${fdt_addr} + 0xF0000; setexpr fdt_buffer 16384; fdt addr ${fdt_addr} && fdt resize ${fdt_buffer}; fatload mmc ${mmcdev}:${mmcpart} ${fdtovaddr} <overlay>.dtbo && fdt apply ${fdtovaddr};
First of all, setexpr it's just to create a new variable, in this case these variable is an integer. Spliting the previously command we can found the steps to applying it.
"fdt addr ${fdt_addr};" used to configure fdt address, and point to the space of memory previously charged.
"fdt resize ${fdt_buffer};" expand fdt memory size, is used as a value 16384 just to get the enough space to charge dtbo, this number was related with 2 14
"fatload mmc ${mmcdev}:${mmcpart} ${fdtovaddr} <overlay>.dtbo" Load device tree overlay using fdovaddr, that is fdt_addr adding an offset of memory space.
"fdt apply ${fdtovaddr};" apply device tree overlay
Remembering about load overlay needs to be executed after loadfdt, it's possible to save the previous command to a variable and executing it after loadfdt with setexpr, in this case using as example lvds test.
u-boot=> setenv loadoverlay "setexpr fdtovaddr ${fdt_addr} + 0xF0000; setexpr fdt_buffer 16384; fdt addr $\{fdt_addr\} && fdt resize $\{fdt_buffer\}; fatload mmc $\{mmcdev\}:$\{mmcpart\} $\{fdtovaddr\} imx93-11x11-evk-test-lvds-panel.dtbo && fdt apply $\{fdtovaddr\};"
and modifying mmcboot with loadoverlay after loadfdt
u-boot=> setenv mmcboot "run mmcargs; run loadfdt; run loadoverlay; run boot_os;"
to save the environment variables created, it can be saved from u-boot wit the following command.
u-boot=> saveenv
At the end, boot imx93
u-boot=> boot
The LVDS panel should be working using the original dtb (imx93-11x11-evk.dtb) applied the overlay.
Automatize u-boot LVDS Panel
This section explain how can be automatize the u-boot load overlay using an LVDS panel, it can vary depending the device to used for, the method used is detecting it in u-boot initialization and if found any device it will generate an environment variable. All the steps was using as a base uboot-imx repository, it can be downloaded from the following repository, at the end of this post will be a patch with the changes.
git clone https://github.com/nxp-imx/uboot-imx.git -b <branch version>
Branch version used "lf-6.6.3-1.0.0".
Base
Knowing more about LVDS Panel used by imx93 it's really hard know more information about registers, so in this example will be limited to detect that is connected the address to a corresponding bus from touch controller.
To know i2c address and bus used by LVDS panel it was used searching it from the original device tree in the next section:
&lpi2c1 {
exc80h60: touch@2a {
compatible = "eeti,exc80h60";
reg = <0x2a>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ctp_int>;
/*
* Need to do hardware rework here:
* remove R131, short R181
*/
interrupt-parent = <&gpio2>;
interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&pcal6524 17 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
imx93-11x11-evk-boe-wxga-lvds-panel.dts
Previous node is related with touch controller from LVDS using lpi2c1, the first channel of i2c corresponding to i2c bus 0, and the register used express the address used to be detected by device tree, in this case was the address 0x2A.
u-boot generating a trigger
About how it can be detected touch controller from u-boot, this procedure use a function named as "board_late_init", it can be found by his definition from u-boot readme:
Board initialization settings:
------------------------------
During Initialization u-boot calls a number of board specific functions
to allow the preparation of board specific prerequisites, e.g. pin setup
before drivers are initialized. To enable these callbacks the
following configuration macros have to be defined. Currently this is
architecture specific, so please check arch/your_architecture/lib/board.c
typically in board_init_f() and board_init_r().
- CONFIG_BOARD_EARLY_INIT_F: Call board_early_init_f()
- CONFIG_BOARD_EARLY_INIT_R: Call board_early_init_r()
- CONFIG_BOARD_LATE_INIT: Call board_late_init()
u-boot README
In the case of i.MX 93 this function can be found in the next path <u-boot path>/board/freescale/imx93_evk/imx93_evk.c. Using the library included, "uclass.h", it will create a function that, if detect in the bus 0 (LVDS i2c bus) the address 0x2A (i2c LVDS address), it will create an environment variable with the overlay used, it can be set with the function env_set(<String with the name of the variable>, <String with the content of the variable>), the following function can detect and create the environment variable mentioned, creating it with the name "device-tree-overlay" with the content "lvds-panel".
#define LVDS_TOUCH_I2C_BUS 0
#define LVDS_TOUCH_I2C_ADDR 0x2A
static void detect_display_connected(void)
{
struct udevice *bus = NULL;
struct udevice *i2c_dev = NULL;
int ret;
ret = uclass_get_device_by_seq(UCLASS_I2C, LVDS_TOUCH_I2C_BUS, &bus);
if (ret) {
printf("%s: Can't find bus\n", __func__);
}
else
{
ret = dm_i2c_probe(bus, LVDS_TOUCH_I2C_ADDR, 0, &i2c_dev);
if (ret) {
printf("%s: Can't find device id=0x%x\n",
__func__, LVDS_TOUCH_I2C_ADDR);
}
else
{
env_set("device-tree-overlay", "lvds-panel");
}
}
}
imx93_evk.c
At the end, add this function to the previously mention, named as board_late_init, in the section CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG, like the following snipped from code:
int board_late_init(void)
{
#ifdef CONFIG_ENV_IS_IN_MMC
board_late_mmc_env_init();
#endif
env_set("sec_boot", "no");
#ifdef CONFIG_AHAB_BOOT
env_set("sec_boot", "yes");
#endif
#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
env_set("board_name", "11X11_EVK");
env_set("board_rev", "iMX93");
detect_display_connected();
#endif
return 0;
}
imx93_evk.c
Now, when it's starting u-boot after flashing, it will generate the environment variable as trigger if something it's connected with that i2c address, else it doesn't do anything.
u-boot applying device tree overlay through event
As was explained in the section "How to apply device tree overlay", applying the device tree overlay automatically after configure the trigger it's easy, just adding an if/else case for this example, it can be more ways to applying it, even it's possible adding more of one device tree overlay, but in this example will load one.
Using u-boot command "test -e <environment variable>" it will detect if exist this environment variable, adding it to an if/else sentence it can create the event and applying the overlay if was detected or not, for this solution will be added this if/else as input if exists loadoverlay variable with the following structure:
u-boot=> if test -e ${device-tree-overlay}; then <case exists device-tree-overlay variable> else <case doesn't exists device-tree-overlay variable>; fi;
adding it to loadoverlay, it will be written like the following command:
u-boot=> setenv loadoverlay "if test -e ${device-tree-overlay}; then setexpr fdtovaddr ${fdt_addr} + 0xF0000; setexpr fdt_buffer 16384; fdt addr ${fdt_addr} && fdt resize $\{fdt_buffer\}; fatload mmc ${mmcdev}:${mmcpart} $\{fdtovaddr\} imx93-11x11-evk-test-lvds-panel.dtbo; fdt apply $\{fdtovaddr\} ; else echo no overlay; fi;"
A no recommended method it's that it can be saved the environment, and changing mmcboot variable with the following command:
u-boot=> setenv mmcboot "run mmcargs; run loadfdt; run loadoverlay; run boot_os;"; saveenv;
The problem about just saving it, it still necessary compile u-boot to load auto-detection of LVDS panel and flashing, another way to add the event trigger, it's adding it to u-boot as initial environment variable, it can be added in the header file of imx93, it is located in the next path <u-boot path>/include/configs/imx93_evk.h, line number 60, it can be added with the same string but it's recommended follow the same structure, like the following definition:
/* Initial environment variables */
#define CFG_EXTRA_ENV_SETTINGS \
...
"loadoverlay=echo loading overlays from mmc ...; " \
"if test -e ${device-tree-overlay}; then " \
"setexpr fdtovaddr ${fdt_addr} + 0xF0000; " \
"setexpr fdt_buffer 16384; " \
"fdt addr ${fdt_addr} && fdt resize ${fdt_buffer}; " \
"fatload mmc ${mmcdev}:${mmcpart} ${fdtovaddr} imx93-11x11-evk-test-lvds-panel.dtbo && fdt apply ${fdtovaddr}; " \
"else " \
"echo no overlay; " \
"fi;\0" \
...
imx93_evk.h
it also it's necessary to change mmcboot environment variable adding loadoverlay after executing loadfdt.
/* Initial environment variables */
#define CFG_EXTRA_ENV_SETTINGS \
..
"mmcboot=echo Booting from mmc ...; " \
"run mmcargs; " \
"if test ${sec_boot} = yes; then " \
"if run auth_os; then " \
"run run boot_os; " \
"else " \
"echo ERR: failed to authenticate; " \
"fi; " \
"else " \
"if test ${boot_fit} = yes || test ${boot_fit} = try; then " \
"bootm ${loadaddr}; " \
"else " \
"if run loadfdt; then " \
"run loadoverlay; " \
"run boot_os; " \
"else " \
"echo WARN: Cannot load the DT; " \
"fi; " \
"fi;" \
"fi;\0" \
...
imx93_evk.h
To build u-boot, copy the following commands in main path from u-boot
$ cd <u-boot path>
$ make -j $(nproc --all) clean PLAT=imx93 CROSS_COMPILE=aarch64-linux-gnu-
$ make -j $(nproc --all) ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- imx93_11x11_evk_defconfig
$ make -j $(nproc --all) PLAT=imx93 CROSS_COMPILE=aarch64-linux-gnu-
generating the files u-boot.bin and u-boot-spl.bin located in <uboot-imx path>/ and <uboot-imx path>/spl
Build imx-boot image using imx-mkimage
To build the binary necessary to flash to iMX 93 EVK it's necessary build a file named as flash.bin, it can building using the next repository using the branch used for this example:
$ git clone https://github.com/nxp-imx/imx-mkimage.git -b lf-6.6.3_1.0.0
to build imx-boot image it's necessary adding some files to the path <imx-mkimage path>/iMX93, including 2 generated by u-boot, u-boot.bin and u-boot-spl.bin, move these files to iMX93 directory.
$ cp <uboot-imx path>/u-boot.bin <uboot-imx path>/spl/u-boot-spl.bin <imx-mkimage path>/iMX93/
follow the steps from imx linux users guide section 4.5.13 and imx linux release notes section 1.2 to build flash.bin, as an example of compile, there's the steps to compile for imx93.
Get mx93a1-ahab-container.img $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-sentinel-0.11.bin
$ chmod +x firmware-sentinel-0.11.bin
$ ./firmware-sentinel-0.11.bin
$ cp firmware-sentinel-0.11/mx93a1-ahab-container.img <imx-mkimage path>/iMX93/
Get lpddr4_imem_1d_v202201.bin, lpddr4_dmem_2d_v202201.bin, lpddr4_imem_1d_v202201.bin and lpddr4_imem_2d_v202201.bin $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.23.bin
$ chmod +x firmware-imx-8.23.bin
$ ./firmware-imx-8.23.bin
$ cp firmware-imx-8.23/firmware/ddr/synopsys/lpddr4_dmem_1d_v202201.bin firmware-imx-8.23/firmware/ddr/synopsys/lpddr4_dmem_2d_v202201.bin firmware-imx-8.23/firmware/ddr/synopsys/lpddr4_imem_1d_v202201.bin firmware-imx-8.23/firmware/ddr/synopsys/lpddr4_imem_2d_v202201.bin <imx-mkimage path>/iMX93/
Get bl31.bin $ git clone https://github.com/nxp-imx/imx-atf.git -b lf-6.6.3-1.0.0
$ cd imx-atf
$ make -j $(nproc --all) PLAT=imx93 CROSS_COMPILE=aarch64-linux-gnu-
$ cp <imx-atf path>/build/imx93/release/bl31.bin <imx-mkimage path>/iMX93
Compile flash.bin from imx-mkimage $ cd <imx-mkimage path>/
$ make SOC=iMX9 REV=A1 flash_singleboot
it will generate the binary flash.bin located in the path <imx-mkimage path>/iMX93/flash.bin.
Flashing u-boot
Flashing just u-boot image using flash.bin, will be used uuu.exe, it can be downloaded from the his repositroy, try using the most recent version taged as "Latest"
https://github.com/nxp-imx/mfgtools/releases
make sure is using i.MX 93 EVK in boot mode download and connect it to your host from download USB port, using uuu.exe run the next code:
.\uuu.exe -b emmc .\flash.bin
or it can be flashed the full image with flash.bin binary.
.\uuu.exe -b emmc_all .\flash.bin ..\uuu\imx-image-full-imx93evk.wic
after that, starting be will using the created u-boot environment.
Result
Inside u-boot, when it's connected the LVDS panel, it will create the variable named "device-tree-overlay" and will be charged automatically LVDS panel overlay, enabling it, if not it will working normally using DSI as output.
Note: Ensure to have imx93-11x11-evk-test-lvds-panel.dtbo in memory.
Reference
Device tree overlay: https://docs.kernel.org/devicetree/overlay-notes.html
View full article