U-Boot PWM Backlight

I’m using an HDMI display panel and have connected GPIOAO_11 to the EN pin on the display backlight control IC. I wrote a device tree overlay to enable the pwm-backlight device control in Linux, and it is working exactly as intended.

The downside is that the backlight remains off until the point in the boot process where my custom script is called and the backlight is powered on. I would like to enable the pwm-backlight support in U-Boot so that the backlight will be enabled earlier in the boot process, and so I can further have U-Boot display a nice boot logo within moments of power-up.

I have (finally) successfully built and flashed mainline U-Boot with the related config options enabled and with a modified .dtsi that includes the pwm backlight and associates it with a “simple-panel.” As a result, U-Boot does recognize the simple-panel and pwm-backlight uclass objects, and it does output the console framebuffer on the HDMI display, but the pwm-backlight pin is not actually enabled/driven high as I would expect.

I will update this later to post my defconfig, dtsi changes, and console outputs when I have access to my machine. But in general, it looks like I am having the same issue as this fellow on SO who does not appear to have gotten an answer to his question.

If anyone here has experience with the inner workings of U-Boot, I could use some help understanding what I should be expecting. Various things I have read suggest that if the panel and associated backlight are properly defined, the backlight will be enabled automatically when the device is probed. On the other hand, I’ve noticed that some devices have a board.c that includes an explicit function to enable the backlight. So I’m not quite sure what tree I should be barking up. Should I be trying to figure out how to get U-Boot to “probe” the panel because doing so will automatically enable the backlight? Or should I be trying to cobble together some function for board.c to explicitly enable the backlight? Or am I not even on the right track at all?

Okay, as my initial attempt to enable PWM backlight in U-Boot, here are the changes I made to U-Boot: https://github.com/theophile/u-boot-amlogic/commit/6d279d72c7f4118c82252972ee34a26e8d5639ec

Here is the (truncated) output of the ‘dm tree’ command:

 Class     Index  Probed  Driver                Name
-----------------------------------------------------------
 root          0  [ + ]   root_driver           root_driver
 firmware      0  [   ]   psci                  |-- psci
 simple_bus    0  [ + ]   simple_bus            |-- soc
 simple_bus    1  [ + ]   simple_bus            |   |-- bus@ff600000
 display       0  [ + ]   meson_dw_hdmi         |   |   |-- hdmi-tx@0
 simple_bus    2  [   ]   simple_bus            |   |   |-- bus@30000
 rng           0  [   ]   meson-rng             |   |   |   `-- rng@218
 simple_bus    3  [ + ]   simple_bus            |   |   |-- bus@34400
 pinctrl       0  [ + ]   meson-g12a-pinctrl    |   |   |   `-- pinctrl@40
 pinconfig     0  [   ]   pinconfig             |   |   |       |-- cec_ao_a_h
 pinconfig     1  [   ]   pinconfig             |   |   |       |   `-- mux
 [***SNIP***]
 gpio          0  [ + ]   meson-axg-gpio        |   |   |       `-- meson-gpio
 phy           0  [   ]   meson_g12a_usb2_phy   |   |   |-- phy@36000
 simple_bus    4  [   ]   simple_bus            |   |   |-- bus@38000
 phy           1  [   ]   meson_g12a_usb2_phy   |   |   |-- phy@3a000
 simple_bus    5  [ + ]   simple_bus            |   |   |-- bus@3c000
 simple_bus    6  [ + ]   simple_bus            |   |   |   `-- system-controller@0
 clk           0  [ + ]   meson_clk_g12a        |   |   |       |-- clock-controller
 power_doma    0  [ + ]   meson_ee_pwrc         |   |   |       |-- power-controller
 syscon        1  [ + ]   syscon                |   |   |       `-- system-controller@0
 clk           3  [   ]   meson_clk_g12a        |   |   |           |-- clock-controller
 power_doma    1  [   ]   meson_ee_pwrc         |   |   |           `-- power-controller
 phy           2  [   ]   meson_g12a_usb3_pcie  |   |   |-- phy@46000
 simple_bus    7  [   ]   simple_bus            |   |   `-- bus@42000
 simple_bus    8  [ + ]   simple_bus            |   |-- bus@ff800000
 simple_bus    9  [ + ]   simple_bus            |   |   |-- sys-ctrl@0
 clk           1  [   ]   meson_clk_g12a_ao     |   |   |   |-- clock-controller
 pinctrl       1  [ + ]   meson-g12a-pinctrl    |   |   |   |-- pinctrl@14
 pinconfig   275  [   ]   pinconfig             |   |   |   |   |-- i2c_ao_sck_pins
 pinconfig   276  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   277  [   ]   pinconfig             |   |   |   |   |-- i2c_ao_sda
 pinconfig   278  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   279  [   ]   pinconfig             |   |   |   |   |-- i2c_ao_sck_e
 pinconfig   280  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   281  [   ]   pinconfig             |   |   |   |   |-- i2c_ao_sda_e
 pinconfig   282  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   283  [   ]   pinconfig             |   |   |   |   |-- mclk0-ao
 pinconfig   284  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   285  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-din0
 pinconfig   286  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   287  [   ]   pinconfig             |   |   |   |   |-- spdif-ao-out
 pinconfig   288  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   289  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-din1
 pinconfig   290  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   291  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-din2
 pinconfig   292  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   293  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-dout0
 pinconfig   294  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   295  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-dout1
 pinconfig   296  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   297  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-dout2
 pinconfig   298  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   299  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-fs
 pinconfig   300  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   301  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-sclk
 pinconfig   302  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   303  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-slv-fs
 pinconfig   304  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   305  [   ]   pinconfig             |   |   |   |   |-- tdm-ao-b-slv-sclk
 pinconfig   306  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   307  [ + ]   pinconfig             |   |   |   |   |-- uart-a-ao
 pinconfig   308  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   309  [   ]   pinconfig             |   |   |   |   |-- uart-ao-a-cts-rts
 pinconfig   310  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   311  [   ]   pinconfig             |   |   |   |   |-- pwm-a-e
 pinconfig   312  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   313  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-a
 pinconfig   314  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   315  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-b
 pinconfig   316  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   317  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-c-4
 pinconfig   318  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   319  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-c-6
 pinconfig   320  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   321  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-d-5
 pinconfig   322  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   323  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-d-10
 pinconfig   324  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   325  [   ]   pinconfig             |   |   |   |   |-- pwm-ao-d-e
 pinconfig   326  [   ]   pinconfig             |   |   |   |   |   `-- mux
 pinconfig   327  [   ]   pinconfig             |   |   |   |   |-- remote-input-ao
 pinconfig   328  [   ]   pinconfig             |   |   |   |   |   `-- mux
 gpio          1  [   ]   meson-axg-gpio        |   |   |   |   `-- meson-gpio
 syscon        2  [ + ]   syscon                |   |   |   `-- sys-ctrl@0
 clk           4  [   ]   meson_clk_g12a_ao     |   |   |       |-- clock-controller
 pinctrl       2  [   ]   meson-g12a-pinctrl    |   |   |       `-- pinctrl@14
 pinconfig   329  [   ]   pinconfig             |   |   |           |-- i2c_ao_sck_pins
 pinconfig   330  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   331  [   ]   pinconfig             |   |   |           |-- i2c_ao_sda
 pinconfig   332  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   333  [   ]   pinconfig             |   |   |           |-- i2c_ao_sck_e
 pinconfig   334  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   335  [   ]   pinconfig             |   |   |           |-- i2c_ao_sda_e
 pinconfig   336  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   337  [   ]   pinconfig             |   |   |           |-- mclk0-ao
 pinconfig   338  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   339  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-din0
 pinconfig   340  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   341  [   ]   pinconfig             |   |   |           |-- spdif-ao-out
 pinconfig   342  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   343  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-din1
 pinconfig   344  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   345  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-din2
 pinconfig   346  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   347  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-dout0
 pinconfig   348  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   349  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-dout1
 pinconfig   350  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   351  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-dout2
 pinconfig   352  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   353  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-fs
 pinconfig   354  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   355  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-sclk
 pinconfig   356  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   357  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-slv-fs
 pinconfig   358  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   359  [   ]   pinconfig             |   |   |           |-- tdm-ao-b-slv-sclk
 pinconfig   360  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   361  [   ]   pinconfig             |   |   |           |-- uart-a-ao
 pinconfig   362  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   363  [   ]   pinconfig             |   |   |           |-- uart-ao-a-cts-rts
 pinconfig   364  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   365  [   ]   pinconfig             |   |   |           |-- pwm-a-e
 pinconfig   366  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   367  [   ]   pinconfig             |   |   |           |-- pwm-ao-a
 pinconfig   368  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   369  [   ]   pinconfig             |   |   |           |-- pwm-ao-b
 pinconfig   370  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   371  [   ]   pinconfig             |   |   |           |-- pwm-ao-c-4
 pinconfig   372  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   373  [   ]   pinconfig             |   |   |           |-- pwm-ao-c-6
 pinconfig   374  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   375  [   ]   pinconfig             |   |   |           |-- pwm-ao-d-5
 pinconfig   376  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   377  [   ]   pinconfig             |   |   |           |-- pwm-ao-d-10
 pinconfig   378  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   379  [   ]   pinconfig             |   |   |           |-- pwm-ao-d-e
 pinconfig   380  [   ]   pinconfig             |   |   |           |   `-- mux
 pinconfig   381  [   ]   pinconfig             |   |   |           `-- remote-input-ao
 pinconfig   382  [   ]   pinconfig             |   |   |               `-- mux
 syscon        0  [   ]   syscon                |   |   |-- ao-secure@140
 pwm           0  [   ]   meson_pwm             |   |   |-- pwm@2000
 serial        0  [ + ]   serial_meson          |   |   |-- serial@3000
 pwm           1  [   ]   meson_pwm             |   |   `-- pwm@7000
 video         0  [ + ]   meson_vpu             |   |-- vpu@ff900000
 vidconsole    0  [ + ]   vidconsole0           |   |   `-- vpu@ff900000.vidconsole0
 simple_bus   10  [ + ]   simple_bus            |   |-- bus@ffd00000
 reset         0  [ + ]   meson_reset           |   |   |-- reset-controller@1004
 pwm           2  [   ]   meson_pwm             |   |   |-- pwm@19000
 serial        1  [   ]   serial_meson          |   |   `-- serial@24000
 mmc           0  [ + ]   meson_gx_mmc          |   |-- sd@ffe03000
 blk           0  [   ]   mmc_blk               |   |   |-- sd@ffe03000.blk
 bootdev       0  [   ]   mmc_bootdev           |   |   `-- sd@ffe03000.bootdev
 mmc           1  [ + ]   meson_gx_mmc          |   |-- sd@ffe05000
 blk           1  [   ]   mmc_blk               |   |   |-- sd@ffe05000.blk
 bootdev       1  [   ]   mmc_bootdev           |   |   `-- sd@ffe05000.bootdev
 mmc           2  [ + ]   meson_gx_mmc          |   |-- mmc@ffe07000
 blk           2  [   ]   mmc_blk               |   |   |-- mmc@ffe07000.blk
 bootdev       2  [   ]   mmc_bootdev           |   |   `-- mmc@ffe07000.bootdev
 simple_bus   11  [   ]   dwc3-meson-g12a       |   `-- usb@ffe09000
 usb           0  [   ]   xhci-dwc3             |       `-- usb@ff500000
 clk           2  [   ]   fixed_clock           |-- xtal-clk
 backlight     0  [   ]   pwm_backlight         |-- backlight
 panel         0  [   ]   simple_panel          |-- panel
 pwrseq        0  [ + ]   mmc_pwrseq_emmc       |-- emmc-pwrseq
 regulator     0  [   ]   regulator_fixed       |-- regulator-ao_5v
 regulator     1  [   ]   regulator_fixed       |-- regulator-vcc_1v8
 regulator     2  [   ]   regulator_fixed       |-- regulator-vcc_3v3
 regulator     3  [ + ]   regulator_fixed       |-- regulator-hdmi_pw
 regulator     4  [   ]   regulator_fixed       |-- regulator-vddao_1v8
 regulator     5  [   ]   regulator_fixed       |-- regulator-vddao_3v3
 bootstd       0  [   ]   bootstd_drv           `-- bootstd
 bootmeth      0  [   ]   bootmeth_distro           |-- distro
 bootmeth      1  [   ]   bootmeth_efi              |-- efi
 bootmeth      2  [   ]   bootmeth_pxe              |-- pxe
 bootdev       3  [   ]   system_bootdev            `-- system-bootdev

As you can see, the “panel” and “backlight” classes are created but the devices are not probed. It feels like there is a further step that must be necessary to initialize this device but I’m not sure what it is.

On further reflection, I think I might have an idea what’s going on. The U-Boot console is outputting on vidconsole0, which is using the meson_vpu video uclass. There is also an hdmi-tx node using the meson_dw_hdmi driver and the display uclass. The regulator-hdmi_pw regulator is also in use. Notably, the vidconsole, meson_vpu, meson_dw_hdmi, and hdmi_pw regulator devices are all probed at boot.

So I think that although I have defined simple_panel and pwm_backlight nodes, they are not being used to display anything, U-Boot doesn’t know what they’re for and has no reason to probe them.

My instinct is to try to modify the .dts to link the backlight node to the hdmi-tx node (display uclass). However, the Kconfig entry for the pwm_backlight driver suggests that the pwm_backlight driver can be used with the simple_panel driver, which in turn uses the panel uclass driver. The panel uclass driver has methods specifically for enabling and setting the backlight. Conversely, the display uclass driver does not have backlight methods.

All of this makes me suspect that it’s simply not possible to link a pwm-backlight device to an HDMI monitor on this hardware without modifying/rewriting the drivers. This could explain why the kernel does not turn the backlight on at boot, even though the backlight device exists. Just like U-Boot, the kernel can set up the backlight device but has no idea it’s for the HDMI display. The difference is that in Linux I can manually enable and configure the backlight in userspace (which is why a custom boot script is needed to enable it), whereas U-Boot does not have such userspace tools (as far as I have been able to figure out so far).

All this to say, I suspect what I’m going to have to do is forget about trying to use the pwm-backlight driver and instead just use the regular pwm uclass and see if I can control it using environment variables or custom board.c code.

I spent some time trying to get the regular “pwm” command to work, but even after removing the backlight and panel notes from the device tree, I found that I would still get “‘synchronous abort’ handler error” and accompanying hard reboot every time I would attempt to run the pwm command on the pwm device. On the other hand, the ‘gpio set’ command would successfully drive the pin high, which of course has the effect of turning on the backlight. However, I discovered that when u-boot hands off to the kernel, the gpio is reset anyway (presumably) because I have that pin configured in the kernel device tree as a backlight. So after all that, even though I was able to get u-boot to turn my backlight pin on, the only effect in practice was making it blink on and then off barely a second later.

Instead, I set up an init.d script to run early in the boot process to load the pwm_bl module and enable the backlight. It doesn’t turn as as early as u-boot, but I’m going to call it close enough for now.